跳到内容

代理架构

许多 LLM 应用程序在 LLM 调用之前和/或之后实现特定的步骤控制流。 例如,RAG 执行检索与用户问题相关的文档,并将这些文档传递给 LLM,以便将模型的响应基于提供的文档上下文。

有时我们希望 LLM 系统能够选择自己的控制流程来解决更复杂的问题,而不是硬编码固定的控制流程! 这是代理的一种定义:代理是使用 LLM 来决定应用程序控制流程的系统。 LLM 可以通过多种方式控制应用程序

  • LLM 可以在两条潜在路径之间进行路由
  • LLM 可以决定调用许多工具中的哪一个
  • LLM 可以决定生成的答案是否足够,或者是否需要更多工作

因此,有许多不同类型的代理架构,它们为 LLM 提供了不同级别的控制。

Agent Types

路由器

路由器允许 LLM 从指定的一组选项中选择单个步骤。 这是一种代理架构,它表现出相对有限的控制级别,因为 LLM 通常专注于做出单个决策,并从有限的一组预定义选项中生成特定输出。 路由器通常采用一些不同的概念来实现这一点。

结构化输出

LLM 的结构化输出通过提供 LLM 应在其响应中遵循的特定格式或模式来工作。 这类似于工具调用,但更通用。 虽然工具调用通常涉及选择和使用预定义函数,但结构化输出可用于任何类型的格式化响应。 实现结构化输出的常用方法包括

  1. 提示工程:指示 LLM 通过系统提示以特定格式响应。
  2. 输出解析器:使用后处理从 LLM 响应中提取结构化数据。
  3. 工具调用:利用某些 LLM 的内置工具调用功能来生成结构化输出。

结构化输出对于路由至关重要,因为它们确保系统的 LLM 决策可以被可靠地解释和执行。 在此操作指南中了解有关结构化输出的更多信息。

工具调用代理

虽然路由器允许 LLM 做出单个决策,但更复杂的代理架构以两种关键方式扩展了 LLM 的控制

  1. 多步骤决策:LLM 可以连续做出多个决策,而不是仅仅一个。
  2. 工具访问:LLM 可以从各种工具中选择和使用工具来完成任务。

ReAct 是一种流行的通用代理架构,它结合了这些扩展,集成了三个核心概念。

  1. 工具调用:允许 LLM 根据需要选择和使用各种工具。
  2. 内存:使代理能够保留和使用来自先前步骤的信息。
  3. 规划:使 LLM 能够创建和遵循多步骤计划以实现目标。

这种架构允许更复杂和灵活的代理行为,超越简单的路由,实现多步骤的动态问题解决。 您可以使用 create_react_agent

工具调用

当您希望代理与外部系统交互时,工具非常有用。 外部系统(例如,API)通常需要特定的输入模式或有效负载,而不是自然语言。 例如,当我们将 API 绑定为工具时,我们会让模型意识到所需的输入模式。 模型将根据用户的自然语言输入选择调用工具,并且它将返回符合工具所需模式的输出。

许多 LLM 提供商支持工具调用,LangChain 中的工具调用界面很简单:您只需将任何 Python function 传递到 ChatModel.bind_tools(function) 中即可。

Tools

内存

内存对于代理至关重要,使其能够在多步骤问题解决过程中保留和利用信息。 它在不同的尺度上运行

  1. 短期记忆:允许代理访问在序列中较早步骤中获取的信息。
  2. 长期记忆:使代理能够回忆起先前交互的信息,例如对话中的过去消息。

LangGraph 提供对内存实现的完全控制

  • State:用户定义的模式,用于指定要保留的内存的确切结构。
  • Checkpointers:在不同交互的每个步骤中存储状态的机制。

这种灵活的方法允许您根据特定的代理架构需求定制内存系统。 有关向图中添加内存的实用指南,请参阅本教程

有效的内存管理增强了代理维护上下文、从过去的经验中学习以及随着时间的推移做出更明智的决策的能力。

规划

在 ReAct 架构中,LLM 在 while 循环中被重复调用。 在每个步骤中,代理决定调用哪些工具,以及这些工具的输入应该是什么。 然后执行这些工具,并将输出作为观察结果反馈到 LLM 中。 当代理确定它有足够的信息来解决用户请求并且不值得调用更多工具时,while 循环终止。

ReAct 实现

本文与预构建的 create_react_agent 实现之间存在一些差异

  • 首先,我们使用工具调用来让 LLM 调用工具,而论文使用提示 + 解析原始输出。 这是因为在撰写本文时工具调用尚不存在,但通常更好且更可靠。
  • 其次,我们使用消息来提示 LLM,而论文使用字符串格式化。 这是因为在撰写本文时,LLM 甚至没有公开基于消息的界面,而现在这是他们公开的唯一界面。
  • 第三,论文要求所有工具的输入都是单个字符串。 这主要是因为当时的 LLM 能力不是很强,并且只能真正生成单个输入。 我们的实现允许使用需要多个输入的工具。
  • 第四,论文一次只考虑调用单个工具,这主要是由于当时 LLM 性能的限制。 我们的实现允许一次调用多个工具。
  • 最后,论文要求 LLM 在决定调用哪些工具之前显式生成“思考”步骤。 这是“ReAct”的“推理”部分。 我们的实现默认情况下不执行此操作,这主要是因为 LLM 已经变得更好,并且这不是必需的。 当然,如果您希望提示它这样做,您当然可以。

自定义代理架构

虽然路由器和工具调用代理(如 ReAct)很常见,但自定义代理架构通常可以为特定任务带来更好的性能。 LangGraph 提供了几个强大的功能,用于构建定制的代理系统

人在环路

人工参与可以显着提高代理的可靠性,尤其是在敏感任务中。 这可能涉及

  • 批准特定操作
  • 提供反馈以更新代理的状态
  • 在复杂的决策过程中提供指导

当完全自动化不可行或不可取时,人在环路模式至关重要。 在我们的人在环路指南中了解更多信息。

并行化

并行处理对于高效的多代理系统和复杂任务至关重要。 LangGraph 通过其 Send API 支持并行化,从而实现

  • 多个状态的并发处理
  • map-reduce 类操作的实现
  • 高效处理独立的子任务

有关实际实现,请参阅我们的 map-reduce 教程

子图

子图对于管理复杂的代理架构至关重要,尤其是在多代理系统中。 它们允许

  • 单个代理的隔离状态管理
  • 代理团队的层级组织
  • 代理和主系统之间的受控通信

子图通过状态模式中的重叠键与父图通信。 这实现了灵活的模块化代理设计。 有关实现详细信息,请参阅我们的子图操作指南

反思

反思机制可以通过以下方式显着提高代理的可靠性

  1. 评估任务完成情况和正确性
  2. 提供反馈以进行迭代改进
  3. 启用自我纠正和学习

虽然反思通常基于 LLM,但也可以使用确定性方法。 例如,在编码任务中,编译错误可以用作反馈。 在此视频中使用 LangGraph 进行自我纠正代码生成演示了这种方法。

通过利用这些功能,LangGraph 能够创建复杂的、特定于任务的代理架构,这些架构可以处理复杂的工作流程、有效地协作并不断提高其性能。

评论