
前两天一位读者给我发了段 AI Agent 的 Demo很兴奋地说他能让模型自己调用搜索工具、写文件、做表格了。结果我问了一句你跑过几个任务他沉默了。换一个任务就崩中间步骤出错没人发现模型甚至会无视工具返回自己编答案。这个场景我这两年看了太多次AI Agent 的工程实现难点从来不在接上大模型 API而在把一个一次性的 LLM 调用变成一个有规划、有记忆、有反馈、有终止条件的闭环系统。我习惯用一套框架来拆解 Agent 的工程实现就是标题里说的七要素和七个决策点。七要素帮你把一个 Agent 到底由哪些部分组成想清楚七个决策点是落地时每个部分都必须做的关键选择从用什么循环范式到怎么判断它是不是真的完成了环环相扣。这篇文章就把这套框架完整展开适合正在搭 Agent、或者搭完发现不稳定的同学参考也适合准备做 Agent 产品化的人用来做技术选型。1. 先把七要素过一遍Agent运转的工程骨架1.1 Agent是感知—规划—行动—观察的循环不是一次对话很多人对 Agent 的理解停留在能调用工具的 ChatGPT。这个理解不能说错但它缺少一个关键视角Agent 的本质是循环。一次对话只有一轮问答Agent 会在思考—调用工具—看结果—再思考这个圈里转很多圈直到拿到可验证的结果或到达终止条件。打个比方把 Agent 比作一个刚入职的实习生大模型是实习生的大脑负责想问题工具是电脑和办公软件负责干活记忆是工作笔记负责记住前面做了啥观测反馈是领导给的回应负责告诉实习生这次干得对不对循环控制是项目经理负责喊停或调整方向。如果只给实习生一个大脑和一堆软件却不给他反馈回路他就不知道错在哪也不知道什么时候该停。1.2 七要素清单与工程对应关系不同资料对 Agent 要素的分法不太一样有的说四大要素有的说五大组件。我这里用工程落地视角把它拆成七块每一块都对应一个具体的系统模块要素核心职责工程落地形态模型推理基于当前信息和目标做决策、输出思考过程LLM 接入层、Prompt 编排、思维链规划把目标拆成步骤、决定下一步动作Planner 模块、任务队列、策略选择记忆保存上下文和跨会话经验消息列表、摘要、向量检索工具与外部世界交互的能力Function Schema、工具注册中心行动执行发起工具调用、处理参数Executor、API 网关、并发控制观测反馈拿到工具结果并判断是否符合预期结果解析、校验、异常处理循环控制决定继续、重试还是终止步数上限、早停、重试策略这张表的排序是按一个 Agent 实际跑起来的时间顺序来的先有推理再做规划查记忆选工具执行动作观察结果然后循环控制决定要不要再来一轮。任何一个环节缺失Agent 都会以某种方式半残——只是很多项目缺了某个环节后只在特定任务上暴露出问题所以没被及时发现。1.3 为什么不是四大要素而是七要素很多人提到 Agent 必说记忆、规划、工具、行动四大要素这没错但在工程上只讲四要素是不够用的。模型推理和观测反馈往往是决定成败的隐藏变量模型推理的 Prompt 写得不好模型每一步都在瞎猜观测反馈缺失工具已经返回错误了模型还接着往下跑。循环控制则是所有基础设施里最容易被忽略的——不加max_steps的 Agent跑偏之后能烧掉你一整天的 token 额度。所以我把七要素当作一张工程检核表。每做一个 Agent 项目就对着表格过一遍这块有没有谁负责如果不加会出什么问题下面的七个决策点就是这张检核表落到代码里最关键的七个岔路口从循环范式、模型选型到上下文记忆、工具协议、规划策略再到终止容错和评估观测每一个都需要明确拍板。2. 决策点①循环范式设计先想清楚Agent怎么转2.1 三种循环范式从简单到复杂决策点①回答的问题给定任务之后Agent 内部用什么结构运转最基础的范式是单次调用模型拿到用户问题和工具列表一次性给出回答。适合简单问答但严格来说这不叫 Agent因为没有任何环境交互工具也没有真正被用起来。第二种是ReActReasoning Acting循环。模型输出一段思考然后输出一个动作比如调用工具系统执行动作后把观察结果放回上下文再让模型接着思考。这是目前最流行的范式适合大部分工具调用场景。它的核心思想是边想边做每一步推理都基于上一步的实际观察而不是预先编好的剧本。第三种是Plan-then-Execute。模型先输出一份完整计划把任务拆成若干子步骤然后按计划逐步执行。这种范式适合步骤多、依赖关系复杂的任务因为先规划可以避免模型走一步看一步导致的路径发散。选择策略并不复杂任务步骤数小于 3 且工具简单直接用 ReAct步骤多、依赖关系复杂用 Plan-then-Execute任务结果需要反复推理验证可以在 ReAct 基础上加反思Reflexion。别一上来就上重型框架plan 本身也会消耗 token 和延迟很多业务场景根本撑不住那套复杂编排。2.2 ReAct循环的最小可运行骨架ReAct 的工程实现可以很简单。下面这个骨架是我常用的起点去掉所有业务细节之后核心逻辑只有十行左右MAX_STEPS 10 system_prompt 你是一个可以调用工具完成任务的AI助手。 请严格按照下面的格式输出 思考对当前任务的简要分析 动作工具名(参数) 如果没有更多工具需要调用直接输出最终回答xxx def run_agent(task, tools, llm): messages [ {role: system, content: system_prompt}, {role: user, content: task} ] for step in range(MAX_STEPS): response llm.chat(messages) action parse_action(response) # 解析动作返回None则代表最终回答 if action is None: return response result execute_tool(action, tools) # 调用工具并捕获异常 messages.append({role: assistant, content: response}) messages.append({role: user, content: f工具返回{result}}) return 超过最大步数任务终止这里面最关键的不是代码量而是工具返回被当作用户消息放回上下文这个动作。它让模型能够看到自己行动的后果然后继续推理。很多初学者会在这里犯错工具调用结果只存到变量里没有拼回消息列表模型到下一步就失忆了这属于七要素里的反馈回路没接通。3. 决策点②推理模型选型Function Calling是硬门槛3.1 为什么模型选型决定Agent的天花板同一个 Agent 框架换一个模型效果可能天差地别。原因是 Agent 对模型有三重考验第一模型能不能根据工具描述生成符合规范的工具调用参数。很多通用对话模型能理解工具意图但输出的参数格式乱七八糟得靠正则和修复脚本去捞极其脆弱。第二模型在长上下文里能不能保持一致性。Agent 每跑一步上下文都在变长如果模型在 20k token 后就迷糊那任务超过五步基本就废了。第三模型愿不愿意在收到工具返回后改变自己的结论。很多小模型会无视工具返回坚持输出自己原本编造的内容这是最可怕的——工具已经明确说查无此人模型还在煞有介事地介绍这个人的背景。3.2 模型能力对比与选型建议拿强 Function Calling 模型和通用对话模型对比一下能力维度强 Function Calling 模型通用对话模型工具调用输出直接输出结构化 JSON经常输出自然语言需强解析长上下文稳定性较好靠窗口截断勉强维持对工具结果的遵从度高低容易无视返回继续编成本高低适合场景生产级 Agent原型验证我的建议是先跑通再换。别在选型阶段纠结哪个模型最好先用一个主流模型把框架跑起来后面用同一套评估数据对应决策点⑦去对比替换。选型还要算成本账Agent 的 token 消耗远高于普通对话任务循环会放大模型成本。一个 20 步的 Agent 任务可能要烧掉 5 到 10 万 token模型单价差几倍总成本会被循环放大到完全不同的数量级。等你把架构跑顺了、评估指标有了再回来做性价比优化才是正路。4. 决策点③上下文预算与记忆分层决定任务能做多深4.1 先用公式估算成本Agent 的上下文管理是工程实现里最容易踩坑的部分而且踩坑的后果很隐蔽——不是直接报错而是任务做到一半模型开始胡说八道。先把账算清楚单轮 Agent 调用开销 系统提示词 工具定义 历史消息 模型输出。假设系统部分 3000 token每轮思考 调用输出约 1000 token工具返回约 1500 token。如果完全不做处理、保留全部历史第 10 步的模型输入 ≈ 3000 (1000 1500) × 9 25500 token累计 10 步的输入 ≈ 3000 × 10 2500 × 45 142500 token。也就是说一个 10 步的简单任务输入侧累计就能突破十四万 token比一次普通对话高出两个数量级。这不是危言耸听而是每个做 Agent 的人迟早要撞上的硬约束。上下文窗口有限、成本有限所以上下文管理不是可选项而是第一优先级。4.2 记忆分层的工程形态面对这个约束我把记忆分成三层短期记忆最近几轮消息完整保留。这是模型推理的主要依据丢失了它模型立刻失忆。长期记忆把较早的历史压缩成摘要或者写入向量库需要时召回相关片段。这是跨会话经验存储的地方。工作记忆当前子任务的中间状态比如规划里的 JSON 变量、任务目标、已完成步骤清单单独存放避免一股脑塞进对话 History。分层之后最常用的是滚动摘要 最近完整保留策略。核心逻辑是这样的def summarize_and_roll(history, summary, keep_last4, summarize_fnNone): history: 完整消息列表 summary: 上一次压缩后的历史摘要 keep_last: 需要完整保留的最近轮次数 if len(history) keep_last * 2 1: return summary, history old history[:-(keep_last * 2)] # 需要压缩的旧消息 recent history[-(keep_last * 2):] # 最近消息完整保留 new_summary summarize_fn( f已有摘要{summary}\n新内容{\n.join(m[content] for m in old)} ) return new_summary, recent这个函数每次在上下文快要溢出时调用把旧消息压缩成一段摘要最近的对话保持原样。它的优点是实现成本低缺点是摘要会丢掉细节。实战中任务的核心目标、已完成的步骤清单、关键中间产物这三类信息要永远保留宁可从别的地方省 token也不能丢这三样。4.3 检索召回的取舍再进一步就是向量库召回。任务做到后面Agent 可能需要回忆很多步之前的一个数据靠自然语言摘要早就糊了这时就要从向量库里按相关性把原片段捞回来。但检索召回有几个现实问题召回结果质量取决于 Embedding 模型和分块方式分块太大没用太小语义割裂召回条数控制不好还会污染上下文——本来是来帮忙的结果塞进一堆无关片段模型更晕。我的经验是召回条数控制在 3-5 条每条长度控制在 300 token 以内并且召回内容要标注来源时间点让模型知道哪段历史对应哪一步。长期任务的上下文预算分配大致可以参考这个比例上下文项目预算占比系统提示词 工具定义20%-30%最近的对话历史20%-30%检索或摘要的历史10%-20%当前任务、规划、工作记忆剩余空间5. 决策点④工具协议设计Agent的手脚必须可控5.1 工具描述怎么写模型才看得懂工具协议简单说就是告诉模型你有什么工具、工具能干什么、参数是什么。工程上最通用的是用 JSON Schema 描述。这里有个反直觉的点给模型看的工具描述和给你同事看的 API 文档完全是两回事。API 文档追求准确工具描述追求让模型在正确时机用正确参数调用。一个搜索引擎工具的 Schema 示例{ name: web_search, description: 在互联网上搜索公开信息返回标题和摘要列表。当用户需要最新资讯、未知事实或需要核实信息时使用。, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词尽量具体一次只搜索一个主题 }, max_results: { type: integer, default: 5, minimum: 1, maximum: 10 } }, required: [query] } }关键是 description 里要写清楚触发条件——什么时候该用这个工具。很多工具描述只写搜索互联网模型不知道该在什么场景下调用结果该搜的时候不搜不该搜的时候瞎搜。参数描述也要写取值范围和默认值减少模型生成非法参数的概率。5.2 工具返回要结构化错误也是信息工具返回值我强烈建议做成结构化的 JSON统一包含status、data、error三个字段。这样模型每次看到的反馈格式都是一致的容易形成稳定的推理模式。更重要的细节是错误处理。工具调用失败时不要把原始异常堆栈直接塞给模型。模型看到一堆ConnectionError、TimeoutError的英文堆栈根本不知道该做什么。正确的做法是把异常整理成错误摘要 可能原因 建议例如原始异常是HTTP 503转成{ status: error, error: 服务暂时不可用可能是限流或后端过载, suggestion: 等待后重试或更换一个搜索关键词 }这样模型就能把错误反馈当成下一步决策的输入重试、换参数、还是换工具。这一步不做好Agent 第一次碰到异常就会开始编答案。5.3 工具协议三个高频坑工具太多Schema 压垮上下文。接了 30 个工具之后光工具描述就能吃掉小一万 token。解法是按任务动态加载只把当前任务可能用到的工具放进系统提示词其他放工具注册中心备用。枚举值说不清模型反复生成非法参数。解法是把可选项写进 Schema 并给 default还不行就在工具执行层做参数校验非法参数直接返回参数不合法可选值为 xx让模型重新生成。工具执行超时没有兜底。大多数工具调用是外部 HTTP 请求不设超时会卡住整个循环。每个工具调用都必须设置超时超时也算一次正常观察把超时了这个信息返回给模型让它自己决定是重试还是换路。6. 决策点⑤规划策略复杂任务要先想再做不硬闯6.1 什么时候需要规划不是所有任务都需要规划。如果任务只调一次工具就能完成硬上 Plan-then-Execute 纯粹是浪费 token 和延迟。我的判断标准是任务包含多个互相依赖的步骤、需要多个工具协作、或者需要对中间结果做条件分支时才考虑规划。3 个步骤以内的任务直接 ReAct超过 3 步且步骤顺序影响结果再上规划。还有一种隐性的信号如果同一个 ReAct 循环里模型反复在里面绕圈子、做了大量无用动作才到达终点那说明任务本身就值得先规划一下而不是让模型走一步看一步。6.2 Plan-then-Execute 落地细节Plan-then-Execute 的核心是两步先生成计划再执行计划。生成计划时Prompt 要明确要求模型只输出计划不要执行任何操作并且按 JSON 数组输出方便程序校验[ { step: 1, goal: 获取最新文章列表, tool: fetch_articles, success: 返回状态为ok且包含文章数组 }, { step: 2, goal: 按发布时间排序并输出摘要, tool: summarize, success: 输出包含3条文章摘要 } ]计划生成后脚本要做一次合法性校验比如检查计划的工具名是否真实存在、必填参数是否有值。这一步很多人省略结果模型生成一个调用不存在工具的计划执行阶段第一轮就崩。执行阶段的关键是把计划列表放进消息上下文每完成一步就在计划上标记完成情况。模型根据已完成标记决定下一步是继续、修正计划还是收尾。注意计划不是不可变的——执行中遇到工具返回和预期不符要允许模型修改计划。很多实现失败就是因为执行器死板地按计划走一步出错就整个卡死。6.3 失败后的反思机制对于结果需要反复验证的任务我强烈建议在 ReAct 或 Plan-then-Execute 之上加一层反思Reflexion。机制很简单任务失败后让模型对比预期结果和实际结果写一段反思文字指出失败原因和下次要改什么然后把这端反思放进下一轮尝试的上下文。比如一个写爬虫 Agent 失败后反思可能是我上一轮忘了先检查响应状态码就直接解析内容导致拿到空数据。下一步要先打印 status_code 并判断是否为 200。这种反思对模型的修正效果比单纯把错误返回给它要好得多因为它是模型自己产出的、用自己语言表达的结论。7. 决策点⑥和⑦终止容错与评估观测这是上线前的两道关7.1 终止条件max_steps不是万能药循环控制里最简单的实现是设一个MAX_STEPS我一般设 15 左右。但如果模型连续生成非法 JSON、parse 失败、或者反复输出同样的话它会在这个上限内原地打转把预算烧光最后还给你一个超过最大步数的失败。所以除了硬上限还要有早停机制。两种方式结合第一模型判断任务完成时输出明确的最终回答标记系统识别后结束第二工具结果里出现明确的任务完成信号时系统直接终止不等模型自己宣布。第二种尤其重要因为模型容易过早宣布完成——它觉得差不多行了但系统侧校验发现最终答复缺了任务要求的必要字段比如没有给出具体的检索结果列表。系统侧校验不过就退回继续循环这是防止模型偷懒的有效手段。7.2 容错降级三层设计容错设计我习惯分三层第一层输出解析。模型输出的工具调用 JSON 不合法时先做修复——比如提取被 Markdown 代码块包裹的 JSON、补上缺失的右括号。修复不了就把输出格式不合法请严格按 JSON 输出工具调用作为反馈返回让它重新生成。第二层工具异常。照第 5 章说的把异常转成结构化错误反馈模型基于错误反馈重新决策。这一层已经能覆盖大部分偶发故障。第三层全局降级。连续失败超过 3 次后不再重试直接进入安全答复——比如告知用户暂时无法完成任务、返回手上已有的部分结果。这一步是为了止损避免一个注定失败的任务把一天的 token 额度烧完。还有一个部署层面的提醒如果你的 Agent 跑在 Django 这类 Web 框架后面工具调用务必走异步任务队列别把一个十几步的长循环直接塞进请求线程一个任务卡住整个 Web 服务的线程池就堵了。编排层的实现语言也会影响吞吐我见过不少团队用 Python 做原型、用 Rust 或 Go 重写执行层来扛并发这是一条很常见的工程路径但前提是先把上面的逻辑跑通。7.3 评估与可观测性最后是评估和可观测性这是我判断一个 Agent 能不能上线的底线。先回答三个问题任务成功率是多少一次成功平均花了多少步、多少钱失败的任务挂在哪一步没有这三个数字你在优化 Agent 时就是在盲调。所以从第一天就要做两件事一是记录完整 trace每个 step 的模型输入、模型输出、工具调用、工具返回、耗时、token 数全部落日志二是准备一批固定的评估任务至少 30 个能代表真实业务场景的任务每次修改 Prompt、换模型、调参数之后都跑同一批数据按成功率、平均步数、平均成本三个指标对比。基于 trace 回放你会很快发现问题的分布是模型不理解工具描述还是工具返回格式太乱还是终止条件太松导致跑飞。有了数据支撑前面的六个决策点才能逐一定位调优。还是回到开头那位读者。我把这套框架丢给他之后他把自己的 Demo 按七要素过了一遍发现最缺的既不是模型也不是工具而是观测反馈和循环控制——工具返回了他没解析模型编错了没人发现循环也没有终止条件最后当然一跑就崩。我做 Agent 工程这两年最大的体会就是大多数Agent 不稳定的问题都能在七要素和七个决策点里找到原因。现在你让我评估一个新框架我只问两个问题它能不能清楚记录每个 step 的 token 消耗和失败位置它遇到异常会怎么降级这两个问题答不上来再炫的功能我也不敢上生产。