☰
大模型Agent九种设计模式全解析:从ReAct到RAG一次讲透
2026/10/15 15:09:38 网站建设 项目流程

1. 这九种模式,为什么值得花时间搞懂

我一直觉得,Agent 开发最尴尬的阶段不是“做不出来”,而是“能做出来但不知道怎么做才稳”。你让大模型去调用工具、读文档、生成报告,它好像都能跑,但换个场景就翻车,同样的需求换一种写法效果又不一样。折腾几次之后我才意识到,问题不在模型强弱,而在于我们把它放进了一个什么样的结构里。结构对了,普通模型也能干复杂活;结构不对,再强的模型也像没头苍蝇。

这九种设计模式,本质上就是 Agent 的九种“组织架构”。它们决定了 Agent 怎么思考、怎么行动、怎么纠错、怎么协作。不夸张地讲,你选一种模式,就基本选定了这个 Agent 的天花板和下限。理解它们,比记住某个框架的 API 重要得多。

我先说清楚这文章适合谁。如果你自己用代码搭过 Agent,但觉得效果不稳定、扩展困难,那这九种模式的拆解正好能帮你找到问题所在。如果你是刚接触 Agent 开发,这篇文章涉及的原理和示例也能帮你建立一个全局认知,至少看完之后不会再觉得 Agent 是个玄学东西。我会把每个模式的适用场景、踩坑点和你可能忽视的细节都讲透,代码方面则尽量给可运行的示例。文章会有点长,但这块内容确实值得一个字一个字读完。

2. 先把 Agent 想成一个外包团队,问题就好懂了

在拆解九种模式之前,我得先说一个理解 Agent 的诀窍:把 Agent 当成一个团队,而不是一个聪明的人。你雇一个全能超人,什么都会,但没有人给他派活、没有时间表、没有验收标准,他照样产出垃圾。大模型本质上也是这样,它能说会道,但它不知道什么时候该停下来,不知道哪些外部信息是必要的,也不知道自己算错了要回头检查。如果我们把 Agent 拆成“规划、调用、记忆、反思、协作”这些不同的角色,问题就会清晰很多。

这其实也解释了为什么设计模式在传统软件开发里好用,换到 Agent 开发里照样好用。传统软件架构解决的是“模块怎么搭、数据怎么流”,Agent 设计模式解决的是“思考怎么组织、行动怎么触发、错误怎么恢复”。两者关注的层次不一样,但思维方式非常相通:不靠偶然,靠结构。

我在实际项目里最常用的一句话是,不要试图让模型一次做对所有事,给它一条足够清晰的路径,让它每一步只需做一个小决策。九种模式核心就是换着花样设计这条路径。下面我就从最经典的 ReAct 开始,一个一个拆。

3. ReAct 模式:让思考和行动交替前进

3.1 原理:不再一口气回答,而是一步步“想一步,做一步”

ReAct 的全称是 Reason and Act,意思是模型在回答过程中交替进行“推理”和“行动”。这个模式的核心思路是,模型不直接生成最终答案,而是生成一个思考过程,再根据思考过程决定调用什么工具,拿到工具的结果后再继续思考,直到认为可以给出答案为止。

你可能觉得这没什么稀奇,让模型一步步思考不是常见做法么。但 ReAct 的关键在于,它把“思考”和“行动”明确地交替编织在一起。普通 CoT 只是让模型在回答前把思路写出来,但 ReAct 要求模型在每轮思考之后,要么调用一个工具,要么给出最终答案。这样每一步都有外部反馈作为依据,而不是凭空推理。

一个典型的 ReAct 循环长这样:

Thought: 我需要知道用户所在地的天气,但我的训练数据里没有实时天气信息,所以需要调用天气查询工具。 Action: 调用 get_weather(city="北京") Observation: 北京当前天气为晴,气温 5°C Thought: 好,天气信息已经拿到了,用户问的是是否需要带伞,当前晴天且没有降雨,所以不需要。 Final Answer: 北京今天晴天,气温 5°C,不需要带伞。

看到区别了吗?模型不是直接说“不需要带伞”,而是先承认自己缺少实时信息,然后主动调用工具,最后基于工具返回的事实下结论。这个“随时承认不够、随时补信息”的过程,就是 ReAct 的精髓。

3.2 适用场景:什么时候应该用 ReAct

ReAct 模式最适合那些需要多步推理、并且中间步骤需要外部信息或工具支持的任务。比如:

  • 用户问“帮我对比两款手机,看看哪一款性价比更高”,需要实时价格和配置信息。
  • 数据分析场景中,Agent 需要先查询数据库里的数据,再做聚合计算。
  • 客服场景中,Agent 需要先查到订单状态,再回答用户“我的快递到哪了”。

与之相对的,如果任务只需要模型依据自身知识直接回答,那 ReAct 就有点杀鸡用牛刀,反而增加了延迟和调用失败的风险。比如你问“李白是哪个朝代的诗人”,直接让模型回答就好,不需要走一遍思考和调用的循环。

