简单链执行预设序列,而代理则引入动态行为。代理使用大型语言模型,不仅处理信息,更是决定下一步行动。它对问题进行推断,选择工具,观察结果,并反复尝试直到达成目标。然而,这个推断过程并非随意;它遵循特定的模式或结构,称之为代理架构。这些架构组织代理的“思考过程”,影响其如何分解问题、与工具互动以及整合信息。理解这些架构对于选择或设计适合您特定任务的代理非常重要。在LangChain背景下普遍实施和讨论的三种有影响力的代理架构包括:ReAct、Self-Ask和Plan-and-Execute。ReAct:推断与行动交织ReAct架构,是“推断+行动”的缩写,促进推断与行动之间的协作关系。ReAct代理不是预先生成一个完整计划,而是将内部推断(Thought)步骤与涉及外部交互(Action和Observation)的步骤交织进行。思考: 代理首先分析当前情况和总体目标。它将推断步骤具体化,决定下一步需要采取什么行动才能取得进展。这个思考过程通常由大型语言模型明确生成。行动: 根据思考,代理选择一个工具并为其指定输入。观察: 代理执行行动(调用工具)并接收观察结果(工具的输出或成果)。重复: 代理将观察结果融入其理解,并以新的思考再次开始循环,根据之前的步骤和最终目标决定后续行动。这个循环持续进行,直到代理判断它有足够的信息提供最终答案。digraph ReAct_Flow { rankdir=LR; node [shape=box, style=rounded, fontname="Arial", fontsize=10, margin=0.2]; edge [fontname="Arial", fontsize=9]; Start [label="用户查询", shape=ellipse, style=filled, fillcolor="#a5d8ff"]; Thought1 [label="思考: 分析查询, 决定下一步", fillcolor="#fffec9"]; Action1 [label="行动: 选择工具, 指定输入", fillcolor="#ffd8a8"]; Observation1 [label="观察: 接收工具输出", fillcolor="#e9ecef"]; Thought2 [label="思考: 分析观察结果, 决定下一步", fillcolor="#fffec9"; style=dashed]; Action2 [label="行动: ...", fillcolor="#ffd8a8"; style=dashed]; Observation2 [label="观察: ...", fillcolor="#e9ecef"; style=dashed]; FinalAnswer [label="最终答案", shape=ellipse, style=filled, fillcolor="#b2f2bb"]; Start -> Thought1; Thought1 -> Action1; Action1 -> Observation1 [label="执行工具"]; Observation1 -> Thought2 [label="融入结果"]; Thought2 -> Action2; Action2 -> Observation2 [label="执行工具"]; Observation2 -> FinalAnswer [label="整合结果"]; // 简化结尾 }ReAct循环将内部推断(思考)与外部交互(行动/观察)交织,直到最终答案形成。优点:适应性: ReAct代理能够根据从工具接收到的观察结果动态调整策略。如果工具失败或提供意外信息,代理可以推断失败原因并尝试不同方法。透明度: 明确的思考步骤使代理的推断过程更具可解释性,有助于调试和理解其行为。工具使用: 它天然适合于需要与多个工具交互或从外部源收集复杂信息的任务。局限性:冗余与延迟: 生成明确的思考增加了大型语言模型调用的次数,可能导致更高的延迟和成本。潜在循环: 设计不佳的提示或意外的工具输出有时可能使代理陷入重复的推断循环。ReAct是一种强大的通用架构,当解决方案路径最初不明确时尤其有效,并且需要根据中间结果进行试探和调整。LangChain提供了方便的方式来实例化ReAct代理,通常只需要一个大型语言模型、一套工具和一个基本提示模板。Self-Ask与搜索:问题分解Self-Ask架构侧重于将复杂问题分解为更简单的子问题,这些子问题通常可以使用专用工具回答,最常见的是搜索引擎。核心思路是迭代分解和信息收集。初始问题: 代理从主要用户查询开始。识别子问题: 大型语言模型判断是否需要更具体的信息来回答主要查询。如果需要,它会提出一个更简单、后续的问题。使用工具(搜索): 代理使用指定工具(如搜索API封装器)来找到子问题的答案。整合答案: 大型语言模型将子问题的答案融入其知识库。重复或回答: 如有必要,代理会提出另一个后续问题(步骤2)。如果它有足够的信息,它会将收集到的事实整合为对原始查询的最终答案。digraph SelfAsk_Flow { rankdir=TD; node [shape=box, style=rounded, fontname="Arial", fontsize=10, margin=0.2]; edge [fontname="Arial", fontsize=9]; Start [label="用户查询", shape=ellipse, style=filled, fillcolor="#a5d8ff"]; IdentifySQ1 [label="识别子问题1", fillcolor="#fffec9"]; UseTool1 [label="使用工具(例如,搜索)\n输入:子问题1", fillcolor="#ffd8a8"]; Result1 [label="结果1", fillcolor="#e9ecef"]; IdentifySQ2 [label="识别子问题2\n(基于查询 + 结果1)", fillcolor="#fffec9"; style=dashed]; UseTool2 [label="使用工具(例如,搜索)\n输入:子问题2", fillcolor="#ffd8a8"; style=dashed]; Result2 [label="结果2", fillcolor="#e9ecef"; style=dashed]; Synthesize [label="整合最终答案\n(使用结果1、2等)", fillcolor="#d0bfff"]; FinalAnswer [label="最终答案", shape=ellipse, style=filled, fillcolor="#b2f2bb"]; Start -> IdentifySQ1; IdentifySQ1 -> UseTool1; UseTool1 -> Result1 [label="获取答案"]; Result1 -> IdentifySQ2 [label="整合"]; IdentifySQ2 -> UseTool2; UseTool2 -> Result2 [label="获取答案"]; Result2 -> Synthesize [label="整合"]; // 简化流程展示多重整合 // 如果不再需要子问题的备选路径 Result1 -> Synthesize [label="整合并决定完成", style=dotted]; Synthesize -> FinalAnswer; }Self-Ask过程将主问题分解为子问题,使用工具(通常是搜索)来回答它们,并整合结果。优点:事实查找: 擅长回答需要从外部知识源获取并组合多条事实信息的复杂问题。结构化分解: 强制将问题清晰分解为可处理的部分。减少幻觉: 通过依赖外部工具获取子问题的真实答案,它可以降低大型语言模型生成不正确信息的可能性。局限性:工具依赖性: 严重依赖指定工具(通常是搜索)的有效性。推断范围有限: 主要侧重于信息检索;可能不太适合需要复杂计算、创意生成或规划的任务。Self-Ask对于构建基于外部数据的问答系统特别有用。LangChain提供了特定的代理构造器(如create_self_ask_with_search_agent),针对此模式进行了优化。规划与执行:规划与执行分离规划与执行架构引入了规划阶段和执行阶段之间的清晰分离。这种方法通常对复杂任务有利,在这些任务中,需要预先确定一系列行动。规划: 鉴于用户目标,一个专门的“规划器”组件(通常由大型语言模型驱动)分析请求并生成分步计划。每一步通常描述要采取的行动。执行: 一个“执行器”组件接收生成的计划,并按顺序执行每个步骤。执行器可能涉及更简单的大型语言模型调用,仅专注于执行特定步骤,或者它可能直接调用计划步骤中指定的工具。一个步骤的结果通常会反馈给下一个步骤。digraph PlanExecute_Flow { rankdir=TD; node [shape=box, style=rounded, fontname="Arial", fontsize=10, margin=0.2]; edge [fontname="Arial", fontsize=9]; Start [label="用户目标", shape=ellipse, style=filled, fillcolor="#a5d8ff"]; Planner [label="规划器(大型语言模型):\n生成分步计划", fillcolor="#eebefa"]; Plan [label="计划:\n1. 行动A(工具X)\n2. 行动B\n3. 行动C(工具Y)\n...", shape=note, fillcolor="#fcc2d7"]; Executor [label="执行器", shape= Mdiamond, fillcolor="#bac8ff"]; Step1 [label="执行步骤1:\n行动A(工具X)", fillcolor="#a5d8ff"]; Step2 [label="执行步骤2:\n行动B", fillcolor="#a5d8ff"]; Step3 [label="执行步骤3:\n行动C(工具Y)", fillcolor="#a5d8ff"; style=dashed]; FinalResult [label="最终结果", shape=ellipse, style=filled, fillcolor="#b2f2bb"]; Start -> Planner; Planner -> Plan [label="输出"]; Plan -> Executor [label="输入"]; Executor -> Step1 [label="处理步骤1"]; Step1 -> Step2 [label="步骤1的结果"]; Step2 -> Step3 [label="步骤2的结果"]; Step3 -> FinalResult [label="步骤N的结果"]; // 简化结尾 }规划与执行架构将计划生成(规划器)与分步执行(执行器)分离。优点:结构化任务: 非常适合于具有清晰、逻辑操作序列的任务。可预测性: 计划是预先生成的,使代理的整体方法更具可预测性(尽管执行细节可能有所不同)。状态管理: 管理步骤之间的状态可能更容易,因为计划提供了清晰的结构。效率: 与ReAct相比,可能需要较少的高级推断大型语言模型调用,因为主要推断发生在规划阶段。执行步骤可能使用更简单的逻辑或专注的大型语言模型调用。局限性:僵硬性: 与ReAct相比,对执行期间的意外结果适应性较差。如果某一步失败或产生意外结果,如果没有复杂的重新规划机制,代理可能难以偏离原始计划。规划复杂性: 为高复杂或模糊任务生成正确且全面的计划,对于规划器大型语言模型而言可能具有挑战性。规划与执行代理在处理多步骤过程时有效,这种过程的工作流可以事先合理确定。LangChain支持这种模式,通常涉及一个规划大型语言模型链与一个负责执行计划步骤的代理或链的结合。选择合适的架构没有单一的“最佳”代理架构;最优选择很大程度上取决于任务的性质:使用 ReAct:当任务需要与工具进行大量交互、需要根据中间结果动态调整,且解决方案路径不提前明确时。推断步骤的可解释性通常是一个优势。使用 Self-Ask(带搜索):用于问答任务,这些任务需要分解复杂查询并从网络等外部知识源检索事实信息。使用 规划与执行:用于具有明确定义、顺序步骤的任务,在这些任务中,计划可以事先可靠地生成,并且执行期间的适应性不太重要。在实际操作中,这些架构代表了基本模式。高级实现可能会混合元素,例如将重新规划融入规划与执行,或在更大计划的特定步骤中使用ReAct式推断。您选择的大型语言模型、工具的质量以及提示的精心设计,仍然是影响任何代理成功的重要因素,无论选择何种架构。随着您构建更精密的代理,尝试这些不同的推断框架对于实现高效性能将是必不可少的。