Agentic 系统的三个调优杠杆:工具设计、SKILL.md 与迭代方法
以前调 AI,就是改提示词。改完跑一遍,看看对不对,再改。
有了工具之后,这个模型不够用了。
我给 agent 配了 bash 权限和十几个专用工具,提示词改了很多版,它还是在某些地方做错。不是提示词的问题——是我在用错误的心智模型调一个已经不一样的系统。
Agentic 系统有三个调优杠杆。提示词是其中一个,但不是最关键的那个。
杠杆一:工具设计——工具给能力,不给判断力
工具回答的是:我能做什么? 技能文件回答的是:我该怎么做?
举个具体的例子。
我的系统里有一个 Creative Director agent,负责编排整个多媒体小说的生产流程。它有这些工具:
chrome:浏览器操作(含定时触发)file_system:查找和读取文件image_generate:生成图片file_request:申请文件message/rest/screenshot/shell/shell_command/spawn:消息、API 调用、截图、命令行执行、创建 subagent
工具本身很简单。但是 Creative Director 需要做的事远不止"调用工具":
- 启动时先读 PROJECT.md,理解项目背景
- 用两级 TODO.md 追踪任务进度(项目级 → 子任务级)
- 决定先召唤 Story Planner 还是 Writer
- 如果 Writer 输出质量差,最多重跑 2 次,然后升级给用户
这些行为没有一条写在工具的定义里。它们写在 creative-director/SKILL.md 里。
工具是能力边界,SKILL.md 是决策框架。
消除歧义——每个工具只做一件事
这个教训,OpenAI 内部数据 agent 的实践里有直接的记录:
"Early on, we exposed our full tool set to the agent, and quickly ran into problems with overlapping functionality... it's confusing to agents. To reduce ambiguity and improve reliability, we restricted and consolidated certain tool calls."
— Bonnie Xu, Aravind Suresh, Emma Tang,Inside our in-house data agent,OpenAI
他们的问题不是工具太少,而是工具太多、功能重叠——agent 不知道该用哪个。
当两个工具能做同一件事时,agent 每次都要做一次选择题,而它并不总是选对。
比如你同时有 web_fetch 和 web_ingest,都能抓网页内容。如果没有明确区分使用场景,agent 会随机选一个,行为变得不可预测。
解法不是限制 agent 的自由,而是减少歧义:
- 合并功能重叠的工具,或在 SKILL.md 里明确写清楚"场景 A 用工具 X,场景 B 用工具 Y"
- 给每个工具一个清晰的、唯一的职责
同样的道理也适用于 SKILL.md 本身。OpenAI 发现,过于精细的步骤指令反而让 agent 走错路——因为现实中每个问题的细节都不一样,死板的脚本套不上去。更好的方式是:告诉 agent 要达成什么目标,让它自己选路径。
工具要清晰,目标要清晰;但不要替 agent 做执行层的决策。
杠杆二:SKILL.md——给 agent 判断规则,不是执行脚本
一个好的技能文件,不是工具的使用说明书,而是情境判断指南。
格式参考(来自我的系统):
注意:这里没有一行代码。全是人类可读的判断规则。agent 读这份文件,就知道"遇到这种情况该怎么做"。
SKILL.md 是怎么加载的——理解它,才能用好它
一个容易忽略的问题:SKILL.md 是什么时候被 agent 读到的?
参考 Claude Code 的设计:SKILL.md 不是"预加载进系统"的,而是在组装 context 的时候才 attach 进去——每次 LLM 调用前,把对应的 SKILL.md 追加到 context 末尾。
这个设计的逻辑是:
- 避免重复计算(只在需要的时候才 attach)
- 保持 SKILL.md 的优先级(它出现在 context 末尾,离当前任务最近,模型更倾向遵从)
对你自己构建 agent 的实际影响:
- 改了 SKILL.md,下次调用就生效,不需要重启 agent。
- context 组装顺序影响模型行为。 如果 SKILL.md 被放在 context 前面而不是末尾,模型可能被后面更长的对话历史覆盖。
- 不要把 SKILL.md 写得太长。 它是每次都 attach 的——越长,每次调用的 token 消耗越大。只写"决策规则",不写"背景介绍"。
SKILL.md 必须包含的四个部分
一个完整的 SKILL.md,要回答这四个问题:
1. 如何判断(触发条件) 每个工具调用、每个分支,都要有明确的触发条件。不要让 agent 猜。
2. 如何循环(执行节奏) 需要重复执行的任务,写清楚循环的每一步。例如:每次生成一章 → 检查质量 → 如果不达标就重新生成。
3. 什么时候终止(退出条件) 这是最容易漏写的。常见的终止条件:
- 任务完成(TODO.md 里所有 item 都 check off)
- 质量达标(review 通过了)
- 重试超限(最多重跑 N 次,超了就上报)
- 预算耗尽(token 剩余 < 5%,停止)
- 用户中止(收到用户的中断信号)
没有终止条件的循环,等于给 agent 一个无限运行的权限。
4. 边缘情况(异常处理) 每一个没有被 SKILL.md 覆盖的边缘情况,都是 agent 会"自行发挥"的地方:工具调用失败了怎么办、用户给了模糊反馈怎么办、subagent 返回空结果怎么办。
SKILL.md 最常见的三个写法错误
有了 SKILL.md 的概念还不够。更常见的问题是:你写了,但 agent 还是不按你说的做。
1. 写了目标,没写决策点
"帮我生成一批图,然后发布到各平台"——这是目标,不是指令。Agent 不知道:生成多少张?发布前要不要等我确认?各平台格式一样吗?
决策点就是 agent 在执行过程中需要做选择的地方。每一个模糊的决策点,都是一个潜在的出错位置。
2. 没写"停下来"的触发条件
Human-in-the-loop 的节点,不会自动发生。你必须明确写:
"完成图片生成后,把结果展示给用户,等用户确认后再继续下一步。"
测试过的结论:这类确认节点,只要在 SKILL.md 里写清楚,Agent 是能执行的。这不是问题所在。
3. 边缘情况没有覆盖
用户说"我不喜欢这张图"时,agent 应该重新生成,还是从剩余结果里选一张?没有覆盖的边缘情况,agent 就会猜——猜错了,你再改一次。
正确的写法格式:触发条件 → 执行动作 → 预期输出(或停止条件)
| 写法 | 效果 | | --- | --- | | "生成图片,然后发布" | Agent 自行决定每一步,行为不可预测 | | "生成图片 → 展示给用户 → 等待 approve → 生成各平台文案(格式见下)→ 发布" | 每一步的触发条件和预期输出都明确 |
最重要的发现:Agent 能执行,不能判断
到这里,三个杠杆的机制都清楚了。但实际跑过工作流之后,会碰到一个更深层的问题。
不是所有的"判断"都能用规则表达。
视频剪辑流程是最典型的例子。工具没问题,FFmpeg 能用,问题在于:
- Agent 看不到画面,没法判断素材质量。 N 段素材参差不齐,但 Agent 没有办法在剪辑前做选片决策。
- 隐性经验无法直接翻译成规则。 同一场景拍了两条,选哪条?这个判断背后是"表情自然不自然"、"台词气口对不对"——这些感受很难、也可能根本不应该被穷举成条文。
- 版本和差异的处理更复杂。 老版本和新版本素材能不能拼?人在不同时间的表现差异如何处理?这些问题 Agent 没有上下文,也没有判断能力。
这揭示了一个根本性限制:Agent 是语言模型,它处理的是 token,不是画面或音频。对于需要视觉、听觉理解,或需要大量隐性经验才能做出的判断,Agent 无能为力。这部分必须先由人完成。
这不只是当前模型的能力边界问题——它意味着 SKILL.md 能发挥作用,有一个前提:人先把隐性判断外显化。正确的工作流顺序是:
以视频剪辑为例,"隐性判断"转化成"显性规则"长这样:
| 人类判断(隐性) | 转化成规则(显性) | | --- | --- | | "这条表现比较好" | "选取无停顿、无 NG 的素材;同一场景多条时优先选最后一条" | | "老素材不能跟新素材直接拼" | "同一场景新旧版本不混用;先检查文件时间戳,优先使用最新录制" | | "这个节奏感觉差一点" | "每段时长不超过 45 秒;对话节奏参考脚本时间码" |
转化的质量决定 Agent 执行的质量。
这也重新定义了 SKILL.md 是什么:它不只是"规则文件",而是你在某个领域的隐性知识被系统化的结果。隐性经验越多的任务,写 SKILL.md 之前需要做的人类理解工作就越多。
杠杆三:迭代
当 agent 做错了,大多数人第一反应是改提示词——改完再跑,希望这次对了。
这个方法的问题,在于"不知道哪里错了就开始改"。Agent 做出错误决策,发生在某一步的某个判断节点上。你改了整段提示词,也许修了那个点,也许没有,你不知道。
更有效的方法是先定位,再改。读 session log,找到 agent 做出错误判断的那一轮,看它当时的 reasoning:它认为自己在做什么,为什么走了这条路。找到之后,只改覆盖那个决策点的规则,其他不动。再跑一次,确认那个点修好了。
这样每次迭代都有明确的目标——"我修了第 3 步的触发条件",而不是"我再改改看"。
这个方法的前提是:log 得记对。
我最早用的是很常见的方案:role | content | timestamp | tool_used,一条回复一行。用来查记录够用,但调 agent 的时候不够——你看到的是 agent 的"回复",但 agent 出错的地方往往在某一步的决策:它看到了 tool_result 之后怎么理解的,为什么选了 tool A 而不是 tool B。这些信息,按回复粒度记录是看不到的。
后来参考 Claude Code 的日志设计,改成了追加式事件流——每一步单独记录,一次完整的执行看起来是这样的:
这样可以看到 agent 在每一步的推理和选择,而不只是最终输出。
我觉得里面最关键的两个设计。一个是 uuid + parentUuid:每个事件有自己的 ID,同时指向它的父事件。这样每个事件之间的因果关系就清楚了——这步的决策,是哪条 tool_result 触发的,上下文从哪来的。
另一个是 tool_use_id ↔ tool_result.tool_use_id 配对:
把工具调用和返回结果绑在一起。有了这个,才能分析某个工具的稳定性、排查某次调用失败的原因,不然只有孤立的两条记录。
如果你也在设计自己的 agent log,下面是我目前在用的最小结构,供参考:
比常见的 role|content|timestamp|tool_used,多的核心字段只有三个:parent_event_id、tool_call_id、content_blocks。加上这三个,log 就从"查记录"变成了"可以复现决策过程的轨迹"。
总结:三个调优杠杆的检查清单
让 agent 按意图执行任务,上线前检查这三件事:
工具设计
- 每个工具有唯一职责,没有功能重叠
- 如果有相似工具,SKILL.md 里明确写了各自的使用场景
SKILL.md 设计
- 在写规则之前,先把你的隐性经验外显化
- 每个决策点都有对应规则(遇到 X → 做 Y)
- human-in-the-loop 节点有显式的"等待用户确认"指令
- 循环有明确的终止条件
- 边缘情况有覆盖(拒绝/报错/模糊反馈时怎么处理)
迭代方式
- 出错时先读 session log 定位决策点,再改对应规则
- 不要凭感觉改整个提示词
还没解决的问题
三个杠杆的逻辑说清楚了,但有两个问题还没有答案。
一、工具怎么设计?
我下一步想构建一个能在桌面直接操作的 Agent。这触发了一个更基础的问题:工具集的边界在哪里?
参照 OpenAI 和其他 Agent 框架的做法,最精简的工具集可以压缩到四个原子操作:读文件、写文件、命令行(Shell)、搜索(Search)。这四个组合起来,理论上能覆盖大多数任务。
但"在其他系统里如何按这个逻辑设计工具"——桌面操作、Web 自动化、跨平台——目前还没有清晰的决策框架。工具粒度的问题,在不同系统下有不同的答案,还在摸索。
二、SKILL.md 怎么数据驱动地调优?
现在的迭代方式是:读 log → 人工定位决策点 → 改规则。每一步都依赖人。
更理想的方向:
- 哪个 Skill 在哪类任务下效果差 → 数据驱动发现
- 发现问题 → 自动生成修改建议,甚至直接改
关键问题是:Agent 能不能评估自己的执行质量?能不能把"我这步做错了"翻译成"SKILL.md 第 3 条触发条件需要修改"?
目前的边界是:任务边界清晰的场景,部分可以自动化;需要主观判断的场景,不能。
这也是文章核心结论的延伸——不能自动化的部分,依然要靠人先判断、再编码成规则。只要这个环节还在,SKILL.md 的迭代就不可能完全交出去。