我用 ReAct 时最看重的一点是“每一步都有依据”。它让模型不再是凭记忆瞎编,而是每一步都踩在事实上。对于一个对准确率有要求的场景,这几乎是必须的。

3.3 落地示例:用 LangChain 实现一个最小 ReAct Agent

现在我用代码给你演示一个简化版的 ReAct Agent。这里我不依赖某个重量级库,而是直接展示核心逻辑,帮助你理解它内部发生了什么。

import json # 模拟一个天气查询工具 def get_weather(city): weather_data = { "北京": {"weather": "晴", "temp": 5}, "上海": {"weather": "小雨", "temp": 12}, "广州": {"weather": "多云", "temp": 18}, } info = weather_data.get(city) if info: return json.dumps(info, ensure_ascii=False) return "未找到该城市数据" # 模拟大模型的思考逻辑(实际请替换为 LLM 调用) def llm_with_react(user_query, tools, max_steps=5): current_query = user_query for step in range(max_steps): # 这里替换成真实 LLM 调用,例如 openai.ChatCompletion.create # 我们用一个硬编码的模拟来演示结构 thought = input("[模拟思考] 请决定下一步动作(输入 'call:get_weather:北京' 或 'final:答案'):") if thought.startswith("call:"): _, tool_name, param = thought.split(":") if tool_name == "get_weather": result = get_weather(param) print(f"[观察结果] {result}") # 将 observation 追加到上下文,进入下一轮 current_query += f"\n工具返回:{result}" elif thought.startswith("final:"): print(f"[最终回答] {thought.split(':', 1)[1]}") break else: print("达到最大步数,停止循环。") if __name__ == "__main__": tools = {"get_weather": get_weather} llm_with_react("北京今天需要带伞吗?", tools)

注意上面这个简化代码里,我用input()代替了真实模型,目的是让你直观看到 ReAct 是一个“思考 → 行动 → 观察 → 再思考”的循环。真实项目中,模型输出的文本会被解析成Action和Action Input,然后由代码负责执行对应的工具,再把Observation拼接回上下文。这就是所有 Agent 框架底层都在做的事情。

3.4 高频踩坑点:循环不终止

ReAct 最大的坑是模型容易陷入死循环。它可能会反复调用同一个工具,或者想了半天还是决定继续想。解决手段有两个:一个是在系统提示词里明确“如果可以回答了,必须直接输出最终答案”,另一个是硬性设置最大轮数。我一般会同时设置两个防线,把max_steps设为 5 到 8,并在提示词里强调“要尽快收敛”。极限情况下,只要 Action 输出格式不合法,就直接报错终止,不能让 Agent 无限制地跑下去。

ReAct 的另一个坑是上下文膨胀。每执行一步,Observation 都会拼接到对话里,步骤多了以后上下文越来越长,模型反而被大量中间信息干扰。我通常会在每一轮只保留与当前目标最相关的上一步结果,或者在工具返回特别长时先做一次摘要再拼接。

4. Plan-and-Execute 与 Plan-and-Solve:先定计划,再动手干活

4.1 原理:把“全局规划”和“局部执行”拆开

ReAct 模式的问题在于,它走一步看一步,没有一个全局视角。比如你让 Agent 做一个“从零开始写一篇市场调研报告”的任务,如果走 ReAct,模型很容易在第一步就纠结于找数据,结果写了一部分才意识到漏了行业背景分析。为了解决这个问题,Plan-and-Execute 模式出现了一个明显的分工:先让模型生成一个完整的任务计划,再按计划一步一步执行。Plan-and-Solve 则是它的加强版,要求模型在生成计划时不仅写出步骤,还要说明每一步为什么这么做。

这两种模式解决的核心问题是一致的:让 Agent 具备“先规划、后执行”的能力,避免深陷局部细节而失去全局方向。如果说 ReAct 是一个即兴发挥的话唠员工,那 Plan-and-Execute 就是一个先写好周计划再逐项打勾的项目经理。项目经理的产出不一定比话唠员工快,但更可控、更不容易跑偏。

它们的执行流程大概是这样的:

  1. 规划器(Planner)根据用户请求生成多步计划。
  2. 执行器(Executor)按计划执行每一步,每一步都是一个子任务,可以由同一个模型完成,也可以调用不同工具或子 Agent。
  3. 检查点(Optional):每一步执行后,规划器评估结果是否满足预期,然后决定是继续下一步、修正计划还是结束任务。

4.2 对比表格:三种“思考类”模式的取舍

模式是否提前规划是否交替行动是否需要外部反馈典型问题
CoT否否否一次想太多容易出错
ReAct否是是容易陷入局部、反复试探
Plan-and-Execute是是(按计划)是计划可能在执行中失效

从表格可以看出,Plan-and-Execute 比 ReAct 多了一个“计划层”,但这个计划层也带来了新问题:计划一旦制定,执行时如果环境发生变化,计划很可能不现实。所以我通常会在计划之后加一个“定期重规划”机制,比如每执行三步就让规划器检查一次当前进展,并决定是否调整剩余计划。这也是我在生产环境中最推荐的变体。

