给智能体写工具:Anthropic 的五条原则,和同一批数据两版工具的实测对比
自己写 MCP 服务器或给智能体加工具时,照着接口一个个包起来往往不是好办法。本文整理 Anthropic 工程博客的五条原则,并在同一批数据上做了两版工具:四道题都答对了,按任务设计的那版少调三分之二的工具,输入 token 不到三分之一。
本站已实测,测试日期:2026-10-16。
这篇解决一个问题:自己写 MCP 服务器、给 Agent SDK 或 Claude API 的智能体加工具时,工具该怎么设计。
最常见的做法是把现成的接口一个个包成工具:有 list_customers 接口就做一个 list_customers 工具。Anthropic 工程博客的《Writing effective tools for AI agents》说这往往不是好办法:工具的使用者是智能体,不是另一个程序,要按智能体的特点来设计。文章总结了五条原则,还讲了怎么用评测加 Claude Code 来改进工具。
本站拿同一批虚构的客服数据做了两版工具,一版照接口包一层,一版按文章的原则改,让 Claude 用两版各答四道题:
- 四道题两版都答对了。 这批数据小,模型也够强,照接口包的那版也能硬查出来。
- 代价差很多。 按任务设计的那版,工具调用从 26 次降到 8 次,输入 token 从约 5.8 万降到约 1.7 万,工具返回的文字从 5.7 万字降到 3 千字。
- 差距随数据量放大。 照接口包的那版有一道题要逐个客户拉订单,调了 17 次工具;客户多十倍,就要多十倍。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-16 |
| 模型 | Claude Sonnet 5.5(claude-sonnet-5-5) |
| 接口 | Claude API 的 Messages 接口,Python SDK,自己写的工具调用循环;通过酷站AI 中转站,不是直连官方 |
| 数据 | 随机生成的 16 个客户、86 笔订单、18 个工单,全是虚构的 |
| 次数 | 每道题每版各跑 1 次 |
工具和普通函数有什么不一样
文章开头的说法很好懂:普通的函数调用是两个确定的程序之间的约定,getWeather("NYC") 每次都会用同样的方式去查纽约的天气。工具是确定的程序和不确定的智能体之间的约定:用户问“今天要带伞吗”,智能体可能调天气工具,可能凭常识回答,也可能先反问在哪个城市;偶尔还会用错工具。
所以写工具不能照着给别的程序员写接口的方式来写。好在文章发现,对智能体顺手的工具,人用起来通常也顺手。
五条原则
一、选对要做的工具,不要照接口一个个包
智能体的上下文有限,电脑的内存却又便宜又多。从通讯录里找一个人,程序可以一条条比对;智能体要是调一个返回全部联系人的工具,就得一个字一个字读完,把有限的上下文浪费在无关的内容上。更自然的做法是直接翻到那一页。所以与其做 list_contacts,不如做 search_contacts。
工具可以把常一起用的几步合成一步。文章举的例子:
| 不要 | 改成 |
|---|---|
list_users、list_events、create_event |
一个 schedule_event,自己找空闲时间并约好 |
read_logs |
search_logs,只返回相关的几行和上下文 |
get_customer_by_id、list_transactions、list_notes |
一个 get_customer_context,一次给齐这个客户最近的相关信息 |
工具多了不一定好,功能重叠或者用途含糊的工具会让智能体分心。先针对几个影响最大的工作流做少数几个,再按评测结果加。
二、用前缀分组
智能体可能同时接着几十个 MCP 服务器、几百个工具,有些还是别人写的。按服务(asana_search、jira_search)或按资源(asana_projects_search、asana_users_search)加前缀,能帮它分清该用哪个。文章说前缀和后缀两种写法在评测里的效果差别不小,而且因模型而异,要用自己的评测来选。
三、返回有意义的内容
工具只返回高信号的信息,不要返回 uuid、256px_image_url、mime_type 这类底层标识;name、image_url、file_type 更能直接帮它决定下一步。把一串随机的 UUID 换成有意义的名字,哪怕只是从 0 开始的编号,都能明显提高 Claude 在检索任务里的准确度,减少编造。
有时智能体确实需要 ID 去调下一个工具。办法是给工具加一个 response_format 参数,让它自己选 concise(只给内容)还是 detailed(带上各种 ID)。文章的例子里,同一个 Slack 消息串,详细版 206 个 token,精简版 72 个,约三分之一。
返回的格式(XML、JSON、Markdown)也会影响效果,没有通用的最优解,要按自己的评测选。
四、控制返回的长度
可能返回很多内容的工具,都要有分页、范围、过滤或截断,并给合理的默认值。截断时告诉它怎么继续,比如“结果太多,请加过滤条件”。Claude Code 自己也有限制:按现在的文档,MCP 工具的返回超过 1 万 token 会提醒,默认最多 25,000 个 token,可以用环境变量 MAX_MCP_OUTPUT_TOKENS 调。
报错也一样:不要只回一个错误码或一段堆栈,要说清楚哪里不对、该怎么改。
五、把描述当提示词来写
工具的描述会被放进智能体的上下文,是改进工具最有效的办法之一。写的时候想象在给新同事讲这个工具:你脑子里默认的东西(查询的格式、行业术语、资源之间的关系)都要写出来。参数名要没有歧义:与其叫 user,不如叫 user_id。文章提到,Claude Sonnet 3.5 在 SWE-bench Verified 上拿到当时的最好成绩,靠的就是对工具描述做了精确的修改。
给 MCP 服务器写工具时,还可以用工具注解标明哪些工具会访问外部世界、哪些会做破坏性的修改。
本站实测:同一批数据,两版工具
两版工具
数据是一个虚构的客服后台:16 个客户,每个人有几笔订单和几个工单。
v1 照接口包一层,四个工具,描述只有一句英文,原样返回数据库里的记录:
| 工具 | 描述 | 返回 |
|---|---|---|
list_customers |
List customers. | 全部客户,每个带 UUID、手机号、头像地址、营销偏好等字段,共 6,188 字 |
list_orders |
List orders for a customer. | 这个客户的全部订单,带 UUID、支付方式 ID、来源等 |
list_tickets |
List support tickets for a customer. | 这个客户的全部工单 |
get_plan |
Get a plan. | 套餐详情 |
传错 ID 时只回一句 Error: 404。
v2 按任务设计,三个工具,描述写清楚什么时候用、返回什么:
| 工具 | 做什么 |
|---|---|
search_customers |
按名字或邮箱的一部分搜客户,返回 C07 林晓雯 邮箱 套餐 这样的一行 |
get_customer_context |
一次给齐一个客户的套餐、最近几个月按月汇总的订单、未解决的工单原文;response_format 选 detailed 时再加订单明细 |
search_orders |
按客户、状态、日期范围搜订单,可以不限客户;结果多时分页,末尾说明还有多少条、用什么 offset 看后面 |
客户编号用 C01 这样的短编号,金额直接写成 ¥99.00。找不到客户时,报错会给出相近的名字,并建议先用 search_customers 搜。get_customer_context 的返回长这样:
C01 林晓雯 lin.xw@example.com,套餐:基础版(¥29.00/月)
最近 3 个月(2026-07-17 起)的订单:
2026-08:refunded 1 笔 ¥99.00
2026-09:paid 3 笔 ¥457.00,refunded 2 笔 ¥144.00
2026-10:paid 1 笔 ¥29.00
未解决的工单:没有
四道题
- 客户林晓雯 2026 年 9 月有几笔退款,合计多少?
- 邮箱
xu.jy@example.com的客户用的是什么套餐,有没有未解决的工单? - 有没有客户被重复扣款(同一客户、同一天、金额相同、都已支付的两笔)?列出名字。
- 王建国提交了取消请求:他为什么要走,最近三个月实际付了多少钱?
标准答案是脚本从数据里直接算出来的。第 3 题我们埋了两起重复扣款,随机生成的数据里碰巧还多出一起,所以答案是三个人。
结果
| 题 | v1 工具调用 | v1 输入 token | v2 工具调用 | v2 输入 token | 两版都答对 |
|---|---|---|---|---|---|
| 1 | 3 | 11,066 | 2 | 3,747 | 是 |
| 2 | 3 | 9,871 | 1 | 2,432 | 是 |
| 3 | 17 | 26,027 | 2 | 7,003 | 是 |
| 4 | 3 | 10,958 | 3 | 4,196 | 是 |
| 合计 | 26 | 57,922 | 8 | 17,378 |
输入 token 是这道题所有请求加起来的。几处值得看:
- 第 3 题最能说明问题。 v1 只能先拉全部客户,再逐个拉订单,16 个客户的
list_orders是在一轮里并行发出的,所以总共 17 次调用。v2 用search_orders按“已支付”过滤,翻了两页就找齐了。两版都找到了三个人,v1 还多说了一句“罗佳琪那两笔隔了 6 小时,可能是两次正常购买,建议核实”。 - v1 每道题第一步都是拉全部客户。 每次 6,188 字,不管问的是谁。客户多到几千个,这一步本身就装不进上下文了。
- 工具返回的文字差得更多。 四道题加起来,v1 的工具返回了 57,102 字,v2 只有 3,028 字。输入 token 的差距没那么大,是因为每次请求都还带着系统提示、工具定义和对话本身。
- 耗时没有稳定的规律。 第 3 题 v1 17.1 秒、v2 8.8 秒;第 2 题反过来,v1 9.0 秒、v2 24.6 秒。每道题只跑了一次,时间受接口的响应影响大,这一项不下结论。
所以这次实测的结论比文章保守:在小数据上,强模型用一套粗糙的工具也能答对,好的工具省下的是调用次数和 token。 文章说的准确度提升(比如把 UUID 换成有意义的名字能减少编造),在这么小的数据上没有体现出来,要更大、更难的任务才测得出。
怎么改进工具:原型、评测、让 Claude 帮你改
文章给的流程分三步。
先做原型,自己试。 用 Claude Code 写工具时,把要用到的库和接口的文档给它,官方文档站一般有 llms.txt。把工具包成一个本地的 MCP 服务器,用 claude mcp add <名字> <命令> [参数…] 接进 Claude Code,自己用一用,找出别扭的地方。怎么写和接 MCP 服务器,见《Claude Code 接 MCP 服务器实战》。
再做评测。 让 Claude Code 照着真实的用法生成几十个任务,每个配一个能核对的答案。好的任务要用到多个工具,甚至几十次调用,比如“客户 9182 说一次购买被扣了三次款,找出相关日志,看还有没有别的客户受影响”;差的任务太直白,比如“搜 customer_id=9182 的支付日志”。核对答案不要太死,格式、标点、说法不同就判错是不对的。跑评测用最简单的循环就行:一个任务一个循环,交替调接口和工具。除了准确率,还要记工具调用次数、token 用量、耗时和工具报错:很多重复调用说明分页或长度限制该调整,很多参数错误说明描述或例子不够清楚。
让 Claude 帮你分析和改。 把评测的完整记录拼在一起贴给 Claude Code,它擅长读记录、一次重构一批工具并保持描述一致。文章说里面的大部分建议,就是他们反复用 Claude Code 优化内部工具得出来的。有一个例子:Claude 的网页搜索工具刚上线时,Claude 总在查询词后面加上“2025”,把结果带偏了,改了工具描述才好。还要留一份不参与调优的测试集,免得在评测上过拟合。
评测本身怎么建,见本站的《给智能体建评测》。
工具很多时:代码执行和三个平台功能
接的 MCP 服务器多了以后,还有两个问题:工具定义本身就占掉大量上下文;中间结果一层层传给模型,也很占地方。
《Code execution with MCP》这篇文章的思路是把 MCP 服务器当成代码接口给智能体,而不是一个个直接调用的工具:每个工具对应文件系统里的一个代码文件,智能体浏览目录找到需要的那几个,读了定义再写代码去调,数据在代码执行环境里处理完再把结果交给模型。文章的例子里,token 从 15 万降到 2 千,省了 98.7%。另一种做法是给服务器加一个 search_tools 工具,按需查找定义,并让它选要多详细(只要名字、名字加描述、完整定义)。
Claude 开发平台上有三个对应的功能,《Introducing advanced tool use》讲了各自什么时候用:
| 你的瓶颈 | 用什么 | 什么时候值得 |
|---|---|---|
| 工具定义太占上下文 | 工具搜索 | 工具定义超过 1 万 token;工具超过 10 个;接了多个 MCP 服务器;选错工具的情况多 |
| 中间结果太大 | 程序化工具调用:让 Claude 在代码执行里调你的工具 | 只需要汇总的大数据;三步以上互相依赖的调用;要对很多项做同样的操作 |
| 参数总是传错 | 工具使用示例(input_examples) |
嵌套复杂的结构;可选参数多;有 schema 表达不了的约定;几个相似的工具需要靠例子区分 |
文章的建议是先解决最大的那个瓶颈,再按需叠加,不要一开始就全上。三个功能的用法本站另有文章:工具搜索和程序化工具调用见《Claude API 的服务器端工具》,input_examples 见《Claude API 工具调用入门》,Agent SDK 里的工具搜索见《Agent SDK 进阶:工具搜索》。
这次实测没有覆盖到的
- 每道题每版只跑了一次。 结果有随机性,跑多次才能看出稳定性。
- 数据很小。 16 个客户,强模型用粗糙的工具也能答对;文章说的准确度提升没有测出来。
- 只测了 Claude API 的工具调用。 同样的工具包成 MCP 服务器接进 Claude Code,原则一样,但本站没有另外测。
- 没有测前缀分组、返回格式(JSON 和 Markdown)的影响。
- 花费:两版各 4 道题,加起来输入约 7.5 万 token、输出约 4 千 token,按 Sonnet 5.5 的官方价(每百万 token 输入 2 美元、输出 10 美元)估算约 0.2 美元。这是估算,不是中转站实际扣的金额。
参考
- Ken Aizawa:Writing effective tools for AI agents—using AI agents(Anthropic 工程博客,2025-09-11)
- Adam Jones、Conor Kelly:Code execution with MCP: building more efficient AI agents(Anthropic 工程博客,2025-11-04)
- Introducing advanced tool use on the Claude Developer Platform(Anthropic 工程博客,2025-11-24)
- Claude 开发平台文档:Define tools
来源
- Writing effective tools for AI agents—using AI agents(Anthropic 工程博客,2025-09-11)
- Code execution with MCP: building more efficient AI agents(Anthropic 工程博客,2025-11-04)
- Introducing advanced tool use on the Claude Developer Platform(Anthropic 工程博客,2025-11-24)
- Define tools(Claude 开发平台文档)
- Connect Claude Code to tools via MCP(Claude Code 文档)