AI Agent项目探索与实践记录
1. 概述
本文对近期工作中AI Agent部分进行总结和介绍,是一篇项目实践记录,也可以看作是技术报告。本项目主要以微软TaskWeaver项目作为参考,以其代码作为基础进行了改造,在之前的文章《TaskWeaver使用记录》中,对其进行了简单的介绍,并提到了一些改造的角度,本文将延续这一思路展开介绍。TaskWeaver项目的特点是其以python作为Agent各个步骤之间的粘合剂,利用LLM的代码能力,实现各类子任务动作直接的协作,最终达成一个相对复杂的动作。其思路与早些时候我的设想类似,并且在其他各种主流的Agent项目种,也看到有类似的思路,因而选择该项目作为基础进行改造开发。
尽管本系统涉及到数据库的查询与使用,但目标并非打造一个KBQA系统,而是致力于实现一个AI智能助手,嵌入我们团队所研发的综合平台进行使用,在具体的业务场景下,根据用户的指令去实现各类任务。本系统没有开源的计划,并且与我们的业务系统耦合性较强,贴出代码意义不大,故而本文主要对系统的结构以及实现思路进行介绍。
设计原则
在设计这套系统时,主要考虑以下几项原则:
-
效率:
在试用TaskWeaver项目时遇到的一个较为显著的问题是prompt过长,这导致模型的推理效率会受到一定的影响,尤其对于本地部署的模型,相对openai等成熟产品没有做到非常好的优化的话,随着推理的进行,推理的tpm会越来越低。因而在设计此项目时,对prompt尽可能的进行压缩,以及更加谨慎的调用LLM。 -
准确性
相比完全依赖于大模型的生成能力,本项目在设计时更侧重准确性,在部分相对固定的场景下将执行了流程改为半自动半固定的形式,以确保流程的顺利执行。 -
可扩展性:
为了使得系统能够适配于各类任务,要求其具有较高的可扩展性。本项目主要通过对各类经验库(prompt等)以及工具集的管理来实现各类功能的扩展。 -
“自学习”:
在使用的过程中,系统需要对成功进行的任务进行记录,形成经验以供下次调用参考。
2. 总体结构
本文的第二章节主要篇结构和概念的介绍,具体实现的方式将在第三章节进行展开。
如图所示,系统主要由以下几个模块构成:
2.1 记忆模块
记忆模块(Memory)表示在整个对话过程中起到了记录的作用,在某些Agent项目中也被称为环境,其思想是与Agent系统进行交互的不仅是用户当前的query,还包含了整个对话过程、对之前信息的总结、可以参考的经验等等信息。
在具体的执行中,会大致分为两种情况:
-
- 没有背景信息的通用对话场景。用户首次发起的query将创建Memory,并在memory中开辟一个新的conversation;
-
- 在一些特定场景中,例如针对某些数据对象类的对话场景,在唤起对话助手时,将数据对象类的schema和其他相关的prompt信息作为基础,创建memory,实际操作中也就是将这些信息作为system prompt拼接到user query之前。
如图中结构所示,user将query输入给memory,作为一个整体传递给Agent系统,系统根据用户的要求完成相应的任务,然后将执行结果信息以及其他附件信息反馈给用户,与此同时,memory将执行成功的经验保存下来,以便在后续的使用中,当用户提出相似的query时,可以为LLM的生成过程作为指导。
2.2 模型服务模块
2.2.1 LLM服务
LLM服务主要为Agent系统中的Planner,generator以及responser提供服务,与此同时在前文总结、新轮次判断等辅助任务中,也可以发挥作用。这些任务与组件将在下文Agent系统部分进行介绍。
2.2.2 retrieval服务
Retrieval服务,或者embedding服务,主要用于各类信息的召回,包括模型经验的召回、组件工具的召回等,也可用于某些特定的下游任务中。
2.2.3 rerank服务
为了进一步提高召回的质量,如果召回的候选数量较多,则需要用到rerank服务进行重排取topk,以控制总prompt的长度。
2.3 Agent系统
Agent系统是整个项目的主体部分,包含了计划制定器(Planner),代码生成器(Code/SQL Generator),代码执行器(Code Executor),以及应答器(Responser)四个主要子模块,以及历史轮次总结器(Round Compressor),新轮次判别器(New Turn Discriminator),查询重写器(Query Rewriter),以及生成代码类型判断器(Generate Type Discriminator)等辅助任务模块。
2.3.1 Planner
Planner的作用是创建一个计划以供执行,计划的生成需要用到LLM服务。制定的计划将依次生成相应的代码并执行,或直接传递给responser,然后输出反馈给用户的应答。
2.3.2 Code/SQL Generator
在TaskWeaver的设计中,这部分称作Code Interpreter,Code Interpreter由负责生成代码的Code Generator和负责执行的Code Executor。在本项目中,我去掉了Code Interpreter这一层的概念,将其拆成了Generator和Executor两部分。
对于Generator而言,考虑到在实际应用中,我们希望此Agent智能助手能够发挥一些数据智能应用相关的作用,于是要求其具有一定的SQL生成能力。即便SQL语句可以直接在python代码中生成出来,但是在实际尝试中,这种全自动的生成包含SQL语句的python代码的方法,失败率还是比较高的,于是将SQL Generator单独作为一个模块进行设计。
这样将两者分离的好处还在于prompt的设置上,可以用更加专一地去指导一项专门的任务,防止生成python code和SQL语句相关prompt的相互干扰,同时也减少一次推理任务中的prompt长度。
2.3.3 Code Executor
代码执行器部分基本上是照搬了TaskWeaver的设计,它提供了一个python运行环境(通过Jupyter kernel实现)。这个环境中包含了用户query相关的组件,工具组件将会在下文中详细介绍。
如果代码的类型是python,即代码的来源是Code Generator,代码将直接被执行,而如果类型是SQL,即代码来自于SQL Generator,其将被之前已经创建在python环境中的Client执行。
2.3.4 Responser
在TaskWeaver项目的设计中,是不包含这一模块的,由Planner充当与用户交互的模块,Planner将根据当前的memory,生成一个Post,这个Post的内容是直接由LLM生成的Json结构,其中的“send_to”字段决定了此Post是发送给Code Generator还是发送给用户。在应答用户时,其采用的是与制定计划时完全相同的一套prompt,也就是并没有提供给LLM有效的指引,因此在设计此项目时,我加入了一个Responser模块,专门用来与用户进行交互。
左图是TaskWeaver中的执行逻辑,右图是在本项目中我所设计的执行逻辑。
2.3.5 Round Compressor
历史轮次总结器是从TaskWeaver中借鉴来的,也是目前大多数项目中都在用的一个策略,主要作用就是压缩prompt长度,以及过滤掉一些不重要的信息。
2.3.6 New Turn Discriminator
每次进行接收到用户的query时,需要进行判断,当前query是否与之前的仍然处于同一个主题,如果不是的话,将在memory中开辟一个新的conversation。在判断时需要将Round Compressor总结的结果作为prompt传入。
判断是否是新轮次的作用,不仅在于过滤掉之前不相关的conversation,防止无关prompt造成干扰,提高推理效率,还会重新初始化一个pyt