4.3 落地示例:两步式代码框架

我用一个简化 Python 示例演示 Plan-and-Execute 的基本骨架。这里假设规划器已经通过一次 LLM 调用生成了计划,代码负责按计划调用工具并收集结果。

def generate_plan(task): # 真实项目中对应一次 LLM 调用,要求模型输出 JSON 数组,如: # [{"step": 1, "task": "搜索市场数据", "tool": "search", "param": "..."}] return [ {"step": 1, "task": "收集行业规模", "tool": "search", "param": "2024年国内智能家居市场规模"}, {"step": 2, "task": "收集主要玩家", "tool": "search", "param": "智能家居主要品牌市场份额"}, {"step": 3, "task": "整合报告", "tool": "writer", "param": "基于前两步数据撰写报告"}, ] def execute_step(step): if step["tool"] == "search": # 调用真实搜索引擎 API return f"模拟搜索结果:{step['param']} 的相关信息" elif step["tool"] == "writer": return f"模拟报告内容:基于数据撰写..." return "工具不存在" def run_plan_and_execute(task): plan = generate_plan(task) results = [] for step in plan: print(f"执行步骤 {step['step']}: {step['task']}") result = execute_step(step) results.append(result) # 这里可以增加一个评估器:判断结果是否符合预期,如不符合则重规划 return results

从实现角度来看,Plan-and-Execute 本身并不复杂,它甚至比 ReAct 更容易落地,因为每一步都是固定顺序执行的。真正的复杂度在重规划逻辑:什么时候判定“当前计划已经不适用了”?我一般通过两个信号判断:一是某一步执行结果与计划预期明显不符;二是连续几步都出现低质量输出。一旦触发,就调用规划器重新生成“剩余步骤”,而不是整个任务从头开始。

4.4 我的个人体会:简单任务别用重模式

Plan-and-Execute 并不是银弹。对于两三个步骤就能完成的小任务,比如“把这句话翻译成英文再润色一下”,没必要先生成一个计划,直接一步到位更高效。我见过很多团队在小任务上强行套用 Plan-and-Execute,结果就是多了一次模型调用、多了一秒延迟,收益却微乎其微。好钢要用在刀刃上,超过五个步骤、涉及多个外部依赖的任务,才是这种模式的舒适区。

还有一点,计划本身的表达方式很重要。如果计划是一大段文字,模型执行每一步时还要从长文中找自己该做什么,既费 token 又容易出错。我建议让规划器输出结构化的 JSON 数组,每个步骤包含明确的动作、工具和参数,这样执行器只需要按图索骥,不需要二次解析。

5. Reflection 模式:让 Agent 学会自我批评

5.1 原理:输出之后,再跳出来审一遍

Reflection 模式听起来很玄,其实特别朴素:让 Agent 在生成答案后,再调用同一个模型(或另一个模型)对答案进行审查和批评,再根据批评意见优化答案。这个过程就像让你写完一段代码后,再装成另一个程序员去 code review。角色变了,视角就变了,问题也更容易暴露。

标准 Reflection 流程如下:

  1. Actor(演员)角色生成初始答案。
  2. Critic(评论家)角色审查答案,找出问题,如逻辑漏洞、事实错误、遗漏要点等。
  3. Actor 根据批评意见修改答案。
  4. 可以重复多轮,直到评论家认为答案合格或达到设定轮数。

这个模式最大的价值是给 Agent 增加了一个“跳出当前思维”的机制,非常适合创造力型任务、内容生成任务和代码生成任务。比如写文章时,模型第一遍写出来的东西往往平庸,但经过一轮“批评+重写”之后,质量会有明显提升。GitHub Copilot 之类的工具在生成代码后给出一段 explanation 并让模型自查,本质上也是 Reflection 的简化版。

5.2 什么时候能靠一个模型完成反思

很多人第一反应是,让模型自己审自己?它能发现自己错在哪吗?这个问题确实存在,而且它跟模型能力强相关。能力强的模型往往具有较好的自我审视能力,能识别出自己先前输出中的逻辑问题;能力弱的模型则倾向于自我肯定,审完跟没审一样。我实测过的体会是,模型越强,Reflection 的收益越明显,如果你的模型本身质量一般,反思步骤可能只是白白消耗 token。

要让反思更有效,有几个小技巧:

  • 让 Critic 角色使用与 Actor 不同的提示词,甚至指定一个对立的立场,比如“你是一个极其严格的评审专家”。
  • 在批评阶段要求 Critic 必须输出具体的修改建议,而不是泛泛地说“内容不够好”。
  • 限制反思轮数,否则模型可能越改越偏,尤其对于主观性强的任务,改两三轮之后甚至可能脱离原始需求。

5.3 一个轻量级 Reflection 伪代码

