Agent 通常指的是一类具备高度自主性的智能实体,它们能够巧妙地在特定环境里自主感知信息,运用内置的智能算法做出理性决策,进而精准无误地执行相应动作。相较于 RAG(检索增强生成)技术,Agent 可解决诸多难题。RAG 虽擅于知识检索整合,但面对动态变化场景稍显乏力。Agent 则不同,它能实时感知环境动态,主动调整策略。比如在供应链管理中,遇到物流堵塞,Agent 可迅速重新规划配送路径,优化资源分配,保障供应链畅通无阻,而 RAG 很难做到如此灵活应变,全方位提升效率与适应性。
Profile
Prompt engineering,例如 CoT(Chain of Thought,思维链)、ICL(In-Context Learning,上下文学习)等,已被证实是一种在不微调的情况下增强大语言模型(LLM)能力的有效方法。由于 LLM 的本质是基于 token 预测,所以 Prompt engineering 相当于给 LLM 提供更多的前置 token,如此一来便更有可能预测出 “正确” 的 token(这纯属个人理解,在预训练阶段模型会接触大量文本,下一个 token 的候选者可能成千上万。此时若给予更多前置 token,依据条件概率,就更有机会生成所谓的正确 token)。
简介模块在此处的作用是定义代理的角色。代理(Agent)通常通过承担特定角色来执行任务,以查询天气的任务为例,一般我们会将 Agent 设定为天气预报员或检索人员,赋予其一定的天气相关背景知识以及检索天气的工具等。这部分信息通常涵盖基本信息(如年龄、性别和职业)、心理信息(反映代理的个性)以及社交信息(在多代理场景下会涉及代理之间的关系)等。
简单来讲,简介模块可理解为添加前置 token,助力 LLM 锁定后续 token 的生成范围,能有效激发 LLM 的 “潜意识”。需要注意的是,这并非仅 Agent 才有的模块,只要与 LLM 交互,就可能涉及这部分内容。
常用的简介生成策略如下:
- 手工制作方法:Agent 的简介由人工指定,这也是最为常用的方式,即人工撰写系统提示。通过设定 Agent 角色,让 LLM 能够更好地生成期望结果,甚至可以设定 LLM 生成内容的格式等。这种方法能够精准把控一些具体细节,但不可避免地需要投入大量人工精力。
- LLM 生成方法:Agent 的简介由 LLM 自动生成。有一种做法是指明档案生成规则,阐明目标群体中 Agent 简介的组成与属性,然后选择性地提供一些样本示例(few-shot),最后利用 LLM 生成所有简介。不过,该方法可能难以对生成的档案进行精确控制,从而导致与预期特征不一致或出现偏差。
- 数据集对齐方法:Agent 的简介源自真实世界的数据集。简单来说,就是依据真实人类的信息,归纳总结成一段描述性文字提供给 LLM。例如,直接将现实生活中某类人的背景信息总结后赋予 LLM,这里要注意,由于单个人的信息可能过于独特,不一定能发挥预期作用,同时输入 token 限制也是一个问题。
PS. 在实际使用场景中,不必过于纠结使用哪种策略,甚至可以采用多种方法相结合的方式来生成对应的信息。
记忆(Memory)
结构
记忆模块用于存储从环境中感知到的信息,并借助记录的记忆推动未来行动。它帮助代理积累经验、实现自我进化,使其行为更加一致、合理、高效。简单而言,记忆可以是当前的聊天记录,比如新开一个对话窗口,该窗口的信息就会被当作记忆。往复杂了说,记忆可以包含过去所有的对话,此时以某个账户作为主体,这个主体与 LLM 发生的所有对话都能当作记忆。
由此明显可见,这里将记忆划分为短期记忆(即图片中的 unified memory)和长期记忆:
- 短期记忆:最简单的做法是将当前窗口中的对话再次输入 LLM,如此一来 LLM 便拥有了短期记忆。其能记忆的长度取决于 LLM 输入 token 的上限,当然也可以采用压缩或提炼的方法来总结过往内容。
- 长期记忆:可以简单理解为外挂数据库,也就是 RAG(Retrieval-Augmented Generation,检索增强生成),在此不多赘述,可参考相关内容。
这个模块实际上也是 RAG 与 Agent 逐渐融合之处,之前提及 modular RAG 时就已有记忆、循环等模块。有趣的是,很多人将 RAG 和 Agent 视为两种独立技术,认为某个方案的解决要么只用 RAG,要么只用 Agent。依我之见,这部分人对 RAG 的理解仅停留在 Naive RAG,即单轮次的简单检索增强,对 Agent 的理解也只是 LLM + 短期记忆 + 函数调用。从这个角度看,两者确实各有优劣,但在实际应用中,我们需要结合各种方法的优势才能达成理想效果。
对于记忆的处理,一种较为合理的方法是将长期和短期记忆相结合(即图片中的 Hybrid memory)。这与人的思维逻辑颇为相似,我们既需要当前正在进行对话的记忆,也需要过去积累学习所得的记忆。例如面试时,首先我们会基于已有的长期记忆进行自我介绍,随着面试深入,面试官可能使用一些代词指代对话中发生的事情,这时就需要利用短期记忆明确其提问内容。
格式
刚刚提到短期记忆指当前对话的内容,显然这部分记忆以自然语言形式存储,无需额外处理便可直接提供给 LLM。除此之外,还有许多其他格式的数据可用于存储过去的知识 / 记忆,这里简单列举两个:
- embedding:将信息转化为 embedding 向量,再通过检索获取对应相关信息(没错,就是 RAG)。
- 数据库:这一点很明显,在工业界许多结构化数据都存储在数据库中,通过 SQL 等语句能够轻松检索到对应信息。在确保 SQL 正确的情况下,甚至可以省去重排序(re-rank)的步骤。
当然还有其他格式,如图结构、树结构等,它们各有优缺点,这里就不一一列举了,简言之,需要依据实际场景选择恰当的存储介质和格式。
操作
关于记忆的操作,主要有三个:读取、写入和反思。
- 读取:这个操作与 RAG 的流程类似,即选择最相似 / 正确的信息读取出来即可。
- 写入:取决于存储介质的不同,相关操作也有所差异。需要注意两点:1) 记忆重复,对于同一信息要尽量避免重复存储。说起来容易做起来难,对于 MySQL 这类数据库,如果确定了唯一键(UK),相对来说较易实现。但对于向量数据库(vectorDB)这种存储形式,很难保证没有重复数据,可以在元数据(metadata)字段添加一些标签,最后通过比对标签来避免或减少重复信息的存储。对于短期记忆而言,最简单的方法是直接利用 LLM 进行总结,减少重复信息。2) 记忆溢出,这里主要针对短期记忆,即 LLM 输入窗口的 token 限制,因为从逻辑上讲,数据库很少会出现超出存储上限的情况。对于短期记忆的长度问题,可以通过压缩总结的方法解决,同时也可以考虑使用支持长上下文(long-context)的 LLM 来提升短期记忆的存储上限。PS. 实际上在正常使用情况下,只需关注短期记忆的写入是否存在问题,因为业界很少有 Agent 会依据客户指令直接对数据库中的数据进行修改。
- 反思:其实反思不算一个独立操作,当记忆被检索 / 读取出来后,与其他信息一同提供给 LLM,自然而然就可以通过提示让大模型依据已有信息进行一定思考,根据过去的行为 / 记忆 / 结果来规划下一步行动。
规划(Planning)
无反馈规划
规划能力并非 Agent 所独有,任何一个大语言模型都具备一定的规划能力。当与 Agent 的其他模块相结合时,便能进一步提升整个任务的完成度。简单来说,当 LLM 没有其他模块辅助时,只能进行一些简单的规划推理,例如 Cot、Tot、Got、ReWOO 等,这种情况被称为无反馈规划,即 LLM 制定规划后,不会得到外部反馈就直接生成结果。当然,这里面也包含单路径推理、多路径推理等方法,如下图所示:
这种方法效果的优劣,直接取决于 LLM 本身的能力,换言之,一旦规划出现问题,那么得出的结论必然也是错误的。因此,一种可行的方法是,直接使用外部规划器(如 PDDL,Planning Domain Definition Language,规划领域定义语言)来制定计划,之后将这部分信息提供给 LLM 生成结果。通过外部规划器进行规划能够避免 LLM 因自身能力不足导致的规划失误。若想让这种方法自动化运行,就离不开函数调用(function calling)的能力,遇到问题时,调用外部规划器,获取所需计划后再交由 LLM 生成最终结果。
有反馈规划
什么是有反馈规划呢?简单来讲,就是 LLM 得到一定反馈后再进行下一步计划或修改之前的计划,其中最著名的就是 ReAct。这类方法的优势在于,当计划中的某个环节结果与最初计划相差甚远时,能够及时修正计划,避免整个任务失败。这里的反馈又可分为以下三类:
- 环境反馈:最具代表性的就是 ReAct 框架,通过思考 - 执行 - 观察三个步骤不断调用工具与环境交互,最终得到结果。还有一个较为知名的框架是 LLM Compiler,它结合了 ReWOO 和 ReAct 的优势,通过设定规划器(planner)和求解器(solver),并与环境进行交互。与 ReAct 不同的是,规划器会给出完整的执行步骤,求解器执行全部内容后,规划器会查看每一步执行结果并思考是否需要重新规划。
- 人类反馈:这一点比较明确,即在程序运行的关键节点引入人工介入,例如人工审查或输入,使整个流程更具可控性。
- 模型反馈:简单来说,就是用模型替代人类进行反馈,可以引入第二个 LLM 或专门训练的模型来提供反馈。与环境反馈只能给出执行结果不同,模型反馈能够依据当前状态给出详细的修改反馈。
行动(Action)
说到 Agent,让人印象最为深刻的便是函数调用,也就是这里提及的行动(Action)。通过调用外部工具能够获取精准答案,弥补 LLM 在某些方面的不足,例如数学计算。
目标
对于一些具有明确任务的问题,如四则运算,只需调用不同的工具来计算每一步结果即可(环境反馈)。而对于一些较为模糊的任务,如论文撰写修改,可能需要调用两个 LLM 返回修改内容,以求得完美答案(模型反馈)。
因此,这里行动的目标不仅仅是完成任务,还可能涉及在完成任务过程中必要的交流与探索。
产生方式
- 从记忆中调用动作:根据当前任务,依据从 Agent 记忆中检索到的信息生成动作。仍以 ReAct 为例,根据当前任务的反馈以及之前的思考,确定下一步的行动。
- 遵循计划中的行动:Agent 依据预先制定的计划采取行动。例如 ReWOO 或外部规划器,Agent 会严格按照计划执行相关步骤。
行动空间
行动空间指的是 Agent 可以执行的所有可能行动的集合,大致可分为两类:(1)外部工具和(2)LLMs 的内部知识。
外部工具
-
API / 工具函数:利用外部 API / 函数来扩充行动空间。以四则运算为例,可以定义加减乘除四个工具函数,让 LLM 根据输入判断应按何种步骤进行运算,每一步都调用对应的函数以获取准确结果。当然,对于更为复杂的任务,可以直接调用其他模型来辅助生成结果,完成任务。
-
数据库 / 知识库:集成外部数据库或知识库能使 Agent 获取特定领域的信息,从而生成更贴合实际的行动。可以将 RAG 封装成一个外部函数,通过调用这个函数工具,提取对应数据库中的信息。
PS. 在定义 API / 工具函数时,需要给出相应的功能描述、参数需求等。而且当 API / 工具函数过多时,需要考虑输入 token 上限。个人经验表明,当一个 Agent 拥有过多工具,或者存在两个描述相近的工具时,可能导致 LLM 无法正确调用。
内部知识
除了使用外部工具,许多智能体还依靠 LLM 的内部知识来指导其行动。这种说法有点类似于早期的评论模型,通过给模型赋予不同角色来完成任务,这其实也是多 Agent 的雏形,不同的 Agent 实际上都是同一个模型,只是给予了不同的系统提示,最终相互协作完成任务。
PS. 个人观点认为,对于特定任务,如果条件允许,最好优先使用外部工具获取关键信息。只有在无法通过外部工具得到确切答案时,才考虑使用内部知识。经验教训显示,当一个 LLM 在初次推理时无法得出想要的答案,那么第二次推理大概率也难以得到正确信息。
行动影响
行动影响指的是 Agent 行动带来的后果。其实这没什么太多可解释的,通过外部工具调用得到结果后,可能会导致计划改变等情况出现,进而可能需要制定新的计划等。