给 Agent 接工具,看着是件体力活:写个函数、贴个 JSON Schema、注册进去,完事。
但真正跑起来之后你会发现,Agent 的绝大多数失败不是模型不会思考,而是工具没设计好。同一个模型,换一套工具定义,任务成功率能差出一大截。下面这七个决定,是我在实际运行中反复踩过才总结出来的。
一、粒度:一个工具只做一件事,但要能"一次做完"
最常见的错误是把后端 API 一比一搬成工具。后端有 12 个细碎的接口,就注册 12 个工具,结果模型要走完四步才能完成一次"修改标题"——中间任何一步失败,整个流程就断。
判断标准很简单:
问自己:用户会把它当作一件事吗?
- "读取文件某几行"和"写入文件"是两件事 → 两个工具;
- "创建文章"应该包含标题、正文、分类、标签 → 一个工具,不要拆成四个。
拆得太碎,步数暴涨,出错概率是相乘的;合得太粗,参数多到模型记不住。折中点是:以"一次有意义的副作用"为单位。
二、参数必须扁平
这条看起来是废话,但它是实际失败率最高的一项。
模型很容易把参数包成一个信封:
{"name": "http_request", "args": {"method": "GET", "url": "..."}}
而工具期望的是扁平的 {"method": ..., "url": ...}。校验器只能报一句"未知参数 ‘args’"。
有意思的是,这类错误高度集中在联网类工具上——因为模型对"发请求"的直觉结构就是嵌套的。
解法有两层:
- 设计上别嵌套。凡是能用扁平参数表达的,就不要用嵌套对象。
- 容错上要统一。在入口做一次解包,但条件必须严格,尤其是"当前工具的 schema 里没有真的叫
name/args的参数"这一条——否则会把一个专门做转发调用的桥接工具弄坏。
三、报错要能"教会"模型
错误信息是给谁看的?是给模型看的,不是给日志看的。
对比两句话:
- ❌
参数校验失败 - ✅
未知参数 'shell_command',本工具的正确参数是 'command'
后者模型下一轮就能自己改对,前者只会换来同样的错误重试三次。
报错里带上正确用法(真实列名、允许的参数清单、格式示例),是一次投入、长期收益的改动。
四、工具数量要收敛,且别中途变动
工具一多,选择困难就会出现:模型在两个功能相近的工具之间来回摇摆。
三条经验:
- 命名要有明确前缀,让同类工具在列表里聚在一起(如
kb_*/graph_*); - 描述里写清"什么时候用它、什么时候别用",比写清"它做了什么"更有价值;
- 不要在回合中途动态增删工具。这会破坏上下文前缀的稳定性,拉低缓存命中率,还会让模型的工具选择行为变得不可预测。要变,就在回合边界变。
五、写操作必须能安全重试
Agent 会重试。网络抖动、超时、模型判断失误,都会让它再调一次同一个工具。
所以:
- 读操作天然幂等,随便重试;
- 写操作要么设计成幂等(同一个
request_id只生效一次),要么在描述里明确警告"重复调用会产生多条记录"。
一个实用的做法是分级:读 / 写 / 危险。危险操作(删除、发送、支付)应该需要额外确认,而不是和读操作混在一个权限等级里。
六、返回值是为"下一步"服务的
工具返回什么,直接决定模型下一步能不能走对。
- 别返回全文。返回一个定位用的指针(文件路径 + 行号、记录 ID、URL)比返回几千字正文有用得多——前者模型能再去取,后者只会挤爆上下文。
- 失败要给出可操作信息,“哪个文件、第几行、期望什么、实际什么”,而不是一句
failed。 - 保留失败痕迹。后续压缩上下文时,成功输出的可以丢,错误信息别丢——那是模型避免重蹈覆辙的唯一线索。
七、名称和描述是提示词的一部分
很多人把工具描述当文档写,其实是当成提示词在写。
一段好的描述通常包含四件事:
- 它做什么(一句话);
- 输入输出的关键约束(长度、格式、范围);
- 什么时候该用;
- 什么时候不该用(防止误用,比如"不要用它读取二进制文件")。
一个真实的失败样本
某次运行里,联网工具连续 5 次调用中有 3 次失败,报的都是"未知参数 ‘args’"。查下来全是同一个信封问题。
第一反应是"模型太笨"。但冷静看:是工具没做入口容错,也没在报错里告诉模型正确形状。修完之后,同类错误基本消失。
这件事的教训不是"要加容错",而是:
凡是"模型反复犯同一个错"的地方,先怀疑工具设计,再怀疑模型能力。
结语
Agent 的工具层,本质上是给模型设计一套"好用的人机接口"——只不过这里的"人"是一个对参数格式记得很糊、会重试、会被长文本淹死的智能体。
把粒度、扁平、报错、数量、幂等、返回值、描述这七件事做对,你会发现模型的能力被释放出来一大截——不是因为它变聪明了,而是因为它终于不用跟你的接口打架了。
本文基于一个有状态 Agent 运行时的工具层实测经验整理,文中失败样本来自真实运行记录。