def generate_answer(query): # 第一次生成 return "北京是中国的首都,人口约 2000 万。" def critique_answer(query, answer): # 审查生成结果,返回批评意见 return ["缺少数据来源", "建议补充城市气候信息"] def refine_answer(query, answer, critique): # 根据批评意见重写 return "北京是中国的首都,截至2023年常住人口约2100万,属于温带季风气候,夏季炎热多雨,冬季寒冷干燥。" def reflection_loop(query, max_rounds=2): ans = generate_answer(query) for i in range(max_rounds): critique = critique_answer(query, ans) if not critique: return ans print(f"第 {i+1} 轮批评:{critique}") ans = refine_answer(query, ans, critique) return ans

这段代码是 Reflection 最核心的骨架。实际项目里,通常让模型以 JSON 格式输出{"issues": [...], "suggestions": [...]},这样更便于程序判断“没有问题”和“继续修改”的分支。这里要注意,critique 和 refine 可以是同一个模型,但要通过不同的 system prompt 来模拟不同角色,否则模型很容易陷入“自说自话”的状态。

5.4 我踩过的坑:反思方向错了,越改越糟

Reflection 模式有一个很隐蔽的问题,就是批评本身可能也是错的。模型可能推翻一个本来正确的答案,或者把简单的事实改成复杂但错误的表述。我在一个知识问答场景里就遇到过这种情况,第一轮生成的答案本来是对的,模型自我批评后反而加了一段“推测性内容”,结果正确答案变成了错误答案。

解决这个问题的办法有两个方向。一个是在 Critic 提示词中专门强调“不要无中生有,只基于已有事实审查”,另一个是给 Actor 和 Critic 设定不同的信息来源。比如 Actor 可以自由发挥,但 Critic 必须基于给定的资料来验证答案是否准确,而不是凭模型内部知识乱批评。如果任务对准确率要求极高,还可以引入外部校验环节,比如用检索到的资料与模型答案做对比。

6. Multi-Agent 协作模式:让一群角色分工配合

6.1 原理:多 Agent 不是人多力量大,而是专业分工

Multi-Agent 模式不像其他模式那样只调一个模型循环,而是同时运行多个 Agent,让它们分别扮演不同角色、执行不同任务、互相传递信息。这个模式的灵感来自现实团队协作:有人负责调研,有人负责写方案,还有人负责审核把关。Agent 世界里也一样,把复杂任务拆解给多个专业化 Agent,往往比让一个 Agent 从头干到尾更高效。

常见的 Multi-Agent 拓扑有三种:

  • 组长-组员(Orchestrator-Workers):一个中央 Agent 负责拆解任务、分发子任务、汇总结果,其他 Worker Agent 各司其职。
  • 辩论/竞争模式:两个或多个 Agent 针对同一问题给出不同观点,互相辩驳,最后由裁判 Agent 或投票机制决定最终结论。
  • 流水线模式:Agent A 的输出作为 Agent B 的输入,形成一条处理链,比如“分析 Agent → 设计 Agent → 代码 Agent → 测试 Agent”。

6.2 设计要点:什么时候配一个 Router

Multi-Agent 模式的实现并不难,难的是决定“谁干什么活”。我最常用的策略是加一个路由器(Router)层。路由器 Agent 的任务是分析用户输入,决定交给哪个 Worker 处理。比如用户问“今天有什么新闻”,路由器判断应该转发给“新闻聚合 Agent”;用户上传一份合同问“有没有风险”,路由器转发给“法律审查 Agent”。角色分离得越清楚,每个 Agent 的提示词就可以越专注,效果自然越好。

不要把每个 Worker 的能力边界设定得太宽。如果你让一个 Agent 既能写代码,又能查资料,又能做设计,它就会退化成一个普通模型,失去了分而治之的意义。我一般会在提示词里明确每个 Worker 的“职责范围”和“禁止事项”,比如“你是数据分析 Agent,只负责处理与分析数据相关的事务,其他问题一律不回答”。

6.3 举例:一个调研报告生成系统

假设我们要搭建一个生成行业调研报告的系统,可以拆成这样几个 Agent:

Agent 角色职责输入输出
规划 Agent拆解任务、生成提纲用户需求调研提纲
搜索 Agent收集资料、检索数据调研提纲中的关键词资料摘要集合
分析 Agent数据分析和趋势判断资料摘要分析结论
撰写 Agent组织语言、生成报告分析结论报告初稿
审核 Agent检查逻辑、事实和格式报告初稿修订意见或终稿

实现时,规划 Agent 先输出提纲,搜索 Agent 按提纲去检索,分析 Agent 对搜索到的内容做提炼,撰写 Agent 最终成文。审核 Agent 如果发现问题,可以把报告退回给撰写 Agent 修改。整个流程是一个有反馈的流水线,而不是一次性的接力。

代码结构上,可以使用 Python 的asyncio来让多个并行 Agent 同时工作,比如搜索 Agent 可以并发执行多个关键词的检索。我在实际项目里会把并行度和结果汇总逻辑分离,比如搜索 Agent 并行执行 5 个关键词,每个返回一个结果,再统一交给分析 Agent。这样做带来的效率提升很明显,但代价是必须处理“部分搜索失败”的情况,不能因为一个失败就整个任务崩溃。

