代理架构¶
许多大型语言模型(LLM)应用实现了一种特定的控制流程步骤,在调用LLM之前和/或之后执行。例如,RAG会检索与用户问题相关的文档,并将这些文档传递给LLM,以便使模型的响应基于提供的文档上下文。
与其硬编码一个固定的控制流程,我们有时希望LLM系统能够自行选择控制流程来解决更复杂的问题!这是代理的一个定义:代理是一种使用LLM来决定应用程序控制流的系统。LLM可以以多种方式控制应用程序:
- LLM可以在两个潜在路径之间进行路由。
- LLM可以决定调用多个工具中的哪一个。
- LLM可以决定生成的答案是否足够,或者是否需要更多的工作。
因此,存在许多不同类型的代理架构,这为LLM提供了不同程度的控制。
路由器¶
路由器允许大型语言模型(LLM)从一组指定选项中选择一个单一步骤。这是一种代理架构,表现出相对有限的控制水平,因为LLM通常专注于做出单个决策,并从一组预定义的选项中产生特定的输出。路由器通常采用几种不同的概念来实现这一目标。
结构化输出¶
结构化输出与LLM的工作原理是通过提供一种特定的格式或模式,让LLM在响应时遵循该格式或模式。这类似于工具调用,但更为通用。虽然工具调用通常涉及选择和使用预定义的功能,但结构化输出可以用于任何形式的格式化响应。实现结构化输出的常见方法包括:
- 提示工程:通过系统提示指示LLM以特定格式响应。
- 输出解析器:使用后处理从LLM响应中提取结构化数据。
- 工具调用:利用某些LLM内置的工具调用功能生成结构化输出。
结构化输出对于路由至关重要,因为它确保了LLM的决策能够被系统可靠地解释并采取行动。了解更多关于结构化输出的如何操作指南。
工具调用代理¶
虽然路由器允许LLM做出单一决策,但更复杂的代理架构在两个关键方面扩展了LLM的控制能力:
- 多步决策:LLM可以依次做出一系列决策,而不仅仅是单个决策。
- 工具访问:LLM可以选择并使用各种工具来完成任务。
ReAct 是一种流行的通用代理架构,它结合了这些扩展,并集成了三个核心概念。
工具调用
:允许LLM根据需要选择和使用各种工具。记忆
:使代理能够保留并利用之前步骤中的信息。规划
:赋予LLM创建和遵循多步计划以实现目标的能力。
这种架构允许更复杂和灵活的代理行为,超越简单的路由,使动态多步问题解决成为可能。你可以使用它与create_react_agent
。
工具调用¶
工具在希望代理与外部系统交互时非常有用。外部系统(如API)通常需要特定的输入模式或负载,而不是自然语言。当我们绑定一个API作为工具时,我们给模型提供了所需的输入模式意识。模型会根据用户的自然语言输入选择调用工具,并返回符合该工具所需模式的输出。
许多LLM提供商支持工具调用 和 工具调用接口 在LangChain中很简单:你可以简单地将任何Python 函数
传递给 ChatModel.bind_tools(function)
。
记忆¶
记忆对代理至关重要,使其能够在解决问题的多个步骤中保留和利用信息。它在不同的尺度上运行:
- 短期记忆:允许代理访问序列中早期步骤获得的信息。
- 长期记忆:使代理能够回忆以前交互中的信息,例如对话中的过去消息。
LangGraph 提供了对内存实现的完全控制:
这种灵活的方法允许你根据特定代理架构的需求定制内存系统。有关如何向图添加记忆的实际指南,请参阅 此教程。
有效的内存管理增强了代理维持上下文、从过去经验中学习以及随着时间推移做出更明智决策的能力。
规划¶
在ReAct架构中,LLM在一个while循环中被反复调用。在每一步中,代理决定调用哪些工具以及这些工具的输入应该是什么。然后执行这些工具,并将它们的输出反馈给LLM作为观察结果。当代理认为已经获得了足够的信息来解决用户请求并且不再值得调用更多工具时,while循环终止。
ReAct 实现¶
本文档与预构建的create_react_agent
实现之间有几个不同之处:
- 首先,我们使用工具调用让LLM调用工具,而论文则使用提示+解析原始输出的方式。这是因为当时还没有工具调用这一功能,但工具调用现在更好且更可靠。
- 其次,我们使用消息来提示LLM,而论文使用字符串格式化。这是因为当时LLM甚至没有暴露基于消息的接口,而现在这是它们唯一暴露的接口。
- 第三,论文要求所有工具的输入都必须是单个字符串。这主要是因为当时的LLM能力有限,只能生成单个输入。我们的实现允许使用需要多个输入的工具。
- 第四,论文只考虑在同一时间调用单个工具,这主要是由于当时LLM性能的限制。我们的实现允许在同一时间调用多个工具。
- 最后,论文要求LLM显式生成一个“思考”步骤,然后再决定调用哪些工具。这是“ReAct”的“推理”部分。我们的实现默认不这样做,主要是因为LLM已经变得更好,这不再是必要的。当然,如果你希望提示它这样做,完全可以做到这一点。
自定义代理架构¶
虽然路由器和工具调用代理(如ReAct)很常见,但自定义代理架构通常能为特定任务带来更好的性能。LangGraph提供了几个强大的功能来构建定制化的代理系统:
人机交互模式¶
人类参与可以显著增强代理的可靠性,特别是在处理敏感任务时。这可能包括:
- 批准特定操作
- 提供反馈以更新代理的状态
- 在复杂的决策过程中提供指导
当完全自动化不可行或不理想时,人机交互模式尤为重要。更多详情,请参阅我们的人机交互指南。
并行化¶
并行处理对高效的多代理系统和复杂任务至关重要。LangGraph通过其Send API支持并行化,从而实现:
- 多个状态的同时处理
- 实现类似MapReduce的操作
- 独立子任务的有效处理
有关实际实现,请参阅我们的MapReduce教程。
子图¶
子图在管理复杂的代理架构中起着关键作用,尤其是在多代理系统中。它们允许:
- 单独代理的状态隔离管理
- 代理团队的层次化组织
- 代理与主系统之间的受控通信
子图通过状态模式中的重叠键与父图通信。这使得灵活、模块化的代理设计成为可能。有关实现细节,请参阅我们的子图如何使用指南。
反思机制¶
反思机制可以通过以下方式显著提高代理的可靠性:
- 评估任务完成情况和正确性
- 提供反馈以进行迭代改进
- 允许自我纠正和学习
尽管通常基于LLM,但反射也可以使用确定性方法。例如,在编码任务中,编译错误可以作为反馈。这种方法在这段视频中演示了使用LangGraph进行自我纠正的代码生成。
通过利用这些功能,LangGraph能够创建复杂的、任务特定的代理架构,能够处理复杂的流程,有效地协作,并不断改进其性能。