给 AI agent 写工具:让 agent 自己帮你打磨
MCP 让 agent 可以一下接几百个工具,但工具好不好用是另一回事。这篇讲 Anthropic 怎么用 Claude Code 帮自己打磨内部工具:先写原型、再跑 eval、再让 Claude 帮你看 transcript 改 tool。
一、Tool 是一种新型软件 ⭐
写传统软件,你是在两个确定性系统之间签契约 —— getWeather("NYC") 永远以同样方式拿同样的天气。
写 tool 就不一样了:tool 是确定性系统与非确定性 agent 之间的契约。用户问「今天要带伞吗」,agent 可能:
- 调天气 tool
- 从通用知识里直接答
- 反问「你在哪个城市」
- 甚至幻觉一个错误调用
这意味着写 tool 不能照搬给开发者写 SDK 那一套,要重新设计。目标是:让 agent 在更多任务上能用更多策略走通。
经验上,agent 觉得”顺手”的工具,人看着也直觉。两种诉求竟然挺一致。
二、写工具的工作流:prototype → eval → 让 agent 改
1. 先建原型
工具好不好用,你自己不上手很难预判。
- 用 Claude Code 一锤子写原型,把依赖的 SDK / API 文档喂给它(
llms.txt这种 LLM 友好的文档优先) - 用 local MCP server 或 DXT(Desktop Extension)包一层,能直接接到 Claude Code / Claude Desktop 测
- 自己玩一遍,从用户那里收 prompt,建立直觉
2. 跑 eval ⭐
光看 demo 不够,得系统性测量。
好的 eval 任务长什么样:
| 类型 | 例子 |
|---|---|
| ✅ 强 | 「下周给 Jane 安排个会议讨论 Acme 项目,附上上次规划会的笔记,订一间会议室」 |
| ✅ 强 | 「客户 9182 反馈被重复扣款 3 次,找出所有相关日志,看有没有其他客户遇到同问题」 |
| ❌ 弱 | 「给 jane@acme.corp 安排下周会议」 |
| ❌ 弱 | 「查 customer_id=9182 的支付日志」 |
差别在哪?强 eval 任务模拟真实复杂度,常常需要十几次工具调用串起来。弱的就只考一次调用、没有歧义。
怎么验证结果:
- 简单到字符串比对,复杂到让 Claude 来评判
- 别太严,比如格式/标点差异就把对的判错
跑 eval 的姿势:
- 直接 API 调用 + 简单 agentic loop(while 循环切换 LLM 调用与 tool 调用)
- 在 system prompt 里让 agent 输出 reasoning + feedback block 再调 tool —— 触发 chain-of-thought,effective intelligence 会涨
- Claude 自带 interleaved thinking 也能用
- 除了 accuracy,要收:单次调用耗时、调用次数、token 消耗、报错率
3. 让 agent 自己分析
Agent 是看 transcript 找问题的好搭子。把 eval transcript 串起来贴给 Claude Code,让它分析、找出:
- 工具描述前后矛盾
- 实现低效的地方
- schema 让人困惑的字段
但 agent 说的不全是它想的。它没说的,往往比它说的更值得注意。亲自读原始 transcript,包括 tool 调用与返回,字里行间找蛛丝马迹
实战例子:他们当初发布 Claude 的 web search tool,发现 Claude 老往 query 里塞 2025,影响搜索结果。改了工具描述就解决了。
三、写好 tool 的 5 条原则
原则 1:别只是包一下已有 API ⭐
最常见的错误:把现成 API 包一层就当工具丢出来。
| 反例 | 正解 |
|---|---|
list_contacts(返回全部联系人,agent 一个个看) | search_contacts / message_contact |
list_users + list_events + create_event(三步) | schedule_event(一步搞定可用性查 + 安排) |
read_logs(吐全日志) | search_logs(只返相关行 + 上下文) |
get_customer_by_id + list_transactions + list_notes | get_customer_context(一次拿齐) |
为什么:agent 的 context 是稀缺资源;传统软件的内存是廉价资源。让 agent 像人翻地址簿一样跳到相关页,别让它逐页扫。
少而精的 tool,胜过多而冗。工具集臃肿会让 agent 在选择中迷失。
原则 2:命名空间区分边界
Agent 可能同时挂几十个 MCP server、几百个 tool。重叠和模糊的功能会让 agent 选错。
用前缀分组:
- 按服务:
asana_search、jira_search - 按资源:
asana_projects_search、asana_users_search
前缀 vs 后缀的命名风格,在他们的 eval 里确实有非平凡影响,建议各自跑 eval 看自己模型偏好
原则 3:返回有意义的上下文
只返高信号信息,别堆底层技术字段。
| 别返 | 多返 |
|---|---|
uuid | name |
256px_image_url | image_url |
mime_type | file_type |
Agent 对自然语言标识符的处理远好于神秘的字母数字 UUID。仅仅把 UUID 换成语义化名字(或 0-indexed ID),就能显著降低幻觉。
进阶:开放 response_format 参数让 agent 自己选「concise / detailed」。例如 Slack 工具:concise 模式只返 thread 内容(72 token),detailed 模式附上 thread_ts / channel_id 等 ID 给后续调用用(206 token)—— 节省 2/3 的 token。
格式(XML / JSON / Markdown)也影响表现 —— LLM 在训练数据里见过的格式上表现更好。没有 one-size-fits-all,用你自己的 eval 来选。
原则 4:token 效率
关键设置:分页、范围选择、过滤、截断 —— 给所有可能吐大量 context 的工具加上合理默认值。
Claude Code 内部默认把工具返回限制在 25,000 token。即便未来 context 变大,token 高效仍是必需
截断时要引导 agent:
| ❌ 无效错误 | ✅ 有效错误 |
|---|---|
| 抛 raw error code / traceback | 「输入超出范围,尝试缩小日期窗 + 加 filter」 |
| 静默截断 | 「结果被截断,做 N 个小搜索而不是一次大搜索」 |
报错信息也是 prompt,专门 prompt-engineer 一下。
原则 5:把工具描述当 prompt 来 engineering
这是最有效的优化点之一。工具描述和 schema 会被加载进 agent 的 context,它们会直接 steer agent 的调用行为。
写工具描述时像在给新人写交接文档:
- 把你下意识带的领域知识写出来(术语定义、资源关系、查询格式)
- 严格 schema 强制约束输入输出
- 参数命名消歧 ——
user_id比user好
Claude Sonnet 3.5 在 SWE-bench Verified 上拿到 SOTA,就是靠精修工具描述把错误率打下去的
总结:直觉
给 agent 写工具不是给 SDK 写接口的延续,是个新工种。
⭐ 核心切换:
- 别只是包 API —— 按 agent 的实际工作流设计端到端的能力
- 用最少的高信号字段返回 —— 每 token 都花在刀刃上
- 用 eval 驱动迭代 —— 直觉常常错,让 Claude 看 transcript 帮你看
- 工具描述是 prompt 的一部分 —— 认真 prompt engineering 一下
随着模型变强,工具与 agent 之间的协议(MCP)也会变。但**「按非确定性系统的视角去设计接口」**这条底层方法不会过时。