6.4 协作的代价:上下文和通信开销

Multi-Agent 模式最需要警惕的问题是上下文爆炸和通信开销。如果 A Agent 把一个超长文档传给 B Agent,B 再传给 C,上下文里充斥着大量冗余信息,token 消耗会非常惊人。我的解决办法是,在 Agent 之间传递信息之前先做摘要和结构化。比如搜索 Agent 不直接传原始网页内容,而是传“标题、来源、关键数据点、结论摘要”这样的结构化 JSON。

另一个问题是协调成本。多 Agent 系统看起来高效,但一旦某个 Agent 输出格式不标准,下游 Agent 就听不懂了。所以我会给每个 Agent 的输出定义严格的 JSON Schema,并在路由层做校验,不合格的重新生成一次。没有校验的 Multi-Agent 系统,运行时间越长,出错概率越高。

7. Tool Calling 模式:连接外部世界的关键通道

7.1 原理:让模型学会“请求别人帮它做事”

大模型本质上是一个统计语言模型,它无法真正执行代码、查询数据库、调用外部 API。Tool Calling(工具调用,也称 Function Calling)模式就是给模型开一个口子,让它在回答问题时可以请求执行某个函数,然后把函数的返回值作为进一步推理的依据。

在 OpenAI 和各大模型厂商的 API 中,Function Calling 的实现方法是:开发者把工具的描述和参数 Schema 传给模型,模型在需要时返回一个 JSON 格式的工具调用请求,而不是直接生成答案。开发者收到这个请求后执行相应工具,把结果返回给模型,模型再继续生成最终的回复。这里面最关键的是“工具的描述”和“参数 Schema”,它们决定了模型能不能正确理解和调用工具。

一个工具定义的 JSON Schema 类似这样:

