
简介《Agentic Design Patterns: A Hands-On Guide to Building Intelligent Systems》是一本系统讲解智能体设计模式的英文PDF电子书目标读者为具备人工智能、机器学习或软件工程基础的研发人员、技术负责人与AI产品经理。全书覆盖从基础概念到高级架构的完整体系重点围绕并行化执行、顺序编排、任务优先级排序、短期与长期记忆管理、人机协同、RAG知识检索增强、多智能体协作、评估监控和推理引擎机制等核心主题结合Google ADK、LangChain等框架的代码示例说明如何构建高效、可靠且可扩展的智能代理系统并强调高风险场景中的安全、透明与责任原则。资源仅含1个PDF文件大小17.56MB内容完整适合作为案头参考书反复查阅。它不只是概念梳理更提供可落地的设计模式与工程实践读者可参照示例代码搭建自己的Agent工作流理解Agent协作与验证机制从而提升LLM应用的自动化水平和可信度。目前已有570人学习/下载。1. 为什么Agentic Design Patterns会成为AI编程的必修课我在给一家公司做内部AI工具时发现同样的模型、同样的提示词单轮问答表现惊艳一旦放在多步骤的真实任务里就经常“卡壳”要么中途跑偏要么反复执行同一个动作要么上下文被填满后忘了最初的指令。问题不在模型不够聪明而在设计智能体时没有一个可以复用的结构。Agentic Design Patterns智能体设计模式就是解决这类问题的关键。这份实操指南把常见智能体拆成了几个清晰模式帮你把《Building Intelligent Systems》这件事从“靠感觉拼装”变为“按图纸施工”。它适合正在做AI编程的算法工程师、独立开发者以及需要在企业流程中落地大模型应用的架构师。了解这些模式后你会知道一个多步骤智能体应该有哪些构件、每个构件放在哪一层以及如何避免最常见的“翻车”点。2. 从反思到多智能体四种核心模式与选型依据智能体并不是单次模型调用的简单加和它需要一套编排逻辑。这一节先不写代码把几种最常用、也是本指南反复出现的模式讲清楚并给出选型时的判断依据。理解这些模式后后续章节的代码实现才不会变成“黑匣子”。2.1 反思Reflection模式让模型对自己的输出“找茬”反思模式的核心是让一个模型先产生答案再用同一个或另一路模型对该答案做批评最后由原模型根据批评意见修订。这个循环可以执行多轮直到批评没有新信息或达到设定次数。def reflect_and_revise(generate_fn, critique_fn, initial_input, max_iterations2): result generate_fn(initial_input) for i in range(max_iterations): feedback critique_fn(result, initial_input) if not feedback: break # 批评为空视为通过 result generate_fn(f{initial_input}\n\n请根据以下意见修改:\n{feedback}) print(f第{i1}轮修订完成) return result这段代码里generate_fn负责生成和修订critique_fn负责挑毛病。为什么要把批评包装成独立的critique_fn因为“自我检查”在最原始的提示词编辑模式下经常失效模型倾向于认为自己写得完美。独立函数的好处是你可以让更强的一个模型来负责评审也可以在feedback为空时提前终止避免无意义的循环。选型时如果任务对数据格式和事实准确性要求高比如生成SQL、医疗建议、法律文书反思模式是首选。它的缺点也很明显每次迭代都会消耗双倍的token且可能陷入“越改越差”的怪圈因此在实践时需要限制迭代次数后面第5章会讨论这个坑。2.2 工具使用Tool Use模式让LLM调用外部函数解决非文本问题很多任务不能只靠模型生成文本来解决。例如计算汇率、查询当前时间、运行一段Python代码或者从企业API拉取订单数据。工具使用模式的核心是在调用大模型时为其注册一组工具函数模型在生成过程中决定要不要调用工具以及用什么参数调用。tools [ {type: function, function: {name: get_weather, description: 查询城市天气, parameters: {type: object, properties: {city: {type: string}}}}} ] response openai.ChatCompletion.create( modelmodel_name, messageschat_history, toolstools, tool_choiceauto )tool_choiceauto表示让模型自主判断是否调用工具如果设为none则会禁用工具调用。参数tools的schema一定不要写得含糊模型靠description来决定工具是否适用描述越具体调用越准确。工具使用模式最大的价值是把大模型从“百科全书”变成“指挥官”它负责拆解问题、选择工具而把精确计算交给外部系统。适合需要实时数据、企业内部API、代码执行的场景。要注意的是模型返回工具调用结果后必须将工具返回内容拼接到上下文再发给模型否则模型不知道上一步执行的情况从第4章的代码里可以清楚看到这一点。2.3 规划Planning模式把大任务分解为可执行序列规划模式解决的痛点是复杂任务一次生成太容易超出上下文限制。它让模型先把任务拆解为几个子任务再逐个执行最后汇总结果。常见有Plan-and-Execute和ReAct两种路径。Plan-and-Execute的做法是先一次性生成完整计划然后按顺序执行ReAct则是在生成时边推理边行动两者交替进行。plan_prompt 请把以下任务拆解为2-5个具体步骤每一步尽量可验证。任务 plan planner(plan_prompt user_task) subtasks parse_steps(plan) final_output [] for step in subtasks: result executor(step) final_output.append(result)这里max_steps是一个关键参数。我在实际项目里一般限制子任务数量不超过5个因为拆得越细上下文和延迟的膨胀速度越快。规划模式适合“一个报告的生成”“竞品分析”“数据处理流水线”这类结构化任务。不是所有任务都需要规划。如果问答在两步以内能解决硬加规划只会增加失败点。判断标准是——任务的中间结果是否依赖前一步的输出依赖关系强规划模式才有价值。2.4 多智能体协作模式用角色分界降低单模型负担单个智能体承担的职责越多提示词就越容易互相污染。多智能体协作模式将不同能力拆分到多个“角色智能体”上比如一个规划者、一个执行者、一个审核者。每个智能体有自己的系统提示词和工具集并通过消息队列或共享状态协作。agents { planner: {prompt: 你负责拆解任务, tools: []}, coder: {prompt: 你负责写代码, tools: [python_execute]}, reviewer: {prompt: 你负责检查代码, tools: []} }这个方案最大的好处是模型上下文不再需要塞满所有指令。但通信成本也很高智能体之间的消息往往需要经过额外模型解析导致延迟和成本上升。一般用在角色差异明显、单模型提示词超过2000字的复杂场景。若任务简单强行造多个智能体只会增加内耗这是我踩过最深的坑之一。3. 动手复现一个最小反思智能体代码与参数调优这一章我们跟着指南实现一个最小可运行的反思智能体。它不依赖任何编排框架只用原生的OpenAI接口方便你理解循环背后的执行顺序。调通一遍后后续再加工具和状态图就很自然了。3.1 安装依赖与模型调用封装准备环境需要openai和python-dotenv建议使用Python 3.10以上版本。将API Key写入.env文件不要把密钥放进代码仓库。import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) def call_model(system_prompt, user_input, temperature0.3): resp client.chat.completions.create( modelos.getenv(MODEL_NAME, gpt-4o-mini), temperaturetemperature, messages[{role: system, content: system_prompt}, {role: user, content: user_input}] ) return resp.choices[0].message.contenttemperature在反思循环里建议在0.2到0.4之间太高的温度会导致生成结果飘忽太低的温度会让修订步骤缺少变化。模型名我习惯放在环境变量里因为不同模型在批评能力上差异很大——小参数模型做的批评经常是“文不对题”的套话。3.2 实现Critique-Revise循环下面这段代码完整实现反思循环。核心是两条提示词一条负责生成回答一条负责挑毛病。GENERATE_PROMPT 你是资深数据分析师请准确回答用户问题适当说明原因。 CRITIQUE_PROMPT 请检查下面回答是否存在事实错误、逻辑跳跃或表述歧义。只输出需要修改的点若没有问题输出空字符串。 def reflection_agent(question, max_rounds3, threshold20): answer call_model(GENERATE_PROMPT, question, temperature0.3) for i in range(max_rounds): feedback call_model(CRITIQUE_PROMPT, f回答:\n{answer}\n\n问题:\n{question}, temperature0.0) if len(feedback.strip()) threshold: print(f第{i1}轮通过无需修改) break revised call_model(GENERATE_PROMPT, f{question}\n\n之前的回答:\n{answer}\n\n请按照以下批评意见修改:\n{feedback}, temperature0.3) # 简单防止退化若修订内容太短则保留原答案 if len(revised.strip()) len(answer.strip()) * 0.5: print(修订异常终止循环) break answer revised return answer说明一下关键参数max_rounds3是循环上限threshold20用于判断“批评是否足够具体”。我设置阈值的原因是模型有时只是在说“这段回答不错”没有实质建议这时如果继续修订只会浪费tokens。temperature0.0用于批评步让评审尽量稳定。如果你希望批评更严格可以提高threshold但要注意过长批评本身也会生成很多无意义内容。我通常的做法是先打印每次的feedback观察两三条之后再定阈值。3.3 调参实验温度、轮次与答案质量的关系这一节建议你实际跑一组对比实验而不是只按一个参数跑到头。配置温度最大轮次典型结果保守0.01速度最快偶尔漏掉明显错误平衡0.33效果和成本折衷多数项目首选激进0.75结果不稳定token消耗约为保守配置的4-6倍我在观察结果时会额外记录每一步生成的长度变化。如果修订后长度明显变长往往是因为模型在“扩写”而不是“修改”。此时更好的做法是在提示词里强调“保持原答案结构只修改被点名的问题”。这算是一个隐藏参数指南里也把它作为重要调优项。还可以尝试另一种评估方法用第三个模型当裁判。对每次迭代后的答案打分但要注意裁判模型的偏好会干扰真实质量因此我一般只在有明确标准答案时才引入自动打分。3.4 把反思能力接到结构化输出上反思模式最强的地方不止于文本还可以用于JSON格式修复。下面的代码演示如何让反思循环把不合法JSON修复到可解析状态。import json def robust_json_generate(prompt, max_fix3): raw call_model(只输出JSON对象不要多余文字, prompt, temperature0.2) for _ in range(max_fix): try: return json.loads(raw) except json.JSONDecodeError as e: raw call_model(修复下面的JSON只输出修复后的JSON。错误信息 f{str(e)}\n原始内容\n{raw}, prompt, temperature0.0) raise ValueError(JSON修复失败)这个函数把“生成-校验-修复”做成了闭环是工具使用模式里常见的兜底逻辑。我把它放在第3章是因为理解反思的本质就是“用校验结果指导生成”JSON修复正是这种思路的最佳例子。4. 用LangGraph落地可扩展的工作流工具调用与条件分支当智能体要处理的状态变多需要记忆、条件跳转和工具调用时手写循环就难以为继。这时我一般用LangGraph来构建一个有状态图。它把节点Node和边Edge显式建模比徒手写while True好维护太多。4.1 状态定义与节点设计首先定义一个状态字典至少包含messages、tool_calls和当前意图。LangGraph会对状态做合并操作所以循环中的每一步结果都会写入状态。from typing import TypedDict, Annotated import operator class AgentState(TypedDict): messages: Annotated[list, operator.add] # 追加式合并 tool_calls: list next_step: str这里Annotated是LangGraph的关键它声明messages字段在每次更新后是追加而不是覆盖。如果不加这个标注新消息会替换掉历史消息导致模型丢失上下文。这个细节我第一次用的时候就栽过日志里看不到历史排查半天才发现。接着定义两个节点一个负责“理解意图”一个负责“执行工具”。def agent_node(state): response call_model(判断是否需要调用工具, state[messages]) state[tool_calls] extract_tool_calls(response) return state def tool_node(state): for call in state[tool_calls]: result execute_tool(call[name], call[args]) state[messages].append({role: tool, content: result}) return state把节点写成纯函数的好处是方便做单元测试给定输入状态断言返回值里的消息数量变化即可。4.2 工具注册与条件路由在LangGraph里节点之间的跳转要定义路由函数。下面这个路由比较简单如果检测到有工具调用就跳到tool_node否则跳到finish_node。def router(state): if state.get(tool_calls): return tool_node else: return finish_node from langgraph.graph import StateGraph graph StateGraph(AgentState) graph.add_node(agent, agent_node) graph.add_node(tool, tool_node) graph.add_node(finish, lambda state: state) graph.add_edge(agent, tool, conditionrouter)conditionrouter的意思是从agent节点出发时先执行router根据返回值决定去哪条边。这种写法比在节点内部硬编码if else要直观。执行时可以设置recursion_limit10这是LangGraph里防止死循环的最后一道闸门。4.3 工具定义与参数说明工具函数不需要和模型定义在一个文件里但返回值最好统一成纯文本避免模型看不懂嵌套结构。下面是一个execute_tool的简化实现。def execute_tool(name, args): if name search_order: return search_order_db(args[customer_id]) elif name calc_total: return str(sum(args[prices])) else: raise ValueError(fUnknown tool {name})这里我刻意把返回值转成字符串因为messages里塞进一个dict容易让后续模型解析出错。工具返回文本不要太长如果工具本身返回大量数据最好先做一次摘要否则上下文窗口会被撑爆。这是一条非常实用的经验。4.4 在状态图里建模反思循环第3节的反思循环也可以放到LangGraph中方式是构造一个包含generate_node、critique_node、judge_edge的图。判断条件写进router即可。def judge(state): if len(state[feedback]) 20: return finish if state[round] 3: return finish return generate把这个函数接入图就能替代原先的手写for循环。带来的收益是每一轮的状态都被保留可以追溯模型在第几轮做了什么修改。调用图时我经常把recursion_limit设为上限1因为超限会导致异常而异常信息并不直观。5. 避坑手册智能体稳定性的五个常见问题这一章是从实践中总结的踩坑记录。每条都按“现象 → 原因 → 解决”来写你可以直接当成排查清单使用。5.1 反思循环停不下来token被白白吃完现象max_rounds5时模型每一轮都提出新的批评但答案内容几乎没变化最终耗尽了预算。原因critique_fn的提示词没有限定“只输出关键差异”模型受到“多多益善”的默认倾向总是给模棱两可的建议。而generate_fn又很尊重批评每次都回复“已修改”实际只是换了个说法。解决在两个函数之间增加一个“变化检测”。我通常对比前后两版答案的编辑距离若编辑距离小于总长度的5%就判定本轮没有有效修改直接终止。同时把critique_fn的提示词改为“如果你认为回答完美必须输出空字符串不要生成溢美之词”。5.2 工具调用总是返回格式错误的JSON下游j解析崩溃现象模型生成arguments: {\city\: \上海\}但偶尔会在JSON前加一句“需要的参数如下”导致json.loads抛出异常。原因模型在调用工具时用的是对话补全而非严格的结构化输出它可能把指令里的示例文本混入参数。这个问题在使用强指令模型时很少见但一旦切换了模型版本就会随机爆发。解决不要直接用json.loads解析模型输出。先尝试从输入中截取最外层大括号再用ast.literal_eval做二次兜底。如果提取失败就把错误信息连同原始内容发给模型让其修复。这个“提取—修复—重试”的流程在类似场景下能救回80%以上的错误调用。5.3 规划出的子任务过多执行到一半上下文被截断现象让模型写一份竞品分析报告它一上来就拆出12个子任务执行到第8步时提示词里的历史信息被截断后半部分全部变成乱码。原因模型在规划时倾向于过度拆解而智能体框架不会自动清理历史消息。每次工具返回结果都往消息列表里追加积累几个长结果后必然超过上下文窗口。解决把max_steps设置为一个显式参数并限制为4到6个。同时在每轮执行后只保留“当前轮次的消息摘要”而不是完整回放。长文档类的任务我会在子任务完成后用另一个模型压缩结果再拼接到最终上下文。5.4 多智能体之间互相“客气”通信轮数激增但毫无进展现象协调者和执行者之间来回发送“我同意你的观点”“请继续执行”之类的消息最后输出里到处都是重复礼貌用语却没有真正完成功能。原因多智能体协作依赖消息来决定是否终止但模型没有意识到“无新信息”时就该停止。每个智能体都害怕自己显得不积极因此倾向于回复一些低信息量的内容。解决在通信协议里设置两个硬性约束每个agent的回复必须少于80个token超过会被截断全局最大通信轮数设为3超时后强制输出当前结论。实践下来这个限制反而让agent们学会在一轮内把关键信息说清。5.5 模型版本升级导致行为漂移之前调好的参数失效现象某个工具调用功能上周稳定本周升级API默认模型后工具名称错误率上升反思循环判定阈值为20时连续误报。原因不同模型对工具选择、JSON生成的规律并不一致而代码里没有锁定模型版本导致“上一版本的经验参数”不再匹配。解决所有调用代码里必须显式写明model字段不使用默认值。如果业务允许还应该在每次升级模型后重新跑一遍小样本回归测试下面第6章会介绍具体做法。6. 进阶用可观测性和小样本验证集给智能体上保险智能体项目上线后最怕的不是没有功能而是不知道它为什么失败。只靠客户端报错日志很难复现问题因为大模型的行为概率性强。下面这个习惯是我几次线上告警后总结出来的“后悔药”。6.1 为每个节点记录结构化日志不要用print调试因为多智能体并发时根本对不上。每条日志至少包含run_id、节点名、输入摘要、输出摘要、token数、耗时。用装饰器实现最简单。import logging, time, uuid def log_node(func): def wrapper(state): run_id str(uuid.uuid4()) start time.time() result func(state) duration time.time() - start logging.getLogger(agent).info( run_id%s node%s input_tokens%d output_tokens%d duration%.2fs, run_id, func.__name__, result.get(input_tokens), result.get(output_tokens), duration ) return result return wrapper有了run_id后即使多智能体并行也能把某一轮的所有步骤从日志里串起来。排查时我习惯按run_id搜日志只查看工具返回和最终结果命中率远高于看单条错误栈。6.2 为关键路径建立最小回归测试集这里不需要几百条样本只需要一条“关键路径”比如“查询用户订单并汇总金额”。用一个脚本回放已知输入断言是否调用了正确的工具、返回金额是否匹配。def test_order_summary(): state initial_state({customer_id: 007}) graph build_agent_graph() result graph.invoke(state, config{recursion_limit: 8}) assert tool_calls in result assert search_order in result[tool_calls] assert 1050 in result[final_message]测试脚本把模型视为外部依赖但用固定的run_id来控制随机性确保可复现。模型温度设为0。我每周跑一次这份回归自从模型升级后不稳定这个测试为我争取了很多处理时间。6.3 设置资源边界token预算与超时熔断在调用大模型时我给每个节点都加上max_tokens和timeout全局再加一层token_budget。超过预算果断终止。config { timeout: 30, max_tokens_per_node: 1000, total_budget: 8000 } def invoke_with_budget(graph, state): total 0 for event in graph.stream(state): usage event.get(usage, {}) total usage.get(total_tokens, 0) if total config[total_budget]: raise RuntimeError(fToken超限 {total}) return event这种熔断机制能防止一次失控循环吃掉整个账户余额。从那以后我每上线一个智能体都会强制走一遍“日志完备性—回归测试—预算熔断”三件事再配合第5章的排查清单智能体项目才真正从“演示能跑”变成“生产能用”。希望这套经验能帮你在构建智能系统时少走几段弯路。本文还有配套的精品资源点击获取