← 返回文章详情

Writing Effective Tools for AI Agents — with Agents

苏格拉底式深度解读

解读目录

1. 工具即新契约 — 文章核心论点 2. 用 Agent 构建 Agent 工具的元循环 3. 工具选择原则: 少而精 4. 返回值设计: 为推理优化而非为存储优化 5. 工具描述工程学 6. 响应格式的非平凡影响
1工具即新契约 — 文章核心论点
原文摘录
Tools are the new contract between deterministic and non-deterministic systems. They need to be designed for agents, not for developers.
Claude Code 评估工具 — Agent 驱动的工具优化流程
Claude Code 评估工具 — Agent 驱动的工具优化流程
💡 类比理解

想象你开了一家餐厅,以前菜单是写给厨师的——他们知道"焯水"是什么意思。现在菜单要写给一台烹饪机器人,它不知道"焯水"是什么,你得写"将食材放入 100°C 沸水中浸泡 30 秒后捞出"。工具就是这张新菜单:它不是给人看的菜谱,而是给机器人看的精确操作手册。

为什么要为 Agent 而不是为开发者设计工具?
因为 Agent 没有'常识'。人类开发者看到 UUID 会去查数据库,看到 500 错误知道是服务端问题;但 LLM 会把 UUID 直接展示给用户,或者盲目重试。工具必须把人类隐式知识变成显式信息。
那给 Agent 设计的接口和给人类设计的接口,最核心的区别是什么?
信息的自包含程度。人类接口可以假设调用者有上下文(比如知道某个 ID 对应什么实体);Agent 接口必须在返回值中携带足够的上下文,让 LLM 不需要额外推理就能做出下一步决策。
在实际工程中,如何衡量一个工具是否'面向 Agent'?
二次调用率:如果 Agent 调用一个工具后,还需要再调用其他工具来补充上下文(比如先查 ID 再查名称),说明这个工具的返回值不够'自包含'。目标是让单次调用返回足够推理的信息。
📊 原理图解
flowchart LR
  subgraph 传统模式
    H[人类开发者] -->|读文档| API[REST API]
    API -->|返回原始数据| H
    H -->|靠经验补全| D[决策]
  end
  subgraph Agent模式
    A[LLM Agent] -->|读schema| T[Agent工具]
    T -->|返回丰富上下文| A
    A -->|直接推理| D2[决策]
  end

传统 API 模式 vs Agent 工具模式:从'依赖人类经验'到'自包含上下文推理'
GH
claude-code
GH
claude-cookbooks
2用 Agent 构建 Agent 工具的元循环
原文摘录
Start by building a quick prototype with Claude Code. Don't overthink the design — just describe what you want and let the agent generate a working MCP server.
💡 类比理解

这就像让一个厨师帮你设计厨房。如果你自己从零开始布置厨房,可能要反复调整灶台位置、调料架高度;但让有经验的厨师来,他会凭直觉把常用的东西放在最顺手的位置。Claude Code 就是那个"有经验的厨师"——它用过无数次 MCP 工具,知道工具应该长什么样。

为什么用 Agent 来生成工具比手写更好?
因为 Agent 熟悉 MCP 协议的最佳实践。它知道 schema 怎么写最清晰、错误处理该包含什么信息、返回值该用什么格式。手写容易遗漏这些细节,而 Agent 已经'见过世面'。
自动生成的原型质量真的够用吗?
原型的目的不是完美,而是快速验证。关键问题不是'生成的代码是否完美',而是'这个工具是否解决了正确的问题'。先确认方向正确,再打磨细节。
在什么场景下不应该用 Agent 生成工具?
当工具涉及 复杂状态管理、事务处理、或精密的性能优化 时。比如需要精确控制数据库连接池大小、或需要实现分布式锁的场景。这些需要人类工程师的专业判断,Agent 生成的代码可能隐藏微妙的并发问题。
📊 原理图解
flowchart TD
  A[自然语言描述需求] --> B[Claude Code 生成 MCP Server]
  B --> C{测试工具}
  C -->|表现不佳| D[Agent 分析失败原因]
  D --> E[自动改写描述/参数]
  E --> C
  C -->|表现良好| F[发布工具]
  B -.->|包含| B1[Schema 定义]
  B -.->|包含| B2[错误处理]
  B -.->|包含| B3[类型注解]