{ "name": "get_weather", "description": "获取指定城市当前天气情况", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称" } }, "required": ["city"] } }

工具名称和描述看似简单,实际上直接影响调用准确率。测试下来,工具描述写得越具体,模型选择工具的准确率就越高。比如"获取指定城市当前天气情况"比"天气查询"要更能消除歧义。

7.2 工具数量的边界:不是越多越好

很多团队一上来就接十几个工具,结果发现模型经常选错工具,或者明明有合适的工具却编造参数。这是因为模型在选择工具时,要在所有工具的描述之间做语义匹配,工具越多,干扰就越大。我个人的习惯是,单次对话中暴露给模型的工具尽量控制在 5 到 10 个以内,如果工具太多,就做一层工具分组或先用路由器缩小候选范围。

另一个容易被忽视的细节是,工具调用后的结果往往非常长。比如一个查询订单状态的 API 返回了几百行 JSON,如果把全部内容塞给模型,既浪费 token 又容易让模型抓不到重点。这时候我们需要对工具结果做后处理,只保留关键字段,或者让模型先做摘要再决定下一步。我在项目里通常会写一个简单的后处理器,把工具返回的 JSON 过滤成“用户关心的字段 + 状态字段”,这比直接原样传给模型效果好得多。

7.3 兼容性和降级策略

工具调用可能会失败,原因包括参数格式错误、API 超时、结果为空等。一个成熟的 Agent 系统必须有降级策略。我在代码中会用 try-catch 包裹工具调用,如果调用失败,可以把错误信息作为 Observation 返回给模型,让模型决定是换一种参数重新调用,还是向用户说明暂时无法获得该信息。

更极端的情况是,模型可能输出了不存在的工具名称,或者参数格式完全不符合 Schema。这里我建议加一层“严格校验”,不合法就直接反馈错误,并附带正确调用示例,让模型在下一次尝试时修正。不要指望模型自行纠正,很多时候它会在同样的问题上反复犯错,必须由代码层及时拦截。

8. RAG 模式:给 Agent 装一个外部知识库

8.1 原理:用检索结果增强生成

RAG 的全称是 Retrieval-Augmented Generation,检索增强生成。它是目前让模型“知道更多”的主要手段。大模型训练数据有截止日期,无法覆盖私有文档和最新资料,但 RAG 允许我们在推理时,先从外部知识库检索出与用户问题最相关的文档片段,再把它们作为上下文提供给模型,让模型基于这些片段生成回答。

RAG 和微调经常被放在一起比较。我的体会是,微调适合让模型学习某种风格或格式,而 RAG 适合让模型获取事实性内容。如果你的需求是“回答内容必须来自公司内部文档”,RAG 远比微调可靠,因为它是检索真实片段,而不是依赖模型记忆。微调之后模型可能记错信息,RAG 则能把答案“钉”在给定资料上。

一个标准 RAG 链路包含四步:

  1. 离线索引:把文档切分成 chunk,用 Embedding 模型转成向量,存入向量数据库。
  2. 在线检索:把用户问题转成向量,在数据库中做相似度检索,取 top-k 相关片段。
  3. 上下文组装:把检索到的片段与用户问题组装成 prompt。
  4. 答案生成:模型基于组装好的 prompt 生成最终回答。

8.2 实际项目中的 RAG 注意事项

RAG 看似简单,落地时坑却不少。第一个坑是切分粒度。切得太长,检索到的内容可能包含大量无关噪音;切得太短,单个片段可能缺少必要语境。我常用的是按语义段落切分,固定 300 到 500 字左右,并且保留 20% 的重叠度,确保跨段边缘的信息不会丢失。

第二个坑是检索质量不等于生成质量,但检索质量直接决定生成质量。如果 top-k 检索结果与问题无关,再怎么强的大模型也答不对。所以一定要花心思在 embedding 模型的选型和检索策略上。对于问答场景,我通常会同时使用“向量检索”和“关键词检索”,然后把两种结果做一个融合排序。这个方案比纯向量检索更稳。

第三个坑是引用溯源。在需要严谨回答的场景,比如医疗、法律、财务,我强烈建议在 prompt 中要求模型必须标注答案来源。具体方法是把每个 chunk 的文档 ID 作为元数据传给模型,并规定格式:“(来源:[文档ID],第N段)”。这样不仅用户可以看到依据,也让 Agent 在资料不完善时敢于说“资料中未找到相关信息”,而不是强行编造。

8.3 RAG 在 Agent 中扮演的角色

在 Agent 语境下,RAG 往往不是单独运行的,而是作为 Tool Calling 中的一个工具来使用。比如一个“法律顾问 Agent”在回答用户问题时,可以先调用“内部法规检索”工具,工具内部执行 RAG 链路,然后把检索结果返回给 Agent,Agent 再据此回答。这种做法把 RAG 的复杂度封装在工具内部,Agent 无需关心向量数据库和 chunk 逻辑,只需要决定“什么时候需要搜索、以什么关键词搜索”即可。

要注意的是,检索工具的参数设计很关键。如果工具只接受一个query参数,模型有时不知道怎么把复杂问题简化成关键词。我一般会增加几个可选参数,比如top_k、date_range、doc_type,让模型有更多控制空间。参数越多,模型的理解负担越重,所以也要拿捏好度。

9. Memory 模式:从“阅后即焚”到长期记忆

9.1 原理:三种记忆层次

人和 Agent 的一个重要区别是记忆。大模型本身没有记忆能力,每次对话都是“阅后即焚”的。但在很多场景下,Agent 需要记住用户偏好、历史记录、业务上下文等信息,才能提供连贯的服务。Memory 模式的本质就是为 Agent 建立一套记忆机制。

我把 Agent 的记忆分为三个层次:

  • 会话级记忆(短期):当前对话的上下文,通常直接放在 prompt 中,受 token 限制。
  • 长期记忆:跨会话存储的用户偏好、事实信息,使用向量库或键值库存储,在需要时检索注入。
  • 内部记忆(工作记忆):Agent 当前任务执行过程中的中间状态,比如已经完成哪些步骤、有哪些结论。

会话级记忆最容易被忽视。很多人直接把多轮对话全部塞进 prompt,造成上下文膨胀。我习惯的做法是,超过一定轮次后,对早期对话做摘要,保留最近几轮完整对话。这个“摘要 + 最近轮次”的组合,既节省 token,又不会让模型完全忘记前面的内容。

9.2 长期记忆落地:存储什么、怎么检索

长期记忆的设计要回答两个问题:存什么、怎么取出来。存的时候,不要一股脑把用户说过的所有话都存起来,而要筛选出真正具有长期价值的事实。比如用户说“我目前负责公司的新媒体运营”,这个信息应该存;用户说“今天天气真热”,这个信息不需要存。我会在对话结束后,专门安排一次“记忆提取”的模型调用,把对话内容转成一条条结构化记忆,例如{"type": "user_info", "content": "用户负责新媒体运营"}。

检索的时候,记忆不能全盘注入,否则和上下文膨胀没有区别。我常用的做法是在用户发起新请求时,先用用户 ID 去记忆库中做相关性检索,只召回最相关的 3 到 5 条记忆。比如用户问“我这周的任务安排怎么样”,系统会优先召回与任务安排相关的记忆,而不是把用户一年前的聊天记录全部翻出来。

长期记忆还可以进一步分成“事实记忆”和“事件记忆”。事实记忆描述用户属性,比如姓名、偏好、身份;事件记忆描述发生过的事情,比如“上周完成了项目立项”。不同类型记忆的检索权重应该不同,回答事实型问题时优先用事实记忆,回答提醒类问题时优先用事件记忆。

9.3 一个轻量级记忆模块示例

import sqlite3 import json class MemoryManager: def __init__(self, db_path="memory.db"): self.conn = sqlite3.connect(db_path) self.conn.execute(""" CREATE TABLE IF NOT EXISTS memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT, memory_type TEXT, content TEXT, embedding BLOB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) self.conn.commit() def add_memory(self, user_id, memory_type, content, embedding=None): self.conn.execute( "INSERT INTO memories (user_id, memory_type, content, embedding) VALUES (?,?,?,?)", (user_id, memory_type, content, embedding) ) self.conn.commit() def retrieve_memories(self, user_id, query_embedding, top_k=3): # 真实项目中此处用向量相似度检索 # 简化实现:直接按 user_id 取出,再交给 LLM 筛选 cursor = self.conn.execute( "SELECT content, memory_type FROM memories WHERE user_id=? ORDER BY created_at DESC LIMIT ?", (user_id, top_k) ) return cursor.fetchall()

这个小实现展示了记忆管理的核心要求:存储介质、用户标识、记忆类型、时间戳。真实项目可以把 SQLite 换成更强大的向量数据库,但概念是相通的。别忘了在记忆管理中加入“忘记”机制,比如定期清理过时记忆或让用户主动清除记忆,这是产品合规和体验上不可回避的问题。

10. Human-in-the-loop 模式:关键时刻让人拍板

10.1 原理:不是所有审批都该自动化

Human-in-the-loop(人在回路中)是指在 Agent 工作流的某些节点上,插入人工确认或审批环节。这个模式不是 Agent 无能,而是面对高风险操作时,必须让人来承担最终责任。比如自动给客户发邮件、自动扣款、自动删除数据库记录,这些操作一旦执行就无法撤回,全部交给 Agent 自主决定风险太高。

这个模式与其他模式不是互斥关系,而是一种叠加机制。你可以在 ReAct 流程中的关键工具调用前插入人工确认,也可以在 Multi-Agent 的审核 Agent 上报疑点后转人工处理。关键问题有三个:在哪个环节插入人、以什么方式通知人、等待多久没响应怎么办。

插入环节的选择取决于风险的严重程度。一个金融客服 Agent 中,“查询余额”不需要人工介入,“转账超过一万元”则需要人工确认。从实现上看,可以给工具打上“需要审批”的标签,框架在调用这类工具前暂停流程,推送给管理员审批。

10.2 实现方式:异步审批 + 超时策略

我在实际项目中实现人工审批时,通常不会让 Agent 流程“卡住”等待,而是采用异步方式:Agent 发起一个审批请求,把当前状态保存起来,然后结束这一轮;管理员在后台处理审批,通过后系统自动触发 Agent 继续执行。这种方式避免长时间占用调用线程,也让管理员有充足的时间去处理。

超时策略是必须提前规定的。管理员可能不在线,或者忘记审批。我一般设置三个分支:同意、拒绝、超时未响应。超时后默认拒绝,并且给用户明确提示“该操作因未获人工确认而取消”。这比无限等待更安全。

在实际产品中,人工审批界面要做到三件事:展示操作上下文、展示 Agent 判断依据、让管理员一键同意/拒绝。如果只给管理员一个冷冰冰的按钮,没有上下文信息,管理员很难做出决策。我现在会给管理员展示 Agent 的思考过程摘要和工具返回的关键数据,确保审批者不用去翻原始日志。

10.3 什么场景必须上 Human-in-the-loop

我总结了几类必须保留人工介入的场景:

  • 资金操作:转账、支付、退款等。
  • 账号操作:修改密码、注销账号、改动权限。
  • 对外沟通:自动发送邮件、群发营销消息。
  • 数据删除:批量删除用户数据或生产环境数据。
  • 法律合同:自动签约、承诺某种法律责任。

在这些场景里,就算 Agent 的准确率已经达到 99%,那 1% 的风险可能也是不可接受的。用人工介入换确定性,在成本上绝对划算。而且从用户信任角度看,有人工确认环节反而让用户更放心。

11. 模式组合与选型:没有银弹,只有权衡

11.1 模式选型决策表

单一模式解决不了所有问题,九种模式之间也不是互斥关系。实际上,成熟项目几乎都是多种模式的组合。比如一个客服系统可能是“Tool Calling + ReAct + RAG + Memory + Human-in-the-loop”的混合体。

我整理了一个选型决策表,你可以根据自己的场景去匹配:

需求特征推荐核心模式可选叠加模式
需要精确回答私域知识问题RAGTool Calling
需要多步推理,逐步验证ReActReflection
需要长任务、多阶段交付Plan-and-ExecuteHuman-in-the-loop
需要多个角色分工协作Multi-AgentMemory
需要工具调用访问外部系统Tool CallingHuman-in-the-loop
需要个性化长期服务MemoryRAG
需要降低错误风险、提高答案质量ReflectionHuman-in-the-loop

这个表是经验性的,不是标准答案。我的建议是,从最核心的一个模式入手,跑通之后再叠加其他模式。不要第一天就所有模式一起上,否则出了问题你根本定位不到是哪一环的锅。

11.2 我从项目中学到的组合顺序

如果让我给一个从零开始的 Agent 项目选路,我会按这样的顺序演进:

  1. 先做 Tool Calling,保证 Agent 能访问外部工具。
  2. 加 ReAct 循环,让它能够在工具调用和推理之间来回切换。
  3. 如果任务步骤多、容易跑偏,引入 Plan-and-Execute。
  4. 如果希望答案质量更高,加 Reflection。
  5. 如果涉及内部专属知识,把 RAG 封装成一个检索工具注入。
  6. 如果用户需要跨会话记忆,加 Memory 模块。
  7. 如果存在高风险操作,在关键节点插入 Human-in-the-loop。
  8. 多角色需求出现时,再拆 Multi-Agent。

这个顺序有一个核心逻辑:先确保“能做”,再追求“做得好”,最后解决“敢做”。每一步都是在前一步的基础上叠加的,不会产生架构上的大返工。

11.3 组合时最常见的问题:职责重叠

组合模式最容易出的问题,是各个模块的职责重叠。比如你既用了 Plan-and-Execute 又用了 ReAct,那“谁来思考下一步”就变得模糊;你既用了 Memory 又用了 RAG,那“资料到底来自记忆还是知识库”也需要区分。我在设计系统时会明确每层模块的边界,并在提示词里约定:规划器只负责写计划,执行器只负责调用工具,反思器只负责审内容,互不越权。一旦越权,输出质量必然下降。

另一个经验是,每一层都应该有独立的可观测性。我要能在日志中单独看到某个工具被调用了几次、某次反思改了什么、哪条记忆被召回。一个没有可观测性的组合型 Agent,出问题等于大海捞针。至少要把每个关键节点的输入输出记录下来,方便回溯。

12. 常见问题与排查技巧实录

12.1 Agent 陷入循环怎么办

这是最常见的问题。现象是 Agent 反复调用同一个工具,或者不断“思考”却不给出最终答案。排查手段有三个:

  1. 检查工具调用是否有副作用。比如重复创建订单、重复扣费等,这类工具必须加幂等控制。
  2. 在 prompt 中加入终止条件,例如“如果你已经获得所需信息,不要再调用工具,直接回答”。
  3. 在代码层强制限制最大轮次,比如 5 到 8 轮,超限直接终止并返回当前内容。

我在项目中通常三个手段一起用。代码层限制是保底,prompt 约束是常规手段,工具幂等是安全兜底,三者缺一不可。

12.2 工具返回内容过长

工具返回一个大 JSON,直接把模型给“冲晕”了,生成的答案就会偏离主题。我的处理方式是在工具层就做截断和摘要。比如对于查询 API,只返回与当前问题相关的字段;对于搜索 API,只返回标题、摘要和 URL。如果确实需要完整长文本,就先把文本切块,只让 Agent 读取当前步骤需要的那一部分。

12.3 反思把正确答案改错了

这个问题我在前面提到过,解决方案是:只让反思器基于事实纠错,禁止凭模型记忆“丰富”答案。具体操作时,我会在反思器的 prompt 里加一句“只能依据给定资料和已经获得的事实进行修改,不要补充资料中不存在的信息”。如果项目条件允许,还可以引入一个独立的验证器,比如用规则引擎检查格式,用外部 API 核对数据。

12.4 多 Agent 之间格式不一致

每个 Agent 都应该有独立的输出格式约束。我通常采用 JSON Schema 输出,并在各 Agent 之间加一个“协议层”。协议层负责将上一个 Agent 的输出转为下一个 Agent 可用的标准格式。这有点像传统开发中的 API 网关,能做校验、转换和错误处理。有了协议层,即使某个 Agent 的输出格式变了,也只需要修改协议转换逻辑,不必重写所有下游 Agent。

12.5 检索不到相关文档

RAG 场景下检索不到相关内容,先别急着换向量数据库。多数情况是切分粒度不对,或者 embedding 模型不适合这个领域。我建议先做一次简单的调试:把几个典型问题拿出来,直接对文档片段做关键词匹配,看看片段内容是否包含答案。如果不包含,说明切分有问题;如果包含但检索不到,再考虑换 embedding 或调检索策略。这个排查顺序能帮你快速定位瓶颈,而不是盲目换方案。

13. 最后再分享一点我的个人心得

九种模式拆开讲完,我想说说这些模式背后共通的东西。它们本质上都是在做同一件事:把不可控的大模型行为,装进一个可控的框架里。RAG 用事实约束幻觉,ReAct 用行动约束推理,Reflection 用批评约束自满,Human-in-the-loop 用人工约束风险。模式不是银弹,但它能把模型的不可靠压缩到一个可接受的范围。

我在实际项目中最大的体会是,不要追求一次做到完美,而是先跑通一个最小闭环,再加一个轮子,再改一处细节。九种模式完全可以一步一步引入,每次引入前想清楚一个问题:它帮我解决了哪个具体的痛点?如果回答不出来,就先不要加。

模式越多,系统越复杂,调试成本和 token 成本都会上升。好的 Agent 设计,应该是“用最少的模式,解决最多的问题”。希望这篇解析能让你在设计自己的 Agent 时少走一些弯路。如果你在实际项目中碰到某个模式失效的情况,不妨回头看看是不是“组合方式”出了问题——很多时候不是模式错了,而是模式之间的衔接错了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询