1. 从“七要素”到“七个决策点”:为什么 Agent 工程化需要一套拆解框架
1.1 一个真实场景引出的问题
去年下半年我接手了一个内部知识库问答助手的改造项目。最初的需求听起来很简单:让模型能回答公司内部文档相关的问题。我当时的想法也很朴素——把文档切片、做向量检索、拼进提示词、调模型生成答案,一套标准的 RAG 流程走完就收工。上线第一周效果确实还行,但第二周开始,运营同事反馈的问题就变得五花八门:有人问“帮我查一下上季度的报销政策,顺便算一下我这种情况能报多少”,模型检索到了政策文档,却不会做那一步计算;有人问“这个流程走完之后下一步找谁”,模型答得头头是道,但引用的文档其实是两年前的旧版本;还有人连续追问三轮之后,模型把第一轮说过的结论完全忘了。
这些问题单独看都不难,但放在一起就暴露了一个本质:我做的那个东西,严格来说只是一个“检索增强的问答接口”,而不是一个 Agent。它没有决策能力,没有工具使用能力,没有记忆管理,也没有对自身行为的校验。后来我把这套系统推倒重做,引入了工具调用、状态管理和循环控制,才真正把它变成了一个能处理多步任务的智能体。
这段经历让我意识到一件事:Agent 的工程实现,难点从来不在“让模型说话”,而在于“让模型在正确的时机做正确的决策”。而要把这件事讲清楚,光靠“Agent = LLM + 记忆 + 工具 + 规划”这种公式是不够的,因为公式只告诉你组件有哪些,没告诉你这些组件在运行时是怎么协作的、每个环节要做哪些取舍。
1.2 七要素:Agent 的静态解剖
业内比较通用的一个拆解方式,是把 Agent 的构成归纳为七个要素。我把它整理成下面这张表,方便对照理解:
| 要素 | 作用 | 缺失后的典型症状 |
|---|---|---|
| 大模型(LLM) | 推理与决策核心 | 系统退化为规则引擎,无法处理开放输入 |
| 提示词与角色设定 | 定义行为边界与输出格式 | 输出漂移,格式不稳定,角色混乱 |
| 记忆(短期/长期) | 维持上下文与跨会话知识 | 多轮对话失忆,无法积累经验 |
| 工具/函数调用 | 与外部世界交互 | 只能“说”,不能“做” |
| 规划与任务分解 | 把复杂目标拆成可执行步骤 | 面对多步任务直接卡死或乱答 |
| 执行循环 | 驱动“思考-行动-观察”反复进行 | 只能单轮响应,无法迭代逼近目标 |
| 校验与容错 | 检查结果、纠正偏差 | 错误一路传导,最终输出不可信 |
这七个要素里,前两个几乎所有做 LLM 应用的人都会碰,中间三个是区分“聊天机器人”和“Agent”的分水岭,最后两个则是决定一个 Agent 能不能上生产的关键。我见过太多项目,前五个要素做得像模像样,一到执行循环和容错就草草了事,结果 demo 惊艳、上线翻车。
1.3 七个决策点:Agent 的动态视角
但光有静态拆解还不够。真正写代码的时候你会发现,七要素是“名词”,而工程实现里你面对的全是“动词”——你要决定什么时候调工具、什么时候停止循环、记忆存什么丢什么、失败了重试几次。这些决策点才是代码里真正要写逻辑的地方。
我把它们归纳为七个决策点:
- 意图判定:这一轮输入到底是要闲聊、要检索、还是要执行一个多步任务?
- 工具选择:在当前上下文下,应该调用哪个工具、传什么参数?
- 循环控制:什么时候继续下一轮,什么时候终止?
- 记忆写入:哪些信息值得持久化,哪些用完即弃?
- 记忆读取:当前这一步需要召回哪些历史信息?
- 结果校验:工具返回或模型输出是否可信、是否需要重试?
- 失败降级:当重试也解决不了时,如何优雅地兜底?
七要素回答“系统由什么组成”,七个决策点回答“运行时怎么运转”。前者是架构图,后者是流程图。两者结合,才是一套能落地的工程认知。下面我就按这个框架,把每个环节的实现细节和踩坑经验展开讲。
2. 七要素的工程落地:每个组件到底该怎么写
2.1 大模型选型:不是越大越好,而是越“听话”越好
选模型这件事,新手最容易犯的错是唯参数论。我早期也这样,觉得 70B 一定比 7B 强,闭源旗舰一定比开源强。但实际做 Agent 的时候,模型的核心能力要求跟做纯对话完全不一样。
Agent 对模型的要求,排在第一位的不是知识量,而是指令遵循能力和结构化输出稳定性。因为 Agent 的每一步输出都要被程序解析——你要从模型输出里提取出“调用哪个工具、参数是什么”,如果模型时不时给你加一句“好的,我来帮你查一下”,解析就崩了。
我实测下来的经验是:一个 7B 到 14B 级别、经过函数调用专项微调的模型,在 Agent 场景下的表现,往往比一个没做过工具调用对齐的大模型更稳。原因很简单,前者被训练成“该输出 JSON 就输出 JSON”,后者还保留着强烈的对话惯性。
具体选型时我会看三个指标:
- 结构化输出成功率:连续跑 100 次工具调用请求,能正确解析的比例。低于 95% 的,基本不能上生产。
- 首 token 延迟:Agent 是多轮循环,每轮延迟会累加。单轮 2 秒和单轮 5 秒,跑五轮就是 10 秒和 25 秒的差距。
- 上下文窗口的实际可用性:标称 128K 不代表 128K 都好用,很多模型在超过 32K 之后注意力就明显涣散。我会用“大海捞针”测试实际有效窗口。
提示:不要用同一个模型干所有事。意图判定、工具选择这类结构化任务,用小模型;需要深度推理的规划任务,用大模型。这种“大小模型混用”的架构,成本和延迟都能降一大截。
2.2 提示词与角色设定:把“人设”写成“契约”
很多人写 Agent 的提示词,写的是角色描述:“你是一个专业的客服助手,要热情、耐心……”这种写法对聊天机器人够用,对 Agent 远远不够。Agent 的提示词本质上是一份行为契约,它要明确规定:你能做什么、不能做什么、输出什么格式、遇到什么情况走什么分支。
我习惯把 Agent 的系统提示词拆成五块:
- 身份与目标:一句话说清这个 Agent 存在的目的。
- 能力边界:明确列出可用工具,以及每个工具的适用场景。
- 输出格式规范:用 schema 或示例固定输出结构。
- 决策规则:什么情况下直接回答,什么情况下必须调工具,什么情况下要追问用户。
- 失败处理约定:工具报错时怎么表述,信息不足时怎么处理。
这里有个反直觉的经验:提示词里“不要做什么”比“要做什么”更重要。因为模型天生倾向于“帮忙”,你不明确禁止,它就会在信息不足时编造、在工具失败时假装成功。我通常会在提示词里硬性写几条禁令,比如“如果工具返回为空,必须如实告知,禁止基于猜测作答”。
2.3 记忆系统:短期靠上下文,长期靠检索
记忆这块,我踩过最大的坑是“什么都想记”。早期我设计了一个长期记忆模块,把每轮对话都摘要存进向量库,结果检索时召回一堆无关的旧信息,反而干扰了当前推理。
后来我调整了策略,把记忆分成两层:
- 短期记忆:就是当前会话的上下文窗口。这里存的是原始对话和工具调用记录,不做摘要,保证信息无损。但要有窗口管理策略,超出长度就按“保留最近 N 轮 + 保留关键节点”的方式裁剪。
- 长期记忆:只存三类东西——用户明确表达的偏好、已经确认的事实结论、跨会话需要延续的任务状态。其他一律不存。
判断一条信息该不该进长期记忆,我用一个简单的标准:这条信息在下一个会话里被用到的概率,是否大于它造成干扰的概率。如果拿不准,就不存。宁可少记,不可乱记。
2.4 工具调用:接口设计比模型能力更影响成功率
工具调用失败,很多人第一反应是“模型不行”。但我排查下来,相当一部分失败其实是工具接口设计的问题。
举个例子,我做过一个查询订单状态的工具,参数是order_id。模型经常传进来一个带前缀的字符串,比如 “订单号:12345”,导致查询失败。后来我在工具描述里明确写了参数格式,并且在服务端做了一层清洗,成功率立刻从 80% 出头涨到 98%。
工具设计我总结了几条原则:
- 参数尽量少而明确:能用一个参数说清就别用三个,能枚举就别用自由文本。
- 工具描述要写“什么时候用”:不只是写功能,还要写适用场景,帮模型做选择。
- 返回值要结构化且精简:返回一大坨原始数据,既占上下文又干扰推理,最好在工具层就做裁剪。
- 错误信息要可读:返回“参数 order_id 格式错误,应为纯数字”比返回一个 500 错误码有用得多,模型能据此自我纠正。
2.5 规划与任务分解:让模型“先想后做”
规划能力是 Agent 处理复杂任务的关键。我的做法是在正式执行前加一个规划阶段:让模型先把任务拆成步骤列表,再逐步执行。
但这里有个陷阱——不是所有任务都值得规划。简单任务强行规划,反而增加延迟和出错概率。我的判断标准是:如果这个任务需要调用两次以上工具,或者步骤之间有依赖关系,就值得规划;否则直接执行。
规划的输出我要求是结构化的步骤列表,每步包含“目标”和“预期使用的工具”。执行过程中,如果某一步的结果与预期偏差较大,允许回到规划阶段重新拆解。这种“规划-执行-再规划”的弹性,比一次性规划到底要稳得多。
2.6 执行循环:Agent 的心脏
执行循环是 Agent 区别于单轮问答的核心机制。它的基本形态是一个 while 循环:模型思考 → 决定行动 → 执行工具 → 观察结果 → 判断是否继续。
这个循环看起来简单,但工程上有几个必须处理的点:
- 最大轮数限制:一定要设上限,否则模型可能陷入死循环。我一般设 8 到 10 轮。
- 循环终止条件:不只是“模型说完成了”,还要有程序侧的判断,比如“连续两轮没有新的工具调用”就终止。
- 每轮的状态记录:把每轮的思考、行动、观察都记下来,既用于下一轮上下文,也用于事后排查。
2.7 校验与容错:决定能不能上生产
这是最容易被忽视、但最重要的一环。校验分两层:输出格式校验和内容合理性校验。
格式校验好做,用 schema 验证就行。内容校验难一些,我的做法是引入一个轻量的“裁判”环节——用另一个模型调用(或者同一个模型的不同提示词)来判断当前结果是否回答了原始问题、是否与已知事实矛盾。这就是业内常说的 LLM as judge 思路。
容错则要设计重试和降级策略。我的默认配置是:工具调用失败重试 2 次,每次调整参数或换工具;重试仍失败则降级为“告知用户当前无法完成,并说明原因”。永远不要让 Agent 在失败时假装成功,这是信任的底线。
3. 七个决策点的实现细节:代码里真正要写逻辑的地方
3.1 意图判定:第一道分水岭
意图判定决定了后续走哪条路径。我的实现方式是用一次轻量模型调用做分类,把输入归到几个预定义类别:闲聊、知识问答、工具执行、多步任务、澄清追问。
这里的关键是分类粒度要适中。太粗,后续逻辑没法分支;太细,分类本身容易出错。我一般控制在 5 到 7 类。
还有一个实用技巧:把意图判定和路由合并。与其先分类再路由,不如让模型直接输出“应该走哪个处理函数”,减少一次信息转换的损耗。
3.2 工具选择:给模型一份“菜单”
工具选择本质是一个受限的选择题。我的做法是把所有可用工具整理成一份带描述的清单,放进提示词,让模型输出工具名和参数。
提升选择准确率的几个手段:
- 工具数量控制在 10 个以内:超过之后选择准确率明显下降,需要做分组或分层。
- 给每个工具写正例和反例:明确“这个工具适合什么、不适合什么”。
- 允许模型输出“无需工具”:否则它会硬凑一个工具来调用。
3.3 循环控制:什么时候该停
循环控制有两个方向:该继续时别停,该停时别继续。
该继续的典型场景:任务还没完成、还有未处理的子目标、上一轮工具返回了需要进一步处理的数据。
该停的场景:任务完成、达到最大轮数、连续无进展、遇到无法恢复的错误。
我实现时会给每轮打一个“进展分”,如果连续两轮进展分为零,就强制终止并返回当前最佳结果。这个机制救过我好几次,避免了无限循环烧 token。
3.4 记忆写入与读取:两个独立的决策
写入和读取要分开设计,这是很多人会混淆的地方。
写入决策:这一步产生了什么值得记住的信息?我的规则是——用户偏好、确认的事实、任务状态三类写入长期记忆,其余只留在短期上下文。
读取决策:当前这一步需要哪些历史信息?我的做法是基于当前输入做一次检索,召回相关记忆,但限制召回数量(一般 3 到 5 条),避免上下文被稀释。
3.5 结果校验:三道防线
我通常设三道校验:
- 格式校验:schema 验证,不通过直接重试。
- 一致性校验:与已知事实、前序结论比对,矛盾则标记。
- 相关性校验:判断结果是否真的回答了问题,用 judge 模型打分。
三道都过,才进入下一轮或返回给用户。
3.6 失败降级:优雅地认输
降级策略我分三级:
- 一级:重试,调整参数或换工具。
- 二级:换策略,比如从工具执行降级为知识问答。
- 三级:如实告知,说明卡在哪一步、需要用户提供什么。
第三级最重要,也最考验产品设计。一个好的失败提示,应该让用户知道“系统尽力了,但确实需要更多信息”,而不是一句冷冰冰的“抱歉,我无法处理”。
4. 常见问题与排查技巧实录
4.1 工具调用成功率低怎么办
先别急着换模型,按这个顺序排查:
| 排查项 | 检查方法 | 常见问题 |
|---|---|---|
| 工具描述 | 读一遍描述,是否说清了适用场景 | 描述太笼统,模型不知道何时用 |
| 参数格式 | 看失败案例的参数 | 格式不匹配,缺清洗层 |
| 工具数量 | 数一下当前可用工具 | 超过 10 个,选择困难 |
| 提示词 | 是否有明确的调用规则 | 缺少“何时必须调用”的约束 |
| 模型能力 | 换一个做过函数调用微调的模型对比 | 模型本身不擅长结构化输出 |
4.2 循环停不下来
这是最烧钱的问题。我的排查清单:
- 最大轮数是否设置?没设就是无限循环。
- 终止条件是否包含“无新工具调用”?只靠模型自述“完成”不可靠。
- 是否有进展检测?连续无进展必须强制终止。
- 工具是否返回了空结果导致模型反复重试?空结果要有明确处理。
4.3 多轮对话失忆
先确认是短期记忆还是长期记忆的问题。如果是同一会话内失忆,检查上下文裁剪策略是不是裁太狠了。如果是跨会话失忆,检查长期记忆的写入和召回是否正常工作。
我遇到过一个隐蔽的 bug:长期记忆写入了,但召回时用的相似度阈值太高,导致几乎召不回任何东西。把阈值调低之后立刻正常。
4.4 输出格式不稳定
这是结构化输出的经典问题。三个手段:一是用 schema 约束,二是给 few-shot 示例,三是加格式校验和重试。三者叠加,基本能压到 99% 以上。
注意:格式校验失败后的重试,要把失败原因一起喂回给模型,比如“上次输出缺少 tool_name 字段”,这样它才知道怎么改。
4.5 成本失控
Agent 的成本是乘法级的——多轮循环乘以每轮的模型调用。控制成本的核心是减少不必要的轮次和调用。我的几个做法:意图判定用小模型、简单任务不规划、工具返回精简、记忆召回限量、设置合理的最大轮数。
实测下来,这些优化叠加能把成本压到原来的三分之一左右,而效果几乎无损。
5. 一套可复用的 Agent 工程骨架
把上面的内容串起来,我给出一套我常用的工程骨架,你可以直接照着搭:
- 入口层:接收输入,做意图判定和路由。
- 规划层:判断是否需要规划,需要则生成步骤列表。
- 执行层:循环执行“模型思考-工具调用-结果观察”。
- 记忆层:负责短期上下文管理和长期记忆读写。
- 校验层:格式、一致性、相关性三道校验。
- 降级层:重试、换策略、如实告知三级降级。
每一层都可以独立替换和优化,层与层之间通过明确的数据结构通信。这样设计的好处是,当某个环节出问题时,你能快速定位是哪一层的责任,而不是在一坨耦合代码里大海捞针。
我在实际项目里用这套骨架改造过好几个系统,从知识库问答到自动化流程处理,适配性都还不错。核心不在于骨架本身多精妙,而在于它强迫你把每个决策点都想清楚——Agent 工程的本质,就是把一堆模糊的“让模型自己搞定”,翻译成一个个明确的、可测试的决策逻辑。这件事没有捷径,但一旦想通了,后面就是体力活了。