
1. 为什么“七要素”和“七个决策点”才是 Agent 工程的骨架很多人第一次接触 AI Agent脑子里蹦出来的画面是科幻电影里那种能自己思考、自己行动的智能体。但真到了动手写代码的阶段你会发现事情完全不是那么回事。你写了一个循环让大模型反复调用工具结果它要么陷入死循环要么在第三步就忘了第一步的目标要么调用了一个根本不存在的函数还理直气壮地编造结果。这些问题不是模型不够聪明而是工程实现上缺了骨架。我做了几个 Agent 项目之后踩了一圈坑回过头来发现一个规律所有能稳定跑起来的 Agent底层都逃不开七个核心要素和七个关键决策点。七要素是“一个 Agent 由哪些零件组成”七个决策点是“这些零件在运行时怎么做选择”。前者是静态结构后者是动态逻辑。把这两件事想清楚了不管你用 LangChain、CrewAI、Dify 还是自己从零手写都不会迷路。这篇文章适合谁看如果你已经用过大模型 API知道什么是 prompt、什么是 function calling但一提到“搭一个 Agent”就觉得无从下手那这篇就是写给你的。如果你已经在做 Agent 开发但系统总是不稳定、行为不可预测那这篇也能帮你找到结构性的原因。我会尽量用大白话把每个要素和决策点讲透配上可以直接参考的实现思路和参数选择逻辑让你看完就能动手改自己的项目。核心关键词先摆出来AI Agent、LLM、工具调用、循环机制、Agent 架构。这几个词贯穿全文后面每个章节都会围绕它们展开。2. 七要素拆解一个 Agent 到底由什么组成2.1 要素一LLM 作为推理内核但不是全部很多人把 Agent 等同于“大模型加个循环”这个理解只对了三分之一。LLM 在 Agent 里的角色是推理内核负责理解目标、拆解任务、决定下一步动作。但它不是 Agent 的全部甚至不是最脆弱的部分。选 LLM 做 Agent 内核时有几个参数必须关注。第一是上下文窗口因为它决定了 Agent 能“记住”多少历史信息。一个多轮工具调用的 Agent每轮都会往上下文里塞工具返回结果窗口太小的话跑到第五轮就开始丢前面的信息。第二是函数调用能力不是所有模型都支持结构化的 tool use有些模型只能输出文本你得自己解析这就多了一层不确定性。第三是推理延迟Agent 是循环执行的单次延迟乘以循环次数就是总耗时一个每次调用要 8 秒的模型跑五轮就是 40 秒用户体验直接崩掉。我个人的经验是Agent 场景下模型的选择优先级是“函数调用稳定性 上下文窗口 推理能力 延迟”。原因很简单Agent 的每一步都依赖上一步的输出格式正确如果模型经常输出不合法的工具调用格式后面全盘皆输。推理能力弱一点可以靠 prompt 补格式不稳定是补不回来的。2.2 要素二工具集Agent 的手和脚工具就是 Agent 能调用的外部函数。查天气、搜网页、读数据库、发邮件、执行代码这些都是工具。工具集的设计直接决定了 Agent 的能力边界。这里有个常见的误区新手总想给 Agent 塞尽可能多的工具觉得工具越多越强大。实际情况恰恰相反。工具数量超过一定阈值后模型的工具选择准确率会明显下降。我实测下来单次暴露给模型的工具控制在 5 到 15 个之间比较稳妥超过 20 个之后选错工具的概率大幅上升。那工具多了怎么办两个办法。一是分组暴露根据当前任务阶段只暴露相关工具比如“信息收集阶段”只给搜索类工具“执行阶段”只给操作类工具。二是工具路由先用一次轻量调用判断该用哪类工具再在子集里做精确选择。这两种方式本质上都是在降低单次决策的复杂度。每个工具的定义也有讲究。工具描述要写清楚三件事什么时候用、参数是什么意思、返回什么格式。我见过太多工具描述只写了一句“搜索信息”模型根本不知道什么时候该用它、什么时候不该用。好的工具描述应该像一份迷你说明书让模型看完就知道该不该调用。2.3 要素三记忆系统不只是存聊天记录Agent 的记忆分三层短期记忆、长期记忆、工作记忆。短期记忆就是当前对话的上下文长期记忆是跨会话持久化的信息工作记忆是当前任务执行过程中的中间状态。大部分人对记忆的理解停留在“把历史消息拼进 prompt”这只是短期记忆。真正让 Agent 表现得像“记得住事”的是工作记忆的设计。举个例子一个帮你订机票的 Agent它需要记住用户出发城市、目的城市、日期、预算、已查到的航班列表、用户偏好的航空公司。这些信息不应该散落在对话历史里而应该有一个结构化的任务状态对象每轮循环开始时注入 prompt循环结束后更新。实操心得工作记忆用 JSON 结构维护比纯文本历史可靠得多。模型对结构化字段的读取准确率明显高于从大段对话里“回忆”信息。长期记忆则涉及存储和检索。简单场景可以用文件或数据库存关键事实复杂场景需要向量检索。但要注意长期记忆的写入时机很关键不能每轮都写否则会存进去大量噪音。我的做法是任务结束时做一次总结性写入只存“用户偏好”和“任务结论”这两类信息。2.4 要素四规划模块把大目标拆成小步骤规划模块负责把用户的模糊需求拆解成可执行的步骤序列。比如用户说“帮我调研一下竞品”规划模块要把它拆成确定竞品范围、逐个搜索信息、整理对比维度、生成报告。规划有两种模式静态规划和动态规划。静态规划是一次性生成完整步骤列表然后按顺序执行。动态规划是每执行一步根据结果决定下一步做什么。静态规划适合流程固定的任务动态规划适合探索性任务。我倾向于混合模式先做一次粗粒度的静态规划生成 3 到 5 个高层步骤然后每个步骤内部用动态规划。这样既有全局方向感又有局部灵活性。纯静态规划遇到意外情况就卡死了纯动态规划容易跑偏或者绕圈子。规划模块的输出格式也很重要。我一般要求模型输出一个步骤列表每个步骤包含步骤描述、预期输入、预期输出、完成条件。完成条件这一项特别关键它让 Agent 知道什么时候该进入下一步而不是无限循环。2.5 要素五执行器真正干活的那一层执行器负责实际调用工具、解析返回结果、处理异常。这一层看起来简单其实是最容易出 bug 的地方。工具调用的返回结果格式五花八门有的是 JSON有的是纯文本有的是 HTML。执行器需要做结果归一化把不同格式的返回统一成模型能理解的格式。同时要处理超时和重试一个搜索工具超时了不能直接让整个 Agent 崩掉要有降级策略。还有一个容易被忽略的点工具返回结果的长度控制。有些工具返回的内容特别长直接塞进上下文会挤占空间。执行器需要做截断或摘要只保留和当前任务相关的部分。我一般会设置一个阈值超过 2000 字符的返回结果先做一次摘要再注入。2.6 要素六循环控制器Agent 的心跳循环控制器决定了 Agent 什么时候继续、什么时候停止。这是 Agent 工程里最核心也最容易出问题的部分。一个基本的循环是这样的观察当前状态、决定下一步动作、执行动作、更新状态、判断是否完成。听起来简单但实际运行时会遇到各种边界情况。比如模型一直调用同一个工具、模型输出格式错误导致无法解析、任务已经完成了但模型还在继续。循环控制器需要设置多重终止条件任务完成标志、最大循环次数、连续无进展检测、超时限制。这几个条件缺一不可。我见过只设最大循环次数的实现结果 Agent 在达到上限之前一直在做无用功白白消耗 token。2.7 要素七输出格式化让结果可用最后一个要素是输出格式化。Agent 跑完一圈最终要产出一个用户能用的结果。这个结果可能是自然语言回答、结构化数据、文件、或者一个操作确认。输出格式化模块要做的事情是把 Agent 执行过程中的中间状态和最终结果转换成符合预期的输出格式。如果用户要的是 JSON就不能返回一段散文如果用户要的是报告就要有清晰的结构。这里有个技巧在 Agent 循环的最后一步专门用一次模型调用来做格式化而不是让模型在循环过程中顺便输出。这样格式化的质量更稳定因为模型只需要专注做一件事。3. 七个决策点Agent 运行时到底在做什么选择3.1 决策点一下一步做什么——继续、调用工具还是结束这是 Agent 每一轮循环都要做的第一个决策。模型需要判断当前状态信息够不够需不需要调用工具任务是不是已经完成了这个决策的质量直接决定了 Agent 的效率。如果模型过于保守明明信息够了还要继续搜索就会浪费轮次如果过于激进信息不足就急着给答案结果质量就差。我的做法是在 prompt 里明确定义三种状态的判断标准。比如“如果已有信息能完整回答用户问题输出 FINISH如果需要外部信息输出 TOOL_CALL如果当前信息部分足够但需要补充输出 TOOL_CALL 并说明缺什么”。把判断标准写清楚模型的决策准确率会高很多。3.2 决策点二选哪个工具——工具选择的优先级逻辑当决定要调用工具后下一个决策是选哪个。前面说过工具数量要控制但即使只有 5 个工具模型也可能选错。工具选择的核心逻辑是匹配度排序。模型需要根据当前任务需求评估每个工具的适用程度。这里有个实用技巧在工具描述里加入使用场景标签比如“适用于实时信息查询”“不适用于历史数据”。这些标签能显著提升选择准确率。另一个技巧是工具调用示例。在 prompt 里给每个工具配一两个调用示例模型看到示例后更容易理解什么时候该用。这比纯文字描述有效得多。3.3 决策点三参数怎么填——从自然语言到结构化参数选好工具后要填参数。这是从自然语言到结构化数据的转换过程也是容易出错的地方。比如用户说“帮我查一下明天北京的天气”模型需要把它转换成{city: 北京, date: 2024-01-16}。日期格式、城市名称的规范化、可选参数的省略这些都需要模型做判断。提高参数填写准确率的方法有几个。一是参数描述要具体不要只写“日期”要写“日期格式 YYYY-MM-DD默认为今天”。二是提供参数示例让模型有参照。三是做参数校验执行器在调用工具前检查参数合法性不合法就返回错误让模型重新填。3.4 决策点四结果怎么解读——工具返回信息的理解与筛选工具返回结果后模型需要解读这些结果判断是否满足当前需求。这一步的难点在于信息筛选返回结果里可能有很多无关信息模型需要提取有用的部分。比如搜索工具返回了 10 条结果模型需要判断哪几条和当前任务相关。这里容易出现的问题是模型被无关信息带偏或者遗漏了关键信息。我的经验是在 prompt 里明确要求模型先总结工具返回结果再判断是否满足需求。这个“先总结再判断”的顺序很重要它强迫模型先理解再决策而不是直接跳到结论。3.5 决策点五状态怎么更新——任务进度的追踪与调整每轮循环结束后Agent 需要更新自己的状态已经完成了什么、还缺什么、下一步该做什么。这个决策点决定了 Agent 是否有“全局感”。状态更新最容易出的问题是信息丢失。比如第一轮查到了用户的城市第三轮需要用到这个信息时模型忘了。解决办法就是前面提到的结构化工作记忆每轮都显式地把关键状态注入 prompt。另一个问题是状态更新不及时。比如工具调用失败了但状态里还标记为“已完成”导致后续步骤基于错误假设执行。所以状态更新要和执行结果严格绑定成功才标记完成失败要记录失败原因。3.6 决策点六异常怎么处理——工具失败、格式错误、超时的应对异常处理是 Agent 工程里最容易被低估的部分。实际运行中工具调用失败、模型输出格式错误、网络超时这些都是常态。异常处理的核心原则是优雅降级不要崩溃。工具调用失败时Agent 应该能感知到失败并决定是重试、换工具、还是跳过这一步。模型输出格式错误时应该有一次重新格式化的机会而不是直接报错退出。我一般会设置三级异常处理第一级是自动重试适用于网络抖动等临时问题第二级是降级替代比如搜索工具失败就用缓存结果第三级是上报给模型让模型决定怎么处理。三级都处理不了才终止任务。3.7 决策点七何时停止——完成判断与终止条件最后一个决策点是判断任务是否完成。这个看似简单实际上很容易出问题。模型有时候会“过度完成”明明任务已经结束了还在继续调用工具有时候会“提前完成”信息还不够就给出答案。终止条件要设计成多条件与逻辑任务完成标志为真且没有待处理的子任务且输出格式符合要求才终止。任何一个条件不满足就继续循环。同时要有硬性上限比如最大 15 轮超过就强制终止并返回当前最佳结果。注意最大循环次数不要设得太高。我见过设 50 轮的结果 Agent 在死循环里烧掉大量 token。一般任务 10 到 15 轮足够复杂任务 20 轮也到顶了。4. 从零搭建一个最小可用 Agent 的完整实操4.1 环境准备与技术选型先说技术栈。如果你只是想理解 Agent 的原理用 Python 加任意一个支持函数调用的 LLM API 就够了。如果你想快速搭一个能用的系统LangChain 或 CrewAI 是不错的起点。如果你想要更细粒度的控制建议自己手写循环不依赖框架。我个人的选择是手写核心循环加轻量工具库。原因是我发现框架的抽象层在调试时反而增加难度出了问题不知道是框架的锅还是自己的锅。手写循环虽然代码多一些但每一行都在自己掌控中。环境依赖很简单Python 3.10 以上、一个 LLM API 的 SDK、requests 库用于调用外部工具。不需要数据库不需要向量库最小可用版本用内存和文件就够了。4.2 定义工具集与工具描述先定义三到五个基础工具。我一般从这几个开始搜索工具、计算工具、文件读写工具、HTTP 请求工具。每个工具用统一的格式定义tools [ { name: search, description: 搜索互联网获取实时信息。适用于查询新闻、天气、实时数据。不适用于数学计算、文件操作。, parameters: { query: {type: string, description: 搜索关键词尽量具体} } }, { name: calculate, description: 执行数学计算。适用于加减乘除、百分比、幂运算。不适用于需要外部数据的计算。, parameters: { expression: {type: string, description: 数学表达式如 2 3 * 4} } } ]工具描述里的“适用于”和“不适用于”是我强烈建议加的。实测下来加了这两项之后工具选择准确率能提升不少。4.3 构建主循环与状态管理主循环的结构大概是这样的def run_agent(user_input, max_turns15): state { goal: user_input, history: [], working_memory: {}, turn: 0, done: False } while not state[done] and state[turn] max_turns: state[turn] 1 # 构建 prompt prompt build_prompt(state, tools) # 调用 LLM response call_llm(prompt) # 解析输出 action parse_action(response) if action[type] FINISH: state[done] True state[result] action[content] elif action[type] TOOL_CALL: result execute_tool(action[name], action[params]) state[history].append({action: action, result: result}) update_working_memory(state, action, result) else: # 格式错误重试一次 state[history].append({error: invalid format}) return state.get(result, 任务未完成)这个结构看起来简单但每一部分都有细节。build_prompt要把工具描述、工作记忆、历史记录都拼进去同时控制总长度。parse_action要处理模型输出的各种格式变体。update_working_memory要判断哪些信息值得记住。4.4 参数计算与关键阈值选择几个关键参数需要根据实际情况调整。最大循环次数我一般设 15简单任务 5 到 8 轮就结束了复杂任务 12 到 15 轮。上下文长度上限根据模型窗口来定一般留 20% 的余量给输出。工具返回结果截断阈值设 2000 字符超过就摘要。重试次数设 2 次第一次失败后重试第二次失败就降级。超时时间单个工具调用设 30 秒整个任务设 5 分钟。这些数值不是固定的要根据你的具体场景调整。比如做实时对话的 Agent超时要设得更短做后台批处理的可以放宽。4.5 实操现场一个完整任务的执行记录假设用户输入是“帮我查一下今天北京的天气然后换算成华氏度”。第一轮模型判断需要调用搜索工具参数{query: 北京今天天气}。工具返回“北京今天晴气温 25 摄氏度”。工作记忆更新{weather_celsius: 25}。第二轮模型判断需要调用计算工具参数{expression: 25 * 9/5 32}。工具返回 77。工作记忆更新{weather_fahrenheit: 77}。第三轮模型判断信息足够输出 FINISH内容为“北京今天晴气温 25 摄氏度约合 77 华氏度”。整个过程三轮循环两次工具调用状态更新两次。这个流程跑通之后你就可以往里面加更多工具、更复杂的规划逻辑、更完善的异常处理。5. 常见问题与排查技巧实录5.1 工具调用死循环怎么破这是最常见的问题。Agent 反复调用同一个工具每次都得到相似结果但就是不结束。原因通常是模型没有正确判断“信息已经够了”。排查思路先看工作记忆里是否记录了已获取的信息。如果记录了但模型还是继续调用说明 prompt 里没有明确告诉模型“这些信息已经够了”。解决办法是在 prompt 里加一条规则“如果工作记忆中已包含回答用户问题所需的所有信息必须输出 FINISH”。另一个原因是工具返回结果没有变化模型以为没查到。这时候要在工具返回结果里加一个标记比如“已查询过结果同上”让模型知道这是重复查询。5.2 模型输出格式错误频发怎么办模型有时候不按你要求的 JSON 格式输出而是输出一段自然语言。这种情况在模型能力较弱时尤其常见。解决办法有三层。第一层是prompt 里给格式示例让模型照着抄。第二层是输出解析器做容错比如用正则提取 JSON 部分或者尝试修复常见的格式错误。第三层是格式错误时自动重试把错误信息返回给模型让它重新输出。我一般三层都上。实测下来加了格式示例之后错误率能降一半加上容错解析再降一半最后重试兜底基本不会因为格式问题卡死。5.3 上下文爆炸与信息丢失的平衡Agent 跑多轮之后上下文会越来越长。如果不控制很快就会超出模型窗口。但如果粗暴截断又会丢失关键信息。我的做法是分层管理上下文。工作记忆始终保留因为它是最关键的状态信息。历史记录只保留最近 5 轮更早的做摘要。工具返回结果只保留和当前任务相关的部分无关的丢弃。具体操作上每轮循环开始时先计算当前上下文长度如果超过阈值就对最早的历史记录做摘要压缩。摘要用一次轻量模型调用完成成本很低。5.4 常见问题速查表问题现象可能原因排查方法解决方案Agent 不调用工具工具描述不清晰检查工具描述是否说明使用场景补充“适用于/不适用于”标签反复调用同一工具未判断信息已足够查看工作记忆是否记录结果prompt 加完成判断规则输出格式错误模型能力不足检查是否有格式示例加示例容错解析重试上下文超限历史记录太长统计每轮 token 消耗分层管理摘要压缩任务提前结束完成条件太宽松检查终止条件逻辑改为多条件与逻辑工具调用超时外部服务不稳定查看工具调用日志设超时重试降级5.5 独家避坑技巧第一个技巧在工具返回结果里加“置信度”标记。比如搜索结果标注“高相关/低相关”让模型知道哪些结果更可信。这个简单的标记能显著提升模型的信息筛选质量。第二个技巧给 Agent 设一个“思考预算”。在 prompt 里告诉模型“你最多有 15 轮机会请高效使用”。模型知道有预算限制后会更倾向于在信息足够时及时结束而不是无限探索。第三个技巧用“反向验证”提升完成判断准确率。在 Agent 准备输出 FINISH 之前加一步验证“请检查你的答案是否完整回答了用户的原始问题”。这一步能拦住不少提前完成的情况。第四个技巧日志要记录每一轮的完整输入输出。Agent 出问题时光看最终结果根本不知道哪一步错了。把每轮的 prompt、模型输出、工具返回都记下来排查效率能提升好几倍。6. 进阶方向从能跑到好用还差什么6.1 多 Agent 协作的适用场景单 Agent 能解决大部分任务但有些场景适合多 Agent。比如一个负责调研、一个负责写作、一个负责审核。多 Agent 的优势是每个 Agent 的职责更聚焦prompt 可以更精简工具集可以更小。但多 Agent 也带来新的问题Agent 之间怎么通信、怎么协调、怎么避免互相等待。我的建议是不要过早引入多 Agent。先把单 Agent 跑稳确认瓶颈确实在“职责太多”而不是其他原因再考虑拆分。6.2 工具调用的安全边界Agent 能调用工具意味着它能对外部世界产生影响。发邮件、改数据库、执行代码这些操作一旦出错后果可能很严重。安全边界的设计原则是最小权限加人工确认。Agent 只应该拥有完成当前任务所需的最小工具集。对于有副作用的操作比如发送、删除、修改应该加一道人工确认或者至少加一个“预览”步骤。注意永远不要给 Agent 无限制的代码执行权限。如果确实需要执行代码用沙箱环境限制可访问的资源。6.3 性能优化的几个方向Agent 的性能瓶颈通常在两个地方LLM 调用延迟和工具调用延迟。优化 LLM 延迟可以用更小的模型做简单决策只在关键步骤用大模型。优化工具延迟可以并行调用无依赖的工具或者做结果缓存。还有一个容易被忽略的优化点减少不必要的循环轮次。很多 Agent 之所以慢是因为做了太多无用功。通过优化 prompt 让模型更果断地做决策比优化单次调用速度效果更明显。6.4 评估与迭代怎么知道 Agent 变好了Agent 的评估比传统软件难因为它的输出不是确定性的。我的做法是建一个测试用例集包含 20 到 30 个典型任务每个任务有明确的成功标准。每次修改 prompt 或工具后跑一遍测试集看成功率的变化。除了成功率还要关注平均循环轮次和平均 token 消耗。一个 Agent 成功率提升了但轮次翻倍未必是好事。好的优化应该是成功率和效率同时改善。我在实际项目中的体会是Agent 的迭代很像调参但比调参更依赖对任务本身的理解。你得知道这个任务“合理的执行路径”是什么样的才能判断 Agent 的行为是否正常。如果自己都不清楚一个任务应该几步完成那就没法评估 Agent 做得好不好。所以做 Agent 开发先把手动执行这个任务的流程走一遍记录下来这个记录就是你评估 Agent 的基准线。