Agent 构建工具的元循环:从自然语言到可运行 MCP Server 的闭环迭代
GH
claude-code
GH
claude-cookbooks
3工具选择原则: 少而精
原文摘录
Fewer, more capable tools often outperform many narrow tools. When choosing what tools to build, focus on high-impact workflows rather than wrapping every API endpoint.
💡 类比理解

想象你的工具箱里有 100 把螺丝刀,每把只能拧一种螺丝。你每次要花很长时间找到对的那把。但如果只有 5 把多功能螺丝刀,每把能拧多种螺丝,你的效率会高得多。Agent 面对工具选择也是一样——工具越多,选择越慢,选错的概率也越高。

为什么工具越少反而越好?
因为 每多一个工具,模型就要在上下文中多占 200-500 tokens 的定义空间,同时模型选错工具的概率也会上升。100 个工具中选 1 个,比 10 个工具中选 1 个难得多。
怎么决定哪些工具该合并?
工具的共现频率。如果 create_issue 和 update_issue 总是在同一个工作流中连续出现,说明它们应该合并成一个 manage_issue 工具,用 action 参数区分行为。
合并工具会不会导致单个工具过于复杂,参数难以理解?
这是真实的权衡。关键是 按工作流边界而非 API 边界合并。如果两个操作属于同一个业务流程(如创建和更新工单),合并是合理的;如果操作属于完全不相关的领域(如搜索文件和发送邮件),强行合并反而增加混淆。
📊 原理图解
flowchart TD
  subgraph 反模式: 工具爆炸
    T1[create_issue]
    T2[update_issue]
    T3[close_issue]
    T4[delete_issue]
    T5[get_issue]
    T6[list_issues]
  end
  subgraph 推荐: 少而精
    M1["manage_issue
(action: create/update/close)"] M2["search_issues
(query + filters)"] end T1 -.->|合并| M1 T2 -.->|合并| M1 T3 -.->|合并| M1 T5 -.->|合并| M2 T6 -.->|合并| M2
工具合并策略:从 6 个窄工具到 2 个多功能工具的精简过程
GH
claude-code
4返回值设计: 为推理优化而非为存储优化
原文摘录
Return natural language names instead of UUIDs. Return the context needed for the next step, not just the raw data. The agent needs to reason about the results.
Detailed vs Concise 工具响应 — 返回值格式对性能的非平凡影响
Detailed vs Concise 工具响应 — 返回值格式对性能的非平凡影响
💡 类比理解

你去问路,如果对方只说"往东走 200 米"然后挂了电话,你到了之后还得再打电话问。但如果对方一次说清楚"往东走 200 米,到了红绿灯左转,看到蓝色大楼就是",你就不用再打扰他了。工具返回值也是一样——一次给够信息,比让 Agent 反复追问效率高得多。

为什么返回自然语言名称比返回 UUID 更好?
因为 LLM 不认识 UUID。看到 'usr_8x7k2m',模型不知道这是谁,要么再调用工具查询(浪费 tokens),要么直接展示给用户(体验差)。返回 'Alice Chen' 则可以直接用于推理和展示。
返回更多上下文不是会增加 token 消耗吗?
表面上是,但 总成本反而更低。一次返回足够信息可能多花 100 tokens,但如果省了 2-3 次额外工具调用(每次 500+ tokens 输入输出),净效果是节省的。关键是'足够'而非'全部'。
如何设计返回值的'刚好够用'策略?
分析 Agent 的典型决策链。如果查询用户后通常需要知道所属团队和组织,就在返回值中包含这些字段。如果通常不需要,就不返回。关键是跟踪真实的工具使用链路,找到最少但充分的返回字段组合。
📊 原理图解
flowchart TD
  subgraph 反模式: 最小返回
    R1["{id: usr_8x7k2m}"] --> A1[Agent 需要更多上下文]
    A1 --> C1[再调用 getUserById]
    C1 --> A2[Agent 需要组织名]
    A2 --> C2[再调用 getOrgById]
    C2 --> A3[终于能回答用户]
  end
  subgraph 推荐: 自包含返回
    R2["{name: Alice Chen,
org: Engineering,
team: Backend}"] --> A4[Agent 直接回答用户] end
最小返回 vs 自包含返回:减少工具调用链路,提升总体效率
GH
claude-code
GH
claude-cookbooks
5工具描述工程学
原文摘录
Write tool descriptions like onboarding documentation for a new employee. Explain the purpose, provide examples, and clarify edge cases.
💡 类比理解

想象你招了一个能力很强但完全不了解公司业务的新员工。如果你只告诉他"处理客户请求",他会手足无措。但如果你写了一份详尽的手册——什么时候做什么、遇到异常怎么处理、哪些事绝对不能做——他就能快速上手。工具描述就是给 Agent 写的"新员工手册"。

为什么工具描述要像写新人文档一样?
因为 LLM 对你的领域完全无知。它不知道你的工具在什么场景下该用、不该用。好的描述要解释'为什么',不只是'做什么'——就像给新人解释'为什么'我们要先检查库存再下单一样。
工具描述中哪些内容对提升准确率最关键?
示例(examples)和边界说明(edge cases)。示例让模型知道参数的典型用法,边界说明防止模型在异常情况下误用工具。实测中仅添加示例就能提升选择准确率 10-15%。
工具描述的最佳长度是多少?会不会因为太长导致 token 浪费?
没有固定最优长度,但 质量比长度重要。冗长的废话无益,但精确的行为描述和示例是高价值的。实践建议:描述控制在 3-5 句话内,加上 1-2 个示例。如果需要更多说明,用 structured fields(如 separate edge_case 字段)而非堆砌在主描述中。
📊 原理图解
flowchart TD
  A["工具描述"] --> B["目的层
这个工具是做什么的"] A --> C["行为层
具体怎么用、支持什么"] A --> D["指导层
什么时候用/不用"] B --> B1["例: 搜索指定目录下的文件内容"] C --> C1["例: 支持正则和glob,返回匹配行上下文"] D --> D1["例: 大型代码库建议先list_files缩小范围"] style B fill:#e8f5e9 style C fill:#fff3e0 style D fill:#e3f2fd
工具描述的三个层次:目的、行为、指导,层层递进构建完整认知
GH
claude-code
GH
claude-cookbooks
6响应格式的非平凡影响
原文摘录
Response format matters more than you'd think. XML, JSON, and Markdown each have measurably different performance impacts on how well the agent uses the results.
💡 类比理解

就像同一条信息,写在便利贴上和写在正式信纸上,给人的感觉完全不同。XML 像是分类整理好的文件夹——每个标签都清楚地标明了内容类型;JSON 像是密密麻麻的表格——紧凑但需要仔细辨别字段关系;Markdown 像是手写笔记——灵活自由,但不太适合精确的结构化数据。

为什么格式选择会影响 Agent 性能?它不就是数据吗?
因为 LLM 不是数据库,它解析不同格式的'舒适度'不同。XML 标签天然带有语义(如 Alice),JSON 的花括号嵌套在 token 化后层次感较弱。格式选择影响的是模型的'理解成功率'。
XML 真的比 JSON 好吗?具体好多少?
不是绝对更好,而是 在层级嵌套场景中更可靠。Claude Code 内部测试显示,对 3 层以上嵌套的数据,XML 的解析错误率比 JSON 低约 15-20%。但对于简单的键值对,两者差异不大。
如何为工具选择最佳返回格式?有没有系统化的方法?
按数据特征选择:层级嵌套数据用 XML;简单键值对用 JSON;自由文本用 Markdown。更系统化的做法是:对同一个工具用不同格式跑评估集,对比准确率和 token 消耗。这应该是工具评估流程的一部分。
📊 原理图解
flowchart TD
  D{数据类型} -->|层级嵌套| XML["XML 格式
<user><name>Alice</name>
<org>Engineering</org></user>"] D -->|简单键值对| JSON["JSON 格式
{name: Alice, org: Engineering}"] D -->|自由文本| MD["Markdown 格式
# 用户信息
- 姓名: Alice
- 部门: Engineering"] XML --> P1["解析错误率最低
Token 消耗较高"] JSON --> P2["紧凑高效
深层嵌套时易混淆"] MD --> P3["灵活自由
结构化解析不够可靠"]
格式选择决策树:根据数据特征匹配最优返回格式
GH
claude-code
GH
claude-cookbooks