首页 / 资讯中心 / 文章详情

Agentic Design Patterns实战:从ReAct到Multi-Agent的选型、实现与避坑指南

Agentic Design Patterns实战:从ReAct到Multi-Agent的选型、实现与避坑指南 ★ FEATURED ARTICLE
简介这是一份面向AI研发、LLM应用开发与智能系统设计人群的英文原版技术书PDF聚焦Agentic设计模式覆盖从基础概念到高级架构的完整路径。书中重点讲解ParallelAgent与SequentialAgent等并行化与流程编排、短期与长期记忆管理、Human-in-the-Loop人机协同、RAG知识检索增强、任务优先级排序、多代理协作、评估监控机制以及推理引擎内部工作机制并通过Google ADK、LangChain等工具代码示例说明落地方式同时强调高风险场景下安全性、透明性与责任性设计。资源为单个PDF文件压缩包约17.56MB适合离线阅读与反复查阅。目前已有570人学习下载关注度在同类资源中较为突出。阅读后可掌握用并行化提升代理效率、用上下文工程处理复杂任务、设计带人工反馈的安全AI系统并理解自我验证与合同式交互等高可信智能代理的构建思路。1. Agentic Design Patterns从“能聊天”到“能干活”到底差在哪一步如果只把大模型当聊天框用你永远不会碰到 Agentic Design Patterns 要解决的问题。可一旦让它去查天气、算报表、操作数据库聊天时那种一问一答的交互立刻失效模型不知道先做什么后做什么不知道出错之后怎么自救也不清楚什么时候该收手。Agentic Design Patterns 就是为这套问题被从业者提炼出来的组织方式用循环、规划、工具调用和角色分工把模型从“张口就来”变成“按流程办事”也是构建 Intelligent Systems 时最常被复用的几块骨架。这套模式适合正在做智能客服、数据分析助手、自动化流水线并且已经被模型一本正经胡说八道折磨过的工程师。下文按选型、实现、调参、避坑、验收的顺序把这套东西讲透。2. 先拆模式再选型三种常见 Agentic 结构的适用边界我最早做 Agent 时吃过一个亏拿到需求后第一件事是翻框架文档把多智能体当成默认答案往上套结果两三个角色互相传话排查一次问题要翻几百行日志。后来想明白了Agentic Design Patterns 不是框架给你的现成模板而是你对任务形态的判断结果。选型第一步不是写代码是回答三个问题任务链条有多长、步骤是否固定、中间需不需要人参与。这三个问题的答案基本就把模式定下来了。2.1 ReAct 循环为什么它一上来就够用ReAct 是“思考-行动-观察”循环。模型每一轮先描述自己的思路Thought再决定调用哪个工具、传什么参数Action拿到工具返回结果后Observation判断下一步是继续调工具还是直接给最终答案。整个循环不预设步骤每一步都是模型根据当前上下文临时决定的所以特别适合开放型任务。落地的时候常见做法是把循环包在一个 for 循环里由模型自己判断是否终止只要响应里没有工具调用请求就说明模型认为答案已经可以交付。实现成本在几种模式里最低一个主循环加一个工具注册表就能跑通。如果你做智能客服、信息查询助手这类单线程任务ReAct 通常是性价比最高的起点。ReAct 的短板也明显它没有全局视野。模型每一步只知道当下这一步该干什么视野被限制在最近几轮消息里。任务链条一旦拉长或者流程中途需要频繁回溯ReAct 就容易走弯路。我见过一个数据分析 Agent 连续调了十几次工具每次都在上一步的结果上打转因为模型已经忘了最初要回答的是什么。这种场景就得引入规划。2.2 Plan-and-Execute长任务靠把决策提前到第一步Plan-and-Execute 把“思考”放到循环外面。模型先根据用户请求生成一份完整计划比如“第一步查订单表第二步按区域聚合第三步生成报表”然后执行器按计划逐步执行每跑完一步检查结果是否和计划预期相符。如果执行结果和预期不符才触发重新规划。这个模式适合任务步骤明确、周期较长的场景。典型例子是数据分析流水线用户说“分析上个月各区域销售额”Agent 先列出取数、清洗、聚合、出结论这几步再一步步执行。相比 ReAct它省掉了每一步都要重新想“下一步干嘛”的 token 开销而且执行到一半挂了你手里还有一份计划可以用来定位问题出在第几步。但代价也很实在计划本身可能出错。模型第一轮生成的计划如果漏掉关键步骤后面所有执行都会沿着错误方向走直到最后一步才发现结果对不上。所以落地时一定要加 watchdog 机制每完成一步让模型判断“这一步结果是否符合计划预期”不符合就回到规划阶段重新来而不是硬着头皮把计划执行完。2.3 Multi-Agent角色分工的收益和它背后的复杂度Multi-Agent 是把任务拆给多个角色各管一摊比如一个负责查数据、一个负责生成文案、一个负责质量检查角色之间靠消息传递协作。这个模式常被包装成“一群 AI 一起干活”的概念听起来很美但背后的复杂度必须靠日志和状态管理去偿还不是白捡的。我一般只在任务天然具备职能隔离时才用 Multi-Agent。比如内容生产流水线策划角色先出提纲写作角色写初稿校对角色查事实。三个角色各用各的提示词和工具职责不重叠。这种情况下多智能体的价值很明显每个角色的上下文更干净工具权限可以收窄到最小出了问题责任边界也清楚。反过来任务本身是紧密耦合的单线程流程比如“查天气再决定带不带伞”那就没有拆的必要。多角色协作的好处是各管一摊坏处是中间传递的信息需要双方对上下文的理解完全一致而这恰恰是模型最不稳定的地方。多智能体系统里最常见的翻车现场就是 A 角色生成的结果 B 角色看不懂然后无限循环追问。2.4 选型的判断顺序先跑通单 Agent再考虑拆协作者三种模式不是互斥的它们更像分层ReAct 是最底层的执行单元Plan-and-Execute 是包裹在执行单元外面的决策层Multi-Agent 则是多个执行单元的组织方式。一个计划驱动的多智能体系统里每个角色内部完全可以用 ReAct 循环。我的选型顺序是这样先用一个 ReAct Agent 把主流程跑通哪怕它会走弯路先让端到端链路成立。然后看日志里的步数分布如果步骤经常超过六步再往 Plan-and-Execute 方向重构。只有当单个 Agent 的提示词已经臃肿到互相冲突时才考虑拆成多角色。这个顺序背后的逻辑是复杂度要等你亲眼看到问题再引入而不是提前预支。三种模式的适用边界可以粗略对比如下表模式核心思想适合场景不适合场景ReAct思考-行动-观察循环短链条、开放任务、工具选择不确定超长链路、强依赖全局规划Plan-and-Execute先规划后执行步骤明确、周期较长的任务需求模糊、计划容易遗漏关键步骤Multi-Agent角色分工协作职能隔离清楚的任务紧密耦合的单线程任务注意表格里的“不适合场景”不是绝对禁止而是你心里要提前有数一旦选择了某种模式需要配套什么机制来兜底。比如 ReAct 要配安全阀Plan-and-Execute 要配重新规划触发条件Multi-Agent 要配消息格式校验。这些兜底机制往往比模式本身更花时间。3. 从零跑通一个 ReAct Agent最小代码骨架和工具调用闭环选型定了剩下事情就简单了。这一章以 ReAct 为例因为它最能体现 Agentic Design Patterns 的核心循环逻辑。我会用 Python 和 OpenAI 的函数调用接口写一个最小实现代码不依赖任何 Agent 框架方便你在自己的项目里直接改。3.1 最小骨架一个主循环驱动的 Agent 核心先把工具和执行循环写出来。核心思路其实很短把消息历史喂给模型模型要么返回最终答案要么返回一组工具调用请求如果是后者执行工具并把结果回传然后进入下一轮循环。import json from openai import OpenAI client OpenAI() def get_weather(city: str) - dict: # 真实项目中这里换成天气服务调用本地用静态数据演示 return {city: city, weather: 多云, temperature: 26} def get_stock_price(code: str) - dict: # 真实项目中这里换成行情接口 return {code: code, price: 12.34, currency: CNY} # 注册表schema 是给模型看的函数说明书fn 是真正被调用的函数 TOOL_REGISTRY { get_weather: { schema: { type: function, function: { name: get_weather, description: 查询指定城市当前的实时天气情况。, parameters: { type: object, properties: { city: {type: string, description: 城市名比如北京、上海} }, required: [city] } } }, fn: get_weather, }, get_stock_price: { schema: { type: function, function: { name: get_stock_price, description: 查询指定股票代码的最新成交价。, parameters: { type: object, properties: { code: {type: string, description: 六位股票代码比如600519} }, required: [code] } } }, fn: get_stock_price, }, } def run_agent(user_query: str, max_steps: int 5) - str: messages [{role: user, content: user_query}] for step in range(max_steps): resp client.chat.completions.create( modelgpt-4o-mini, messagesmessages, tools[item[schema] for item in TOOL_REGISTRY.values()], tool_choiceauto, ) msg resp.choices[0].message messages.append(msg) # 没有 tool_calls 说明模型认为可以直接回答了 if not msg.tool_calls: return msg.content for tool_call in msg.tool_calls: fn_name tool_call.function.name try: args json.loads(tool_call.function.arguments or {}) except json.JSONDecodeError: args {} tool TOOL_REGISTRY.get(fn_name) if tool is None: result {error: f未知工具: {fn_name}} else: try: result tool[fn](**args) except Exception as e: # 关键工具内部报错不要抛出去包成 dict 让模型自己处理 result {error: str(e)} messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse), }) return 达到 max_steps 上限任务未在限定步数内完成逻辑说明这里的循环只有两个出口——模型不返回 tool_calls说明信息已经搜集够可以交付答案步数打满 max_steps说明系统判断任务复杂度过高或陷入异常强制终止并返回提示。两个出口缺一不可后者是生产环境的最后一道防线。再说三个容易忽略的点。第一messages.append(msg) 必须在执行工具之前发生这样 tool_call_id 才能和 assistant 消息里的调用记录对上API 校验才会通过。第二工具异常被捕获后包装成 {error: ...} 回传而不是直接抛给调用方这是整个循环的自愈能力所在——模型下一次迭代会读取错误信息判断是自己参数传错了还是工具本身出了问题然后决定重试还是放弃。第三执行顺序是模型返回的 tool_calls 数组按模型内部顺序排列你按顺序逐个执行并回填即可这一步没有并发也就没有竞态问题。参数说明max_steps5 是个保守值常见做法是 3 到 8 之间。查询类任务一般三步内能完成一旦超过五步大概率是理解偏差而不是任务真需要这么多动作这时直接终止比让 Agent 继续试更省成本。model 选支持函数调用的模型gpt-4o-mini 这档的推理能力对简单任务足够复杂规划可以换更强的推理模型后面第 4 章细说。3.2 工具注册表与 Schema让模型看得懂、调得对工具注册表是整个 Agent 最容易做糙的地方。从上面代码能看到每个工具由两份东西组成一份是 function schema给模型描述“这个函数叫什么、干什么、参数有哪些约束”一份是实际执行的 fn。schema 的质量直接决定模型能不能正确生成参数这里有几个反复踩过的坑。第一description 要写“什么场景用”不要只写“是什么”。比如 get_weather 的 description 写成“查询指定城市的实时天气”模型很容易在用户问气温时想到它如果写成“获取气象数据”模型可能在任何涉及天气的语境里都调它甚至误用。第二每个参数都要写示例。parameters.properties 里 city 的描述加上“比如北京、上海”模型生成参数的准确率明显提升。第三required 列表必须和实际函数签名一致少一个字段模型就不填多一个字段模型可能补一个不存在的参数两边都会翻车。第四schema 里不要塞任何“行为指令”。比如你希望工具失败后自动重试应该写进系统提示词而不是写进 get_weather 的 description。因为 schema 会被模型当成“外部世界的客观描述”你在里面写“如果失败请重试”模型可能会把它当成工具的自然行为反而不去检查错误返回。3.3 给循环装安全阀超时、步数和异常回传最小骨架能跑但距离能上生产还差几个安全阀。第一个是异常回传已经在代码里做了但要注意回传内容的格式一定要是 JSON 可序列化的 dict不要传纯字符串否则模型对错误类型的判断会变差。第二个是步数上限max_steps 只是个笼统限制更细的做法是加一个最近调用结果缓存检测重复调用last_results {} # 在 for 循环内部、执行工具前增加这一段 key (fn_name, json.dumps(args, sort_keysTrue)) if key in last_results: result dict(last_results[key]) result[_note] 相同参数已调用过且结果未变化请基于现有结果直接回答不要重复调用。 else: result tool[fn](**args) last_results[key] result这段代码把“工具名 参数序列化”作为键缓存结果。如果模型在下一轮用完全相同的参数再次调用同一个工具就把旧结果直接回传并附一句显式提示。这样做有两个作用阻断死循环的惯性以及避免真实工具被反复调用产生费用或副作用。第三个安全阀是整体超时。步数限制只能挡住循环挡不住单次工具调用卡住。常见做法是给整个 run_agent 包一层超时控制比如 60 秒跑不完就返回“部分完成”并附带上已经收集到的信息。很多系统舍不得这一步结果生产环境里一个第三方 API 卡住整个 Agent 实例被拖到超时用户等半天只拿到一个空白页。这种事故我经历过不止一次血泪经验摆在这。提示超时时间不要拍脑袋定。先跑一周日志看线上 p95 步数和 p95 单步耗时再把超时设成这两者乘积的 1.5 倍左右。既不会频繁误杀也不会让用户无限等待。4. 参数与提示词调优让 Agent 行为可预期的四个关键旋钮代码骨架跑通之后Agent 的行为还是不听话。同样是“查询北京天气”有时候模型会直接回答有时候非要先调一次工具再回答甚至有时候调了三次工具还在追问。这些差异大部分来自参数和提示词的设置方式。这一章把四个影响最大的旋钮一个个过一遍每个都有默认值和建议调法。4.1 temperatureAgent 任务的默认值和调法temperature 在生成式任务里是创造力的开关但 Agent 的核心动作是工具调用和参数生成这两件事都不需要创造力。温度太高模型会在参数里编造城市名、股票代码或者把 JSON 字段拼错。我一般把 Agent 的 temperature 设在 0.2 到 0.4 之间0 是纯贪婪行为最稳定但对 prompt 的微小变化太敏感0.3 到 0.4 保留一点灵活性让模型在遇到没见过的 tool response 时还能换个思路自救。这里有个玄学现象值得说temperature 和模型家族有关系。有的模型在 0 的时候工具调用表现最好换一个模型后 0 反而会退化成重复同一个动作。所以不要照搬网上的推荐值把 0、0.2、0.4 三个值各跑几十条测试用例统计工具调用成功率选一个。选完之后不要再动Agent 调优里最怕的就是一边改 prompt 一边改 temperature出了问题分不清谁干的。4.2 max_steps 和超时让循环有边界前面说过 max_steps 是最后防线这里补充一点它也是成本控制器。每一步循环就是一次模型调用调用次数直接决定账单。给一个参考线查询类任务 3 步分析类任务 5 步涉及多轮用户澄清的对话类可以到 8 步。超过 8 步还跑不完的几乎可以断定是模式选错了比如本来就该用 Plan-and-Execute 的任务硬塞给 ReAct。超时方面除了给整体 Agent 运行加时间上限还要给每个工具调用加独立的超时。常见做法是把工具执行包在 concurrent.futures 的线程池里设置 timeout超时返回 {error: tool timeout}。这样单个工具卡住不会拖垮整个循环模型读到超时错误后还可以选择跳过这个工具继续别的动作。4.3 系统提示词把约束写在模型的注意力范围内Agent 的系统提示词和聊天机器人的提示词是两种写法。聊天机器人要的是人设和语气Agent 要的是决策规则和边界。我一般会固定一套模板角色你是任务执行助手通过调用工具获取真实数据后回答问题。 规则 1. 不要编造工具结果所有答案必须引用工具返回的字段。 2. 工具报错时先检查参数是否紧缺修正后最多重试一次重试仍失败则向用户说明。 3. 同一个工具连续调用两次且结果相同立即停止基于已有信息回答。 4. 每轮思考控制在两句话以内不要输出与工具调用无关的分析。 5. 最终回答用一到三段中文不允许输出过程日志。提示词里有几个点值得展开。第一条“不要编造工具结果”看着简单实际上一旦漏掉模型在工具返回非法值时会自作主张补一个合理值进去这在金融和医疗场景里是事故级别的问题。第二条给重试设了上限否则模型会在同一个错误参数上反复尝试。第三条配合 3.3 节的缓存检测是双保险。第四条约束思考长度因为思考内容也会占用上下文空间省下来的 token 可以放更多工具结果。第五条约束输出格式避免模型把思考过程混进最终答案。系统提示词写完之后要做一次回归把历史里所有翻车的输入拿出来重新跑一遍看错误行为是否被规则覆盖。用这个清单来维护提示词版本而不是凭感觉往里堆句子。4.4 模型选型规划用强模型执行用快模型Agent 不是只有一个模型在跑。复杂任务里负责规划的模型和负责单步执行的模型可以分开选。规划阶段需要综合理解用户意图、拆分步骤、判断依赖关系这些对推理能力要求高用更强、参数更大的模型。执行阶段是机械的工具调用和结果整理延迟敏感用小而快的模型。常见做法是用一个强模型做 planner一个快模型做 executor中间通过消息传递衔接。多智能体场景更是如此不同角色本来就可以用不同模型。比如查数角色用快模型因为动作是重复的 SQL 拼接合规检查角色用强模型因为判断边界需要语义理解。角色之间的模型差异不用刻意统一成本也会因此摊开。另一点提醒不同模型 API 的函数调用格式是有差异的切换模型时跑一遍工具调用回归集不要直接替换。5. Agent 踩坑实录循环失控、参数幻觉和状态错乱的排查套路这章写我在真实 Agent 项目里踩过的坑。每条按“现象、原因、处理”的顺序写你可以直接对着自己的日志找。5.1 死循环同一工具被连续调用十几次现象线上日志显示 Agent 反复调用同一个工具比如不停查某城市天气每轮返回结果完全一样模型还在继续调用直到 max_steps 强制终止。用户看到的是请求最后返回一段“达到最大步数运行结束”完全没有可用答案。原因多半是工具返回结果和模型心里的预期不匹配。比如模型想查“北京明天会不会下雨”但工具只返回了今天的天气模型发现信息不够又不会换个工具或换个参数只能拿同一个参数再试一次寄希望于结果变化。另一类原因是提示词里没有“结果相同就停”的规则模型默认继续试。处理三层叠加。第一层在系统提示词里写死“相同参数连续调用两次结果不变直接用已有信息回答”。第二层用 3.3 节的缓存检测拦截重复调用。第三层把 max_steps 从默认 5 降到 3配合日志监控线上单任务平均步数一旦超过 2.5就说明有异常请求在消耗配额该调提示词而不是调步数上限。5.2 参数幻觉模型编造了 Schema 里不存在的字段现象工具描述里参数名是 city模型调用时传了 city_name或者日期参数写成“今天”而不是具体的日期字符串。函数执行直接因为缺少必填参数报错模型读到报错后开始手忙脚乱地重试有时会把参数改成更离谱的值。原因schema 的 description 写得不够具体模型对参数格式只能靠猜。尤其是中文场景自然语言里的“城市”和字段名 city 之间没有天然对应关系模型容易按自己的表达习惯生成参数。另一个原因是缺少示例值模型没见过合法格式。处理在 schema 的描述里给每个字段加示例能写枚举就写枚举能写格式就写格式。比如 city 描述改成“城市中文名例如北京、上海”。代码侧加参数校验用 dataclass 或 Pydantic 模型接收参数校验失败就把“需要 city 字段类型为 string”回传给模型让模型自己纠正。这一步是必做的线上模型生成 JSON 偶尔会带多余字段不能指望提示词完全管住。5.3 目标丢失多轮调用后 Agent 忘了最初任务现象任务开始时模型还在查天气几轮之后突然开始介绍当地美食或者中途插入一段和用户请求无关的总结。看起来像模型“跑题”实际上是早期指令在长上下文中被稀释了。原因Agent 每一步循环都会往 messages 里追加消息十几轮下来原始用户请求已经缩在几十条消息的最前面。模型注意力会更看重更近的内容尤其是工具返回的中间结果目标优先级被压到最低。处理把不可变目标放进系统提示词而不是只放在第一条 user 消息里。系统提示词每轮都会参与计算权重稳定得多。更彻底的办法是压上下文每积累五轮工具结果就调用一次摘要模型把中间过程压成一段话替换掉原始的工具调用历史。“原始请求 摘要 最近一轮完整消息”三件套放进 messages既能保住目标又能控制 token 消耗。5.4 并行调用冲突两个工具的结果互相覆盖现象模型一次响应里发出两个 tool_calls比如一个写配置文件、一个查询配置项执行器按顺序执行先查后写最终返回给模型的查询结果是旧值模型基于旧值给出的回答和实际配置不一致。原因模型允许一次响应申请多个工具调用但执行器是串行执行的。如果多个调用之间存在“先写后读”的依赖串行顺序会把结果带歪。另一个常见场景是多个写操作同时修改同一个键后执行的覆盖先执行的。处理在执行器里区分只读和写工具。遇到同一批 tool_calls 里有写操作一律按顺序执行且同一资源的多个写操作加锁。更简单的做法是改提示词“同一轮内不要同时调用写操作和读操作”。不过不能只靠提示词执行器侧的读写分类才是硬保障。5.5 工具返回格式不统一模型解读中间结果越来越费劲现象有的工具返回 JSON有的返回纯文本有的返回 Markdown 表格。模型在多轮工具调用后解读格式各异的返回结果开始出错甚至把文本当 JSON 解析下一轮参数也跟着错。原因Schema 只约束了入参没约束出参。工具实现者各自返回自己习惯的格式模型被迫在每一轮里做格式识别识别错了整个链路就断了。处理所有工具返回统一转成 JSON 字符串。文档型数据包一层 {data: ...}错误包一层 {error: ...}元信息放 {meta: ...}。在注册表层面写一个统一包装函数工具函数只负责返回业务 dict包装逻辑集中处理。模型解读成本降下来之后整个循环的稳定性会肉眼可见地提升。6. 收尾技巧日志、评估和灰度三件事决定 Agent 能不能稳定上生产前面几章把 Agent 的选型、实现、参数和避坑都过了一遍最后说三个我每次上生产前都会做的事按重要性排序。第一件事是全量日志。用 JSON Lines 把每一轮循环完整落盘请求消息、模型响应、tool_call 内容、工具返回结果、耗时、步数。不要只记最终答案因为线上怀疑模型某个步骤做错时没有中间日志你就是在黑匣子里找 bug。格式可以简化成一行一个事件字段用 event 区分方便直接接日志平台。第二件事是定三个评估指标任务完成率、平均步数、工具调用成功率。任务完成率靠人工或者用更强的模型当 judge 打分平均步数反映选型和 prompt 的健康度工具调用成功率反映 schema 质量和模型指令遵循能力。三个指标配成一个看板每次改提示词或换模型后跑一遍回归集数字下降就回滚。这是唯一防止“改了个提示词一周后才被用户骂”的手段。第三件事是灰度。Agent 系统比普通接口危险因为它会真实调用工具造成副作用。我一般先跑影子模式生产流量复制一份到 Agent但工具调用只记录不执行用真实流量验证它“会怎么做”。然后开小流量真实执行但只放行只读工具写操作仍然关闭。最后才逐步放开写权限并按周观察评估指标。每一步的回滚都应该是切换配置开关而不是重新部署代码。我这个习惯是拿一次事故换来的当年上一个数据导入 Agent没做灰度直接全量开放结果模型用错参数把线上配置表改了一遍列名全对但值全偏业务数据一晚上没法用。从那以后影子模式和逐步放量就进了我每一个项目的上线前检查单。工具调用日志、三个指标、灰度开关这三件事加起来代码量不大但对生产稳定性的提升比任何花哨的 Agent 模式都大。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站