为 AI Agent 编写高效工具——借助 AI Agent
原文链接: www.anthropic.com/engineering…
发布日期: 2025年9月11日
Agent 的效能完全取决于我们为其提供的工具。本文分享如何编写高质量的工具和评估方案,以及如何利用 Claude 为自己优化工具来提升性能。
模型上下文协议(MCP) 可以让 LLM Agent 拥有数百种工具来解决实际任务。但如何让这些工具发挥最大效用呢?
在这篇文章中,我们描述了在各种 Agent 式 AI 系统中提升性能的最有效技术。
我们将首先介绍如何:
- 构建和测试工具原型
- 创建和运行工具的综合评估
- 与 Claude Code 等 Agent 协作,自动提升工具性能
最后,我们总结了编写高质量工具的关键原则:
- 选择正确的工具来实现(以及不实现哪些)
- 使用命名空间来定义功能的清晰边界
- 从工具向 Agent 返回有意义的上下文
- 优化工具响应以提高 token 效率
- 对工具描述和规格进行提示工程

💡 本文核心内容概览
graph TD
Root((AI Agent<br/>工具编写))
Root --> A[构建原型]
Root --> B[运行评估]
Root --> C[与 Agent 协作]
Root --> D[编写原则]
A --> A1[使用 MCP SDK]
A --> A2[本地测试]
B --> B1[生成评估任务]
B --> B2[运行评估]
B --> B3[分析结果]
C --> C1[Claude Code 优化]
C --> C2[迭代改进]
D --> D1[选择正确工具]
D --> D2[命名空间]
D --> D3[返回有意义上下文]
D --> D4[Token 效率优化]
D --> D5[提示工程]
通过构建评估方案,您可以系统地测量工具的性能。您可以使用 Claude Code 根据评估结果自动优化工具。
什么是工具?
在计算领域,确定性系统在相同输入下每次都产生相同输出,而非确定性系统——如 Agent——即使在相同的起始条件下也可能生成不同的响应。
当我们传统地编写软件时,我们是在确定性系统之间建立契约。例如,像 getWeather("NYC") 这样的函数调用每次都会以完全相同的方式获取纽约市的天气。
工具是一种新型软件,它反映了确定性系统与非确定性 Agent 之间的契约。当用户询问"我今天需要带伞吗?"时,Agent 可能会调用天气工具、从通用知识中回答,甚至可能首先询问关于位置的澄清问题。偶尔,Agent 可能会产生幻觉,甚至可能无法理解如何使用工具。
💡 传统软件 vs Agent 工具对比
| 特性 | 传统软件(确定性) | Agent 工具(非确定性) |
|---|---|---|
| 行为预测 | 相同输入 → 相同输出 | 相同输入 → 可能不同输出 |
| 契约对象 | 系统与系统之间 | 系统与 Agent 之间 |
| 调用决策 | 由开发者预设 | 由 Agent 自主决定 |
| 错误处理 | 预定义的异常处理 | 可能幻觉或误用 |
| 设计重点 | 功能正确性 | Agent 可理解性 |
这意味着在为 Agent 编写软件时,我们需要从根本上重新思考方法:我们不能像为其他开发者或系统编写函数和 API 那样编写工具和 MCP 服务器,而是需要专门为 Agent 设计它们。
我们的目标是扩大 Agent 能够有效解决各种任务的范围,通过使用工具来追求各种成功策略。幸运的是,根据我们的经验,对 Agent 最"符合人体工程学"的工具最终对人类来说也出奇地直观易懂。
如何编写工具
在本节中,我们描述如何与 Agent 协作来编写和改进您给它们的工具。首先快速建立工具原型并在本地测试。接下来,运行综合评估来测量后续更改。与 Agent 并肩工作,您可以重复评估和改进工具的过程,直到您的 Agent 在实际任务中达到强劲的性能。
💡 工具开发迭代流程
graph TD
A[构建原型] --> B[本地测试]
B --> C[创建评估任务]
C --> D[运行评估]
D --> E[分析结果]
E --> F{性能达标?}
F -->|否| G[与 Agent 协作优化]
G --> D
F -->|是| H[部署使用]
构建原型
在没有亲身实践之前,很难预测哪些工具 Agent 会觉得符合人体工程学,哪些不会。首先快速建立工具原型。如果您使用 Claude Code 编写工具(可能一次性完成),向 Claude 提供工具将依赖的任何软件库、API 或 SDK(可能包括 MCP SDK)的文档会有所帮助。LLM 友好的文档通常可以在官方文档网站上的扁平 llms.txt 文件中找到(这是我们 API 的文档)。
将您的工具封装在本地 MCP 服务器或桌面扩展(DXT)中,将允许您在 Claude Code 或 Claude 桌面应用中连接和测试您的工具。
连接 MCP 服务器的方法:
- 连接到 Claude Code:运行
claude mcp add <name> <command> [args...] - 连接到 Claude Desktop:导航到
Settings > Developer或Settings > Extensions
工具也可以直接传递到 Anthropic API 调用中进行程序化测试。
亲自测试工具以识别任何粗糙的边缘。收集用户反馈,建立对您期望工具支持的用例和提示的直觉。
运行评估
接下来,您需要通过运行评估来测量 Claude 使用工具的效果。首先生成大量基于实际使用场景的评估任务。我们建议与 Agent 协作来帮助分析结果并确定如何改进工具。在我们的工具评估手册中可以看到完整的端到端流程。

💡 评估驱动的工具优化流程
graph TD
A[生成评估任务<br/>基于真实使用场景] --> B[运行评估<br/>程序化 API 调用]
B --> C[收集指标<br/>准确率/运行时间/Token消耗]
C --> D[分析结果<br/>识别问题模式]
D --> E[与 Claude Code 协作<br/>自动优化工具]
E --> F[验证改进<br/>测试集评估]
F --> G{达到目标?}
G -->|否| B
G -->|是| H[发布优化后的工具]
生成评估任务
使用早期原型,Claude Code 可以快速探索您的工具并创建数十个提示和响应对。提示应该受到实际使用的启发,并基于真实的数据源和服务(例如,内部知识库和微服务)。我们建议您避免过于简单或肤浅的"沙盒"环境,这些环境无法用足够的复杂性来压力测试您的工具。强评估任务可能需要多次工具调用——可能是几十次。
💡 评估任务质量对比
| 类型 | 示例 | 原因 |
|---|---|---|
| ✅ 强任务 | "安排下周与 Jane 开会讨论我们最新的 Acme Corp 项目。附上上次项目规划会议的笔记并预订会议室。" | 需要多步骤、多工具协调 |
| ✅ 强任务 | "客户 ID 9182 报告说单次购买尝试被收费三次。查找所有相关日志条目并确定是否有其他客户受到同一问题的影响。" | 需要搜索、分析、判断 |
| ✅ 强任务 | "客户 Sarah Chen 刚提交了取消请求。准备一份留存方案。确定:(1) 她为什么要离开,(2) 什么留存方案最有吸引力,(3) 在提出方案之前我们应该注意的任何风险因素。" | 复杂的业务逻辑和决策 |
| ❌ 弱任务 | "安排下周与 jane@acme.corp 开会。" | 过于简单,单一操作 |
| ❌ 弱任务 | "搜索付款日志中的 purchase_complete 和 customer_id=9182。" | 过于具体,无需 Agent 决策 |
| ❌ 弱任务 | "查找客户 ID 45892 的取消请求。" | 直接查询,无复杂性 |
每个评估提示都应该与可验证的响应或结果配对。您的验证器可以简单到在真实值和采样响应之间进行精确字符串比较,也可以高级到请 Claude 来判断响应。避免过于严格的验证器,这些验证器会因格式、标点符号或有效的替代措辞等表面差异而拒绝正确的响应。
对于每个提示-响应对,您还可以选择性地指定您期望 Agent 在解决任务时调用的工具,以测量 Agent 在评估期间是否成功理解了每个工具的目的。但是,由于可能有多条有效路径来正确解决任务,尽量避免过度指定或过度拟合策略。
运行评估
我们建议使用直接 LLM API 调用以程序化方式运行评估。使用简单的 Agent 循环(while 循环包装交替的 LLM API 和工具调用):每个评估任务一个循环。每个评估 Agent 应该被赋予单个任务提示和您的工具。
在评估 Agent 的系统提示中,我们建议指示 Agent 不仅输出结构化的响应块(用于验证),还要输出推理和反馈块。指示 Agent 在工具调用和响应块之前输出这些内容,可能会通过触发思维链(CoT)行为来增加 LLM 的有效智能。
如果您使用 Claude 运行评估,您可以打开交织思考(interleaved thinking) 以获得类似的"开箱即用"功能。这将帮助您探究 Agent 为什么调用或不调用某些工具,并突出工具描述和规格中的具体改进领域。
除了顶级准确率外,我们建议收集其他指标,如:
- 单个工具调用和任务的总运行时间
- 工具调用总数
- Token 消耗总量
- 工具错误
跟踪工具调用可以帮助揭示 Agent 追求的常见工作流,并为工具整合提供一些机会。

分析结果
Agent 是您发现问题和提供反馈的得力伙伴,从矛盾的工具描述到低效的工具实现和令人困惑的工具模式都能识别。然而,请记住,Agent 在反馈和响应中省略的内容往往比包含的内容更重要。LLM 并不总是言行一致。
观察您的 Agent 在哪里遇到困难或困惑。阅读评估 Agent 的推理和反馈(或 CoT)以识别粗糙的边缘。查看原始记录(包括工具调用和工具响应)以捕获 Agent 的 CoT 中没有明确描述的任何行为。字里行间地阅读;记住您的评估 Agent 不一定知道正确的答案和策略。
分析您的工具调用指标:
- 大量冗余工具调用 → 可能表明需要对分页或 token 限制参数进行适当调整
- 大量无效参数的工具错误 → 可能表明工具需要更清晰的描述或更好的示例
当我们推出 Claude 的网页搜索工具时,我们发现 Claude 不必要地在工具的 query 参数后附加 2025,这使搜索结果产生偏差并降低了性能(我们通过改进工具描述引导 Claude 走向正确的方向)。
与 Agent 协作
您甚至可以让 Agent 为您分析结果并改进工具。只需连接评估 Agent 的记录并将其粘贴到 Claude Code 中。Claude 是分析记录和一次性重构大量工具的专家——例如,确保在进行新更改时工具实现和描述保持自洽。
事实上,这篇文章中的大部分建议都来自于使用 Claude Code 反复优化我们内部工具实现的过程。我们的评估是在我们的内部工作空间之上创建的,反映了我们内部工作流的复杂性,包括真实的项目、文档和消息。
我们依靠留出的测试集来确保我们不会过度拟合"训练"评估。这些测试集揭示,即使超越"专家"工具实现——无论这些工具是由我们的研究人员手动编写还是由 Claude 本身生成——我们仍然可以提取额外的性能改进。
💡 Slack 工具性能对比
以下是我们内部 Slack 工具在留出测试集上的性能表现:
| 工具版本 | 测试集准确率 |
|---|---|
| 人工编写 | ~75% |
| Claude 优化后 | ~90% |
💡 Asana 工具性能对比
| 工具版本 | 测试集准确率 |
|---|---|
| 人工编写 | ~70% |
| Claude 优化后 | ~85% |
在下一节中,我们将分享从这个过程中学到的一些经验。
编写高效工具的原则
在本节中,我们将我们的学习提炼为编写高效工具的几个指导原则。
💡 高效工具编写原则总览
graph TD
Root((编写高效工具<br/>的五大原则))
Root --> A[选择正确的工具]
Root --> B[命名空间管理]
Root --> C[返回有意义上下文]
Root --> D[Token 效率优化]
Root --> E[提示工程]
A --> A1[聚焦高影响力工作流]
A --> A2[整合功能减少工具数]
B --> B1[按服务命名]
B --> B2[按资源命名]
C --> C1[高信号信息]
C --> C2[自然语言标识符]
C --> C3[响应格式控制]
D --> D1[分页]
D --> D2[过滤]
D --> D3[截断]
E --> E1[明确的描述]
E --> E2[清晰的参数]
E --> E3[示例]
选择正确的工具
更多的工具并不总是带来更好的结果。我们观察到的一个常见错误是,工具仅仅包装现有的软件功能或 API 端点——无论这些工具是否适合 Agent。这是因为 Agent 与传统软件有不同的"可供性"(affordances)——也就是说,它们感知使用这些工具可以采取的潜在行动的方式不同。
LLM Agent 的"上下文"有限(即,它们一次能处理的信息量有限),而计算机内存便宜且充足。考虑在地址簿中搜索联系人的任务。传统软件程序可以高效地存储和处理联系人列表,逐一检查每一个然后继续下一个。
然而,如果 LLM Agent 使用一个返回所有联系人的工具,然后必须逐个 token 地阅读每一个,它就是在浪费有限的上下文空间在无关信息上(想象一下通过从上到下阅读每一页来搜索地址簿中的联系人——即暴力搜索)。更好且更自然的方法(对 Agent 和人类都是如此)是先跳到相关页面(也许按字母顺序找到它)。
💡 工具设计决策对比
| ❌ 不推荐的方法 | ✅ 推荐的方法 | 原因 |
|---|---|---|
实现 list_users、list_events、create_event 工具 | 实现 schedule_event 工具,内部处理查找空闲时间和创建事件 | 减少工具调用次数,整合相关功能 |
实现 read_logs 工具 | 实现 search_logs 工具,只返回相关日志行和周围上下文 | 减少上下文消耗 |
实现 get_customer_by_id、list_transactions、list_notes 工具 | 实现 get_customer_context 工具,一次性编译客户所有最近和相关信息 | 减少往返调用 |
我们建议构建几个针对特定高影响力工作流的深思熟虑的工具,这些工具与您的评估任务匹配,然后从那里扩展。在地址簿案例中,您可能会选择实现 search_contacts 或 message_contact 工具,而不是 list_contacts 工具。
工具可以整合功能,在底层处理可能的多个离散操作(或 API 调用)。例如,工具可以用相关元数据丰富工具响应,或在单个工具调用中处理经常链接的多步骤任务。
确保您构建的每个工具都有清晰、独特的目的。工具应该使 Agent 能够以人类在给定相同底层资源访问权限时会采用的方式来细分和解决任务,同时减少中间输出本应消耗的上下文。
太多工具或重叠的工具也会分散 Agent 追求高效策略的注意力。仔细、有选择性地规划您构建(或不构建)的工具确实会带来回报。
命名空间管理
您的 AI Agent 可能会获得数十个 MCP 服务器和数百种不同工具的访问权限——包括其他开发者的工具。当工具在功能上重叠或目的模糊时,Agent 可能会对使用哪些工具感到困惑。
命名空间(将相关工具分组在公共前缀下)可以帮助划分大量工具之间的边界;MCP 客户端有时默认会这样做。例如,按服务命名(如 asana_search、jira_search)和按资源命名(如 asana_projects_search、asana_users_search)可以帮助 Agent 在正确的时间选择正确的工具。
我们发现,选择基于前缀还是基于后缀的命名空间对我们的工具使用评估有非平凡的影响。效果因 LLM 而异,我们鼓励您根据自己的评估选择命名方案。
💡 命名空间示例
graph TD
subgraph 按服务命名
A1[asana_search]
A2[asana_create]
A3[jira_search]
A4[jira_create]
end
subgraph 按资源命名
B1[asana_projects_search]
B2[asana_users_search]
B3[asana_projects_create]
B4[asana_users_create]
end
Agent 可能会调用错误的工具、用错误的参数调用正确的工具、调用太少的工具,或错误地处理工具响应。通过有选择性地实现名称反映任务自然细分的工具,您同时减少了加载到 Agent 上下文中的工具和工具描述数量,并将 Agent 计算从 Agent 的上下文卸载回工具调用本身。这降低了 Agent 犯错的整体风险。
返回有意义的上下文
同样,工具实现应该注意只向 Agent 返回高信号信息。它们应该优先考虑上下文相关性而非灵活性,避免低级技术标识符(例如:uuid、256px_image_url、mime_type)。像 name、image_url 和 file_type 这样的字段更可能直接指导 Agent 的下游动作和响应。
Agent 也倾向于比处理神秘标识符更成功地处理自然语言名称、术语或标识符。我们发现,仅仅将任意的字母数字 UUID 解析为更具语义意义和可解释性的语言(甚至是 0 索引的 ID 方案),就能通过减少幻觉显著提高 Claude 在检索任务中的精确度。
在某些情况下,Agent 可能需要灵活地与自然语言和技术标识符输出交互,即使只是为了触发下游工具调用(例如,search_user(name='jane') → send_message(id=12345))。您可以通过在工具中公开一个简单的 response_format 枚举参数来同时启用两者,允许您的 Agent 控制工具返回 "concise"(简洁)还是 "detailed"(详细)响应。
您可以添加更多格式以获得更大的灵活性,类似于 GraphQL,您可以精确选择要接收的信息片段。
💡 响应格式控制
enum ResponseFormat {
DETAILED = "detailed",
CONCISE = "concise"
}
详细响应示例(206 tokens):
{
"thread_ts": "1234567890.123456",
"channel_id": "C0123456789",
"user_id": "U0123456789",
"user_name": "alice",
"message": "Let's discuss the Q4 roadmap",
"replies": [
{
"user_id": "U9876543210",
"user_name": "bob",
"message": "Sounds good, I'll prepare the slides",
"timestamp": "1234567891.123456"
}
],
"permalink": "https://slack.com/archives/..."
}
简洁响应示例(72 tokens):
{
"user_name": "alice",
"message": "Let's discuss the Q4 roadmap",
"replies": [
{
"user_name": "bob",
"message": "Sounds good, I'll prepare the slides"
}
]
}
Slack 线程和线程回复由唯一的 thread_ts 标识,这是获取线程回复所必需的。thread_ts 和其他 ID(channel_id、user_id)可以从 "detailed" 工具响应中检索,以启用需要这些的进一步工具调用。"concise" 工具响应只返回线程内容,不包含 ID。在这个例子中,我们使用 "concise" 工具响应时只使用了约 1/3 的 tokens。
甚至您的工具响应结构——例如 XML、JSON 或 Markdown——都可能对评估性能产生影响:没有一劳永逸的解决方案。这是因为 LLM 是在下一个 token 预测上训练的,倾向于在与其训练数据匹配的格式上表现更好。最佳响应结构将因任务和 Agent 而异。我们鼓励您根据自己的评估选择最佳响应结构。
优化 Token 效率
优化上下文质量很重要。但优化返回给 Agent 的工具响应中的上下文数量也很重要。
我们建议对任何可能使用大量上下文的工具响应实施分页、范围选择、过滤和/或截断的某种组合,并使用合理的默认参数值。对于 Claude Code,我们默认将工具响应限制为 25,000 tokens。我们预计 Agent 的有效上下文长度会随时间增长,但对上下文高效工具的需求将保持不变。
💡 工具响应优化策略
| 策略 | 描述 | 适用场景 |
|---|---|---|
| 分页 | 每次返回固定数量的结果 | 大量搜索结果 |
| 范围选择 | 允许指定返回结果的范围 | 日志、时间序列数据 |
| 过滤 | 根据条件筛选相关数据 | 复杂数据集 |
| 截断 | 限制响应的最大长度 | 长文本内容 |
如果您选择截断响应,请务必用有用的说明引导 Agent。您可以直接鼓励 Agent 追求更高 token 效率的策略,比如对知识检索任务进行多次小型和有针对性的搜索,而不是单次广泛搜索。
同样,如果工具调用引发错误(例如,在输入验证期间),您可以对错误响应进行提示工程,以清楚地传达具体和可操作的改进,而不是晦涩的错误代码或回溯。
截断工具响应示例:
{
"results": [...],
"truncated": true,
"total_count": 1500,
"returned_count": 100,
"hint": "Results truncated. Use 'filter' or 'date_range' parameters for more specific queries."
}
❌ 无用的错误响应:
{
"error": "ERR_INVALID_PARAM",
"code": 400
}
✅ 有用的错误响应:
{
"error": "Invalid date format",
"expected": "YYYY-MM-DD (e.g., 2025-01-15)",
"received": "01/15/2025",
"suggestion": "Please use ISO 8601 date format: 2025-01-15"
}
工具截断和错误响应可以引导 Agent 采用更高 token 效率的工具使用行为(使用过滤器或分页)或给出正确格式化工具输入的示例。
提示工程工具描述
现在我们来到改进工具最有效的方法之一:对工具描述和规格进行提示工程。因为这些被加载到 Agent 的上下文中,它们可以集体引导 Agent 采用有效的工具调用行为。
在编写工具描述和规格时,想象一下您会如何向团队中的新员工描述您的工具。考虑您可能隐含带入的上下文——专门的查询格式、小众术语的定义、底层资源之间的关系——并使其明确。通过清楚描述(并使用严格的数据模型强制执行)预期的输入和输出来避免歧义。特别是,输入参数应该被明确命名:用 user_id 而不是 user。
通过评估,您可以更有信心地测量提示工程的影响。即使是对工具描述的小改进也能产生巨大的改进。Claude Sonnet 3.5 在 SWE-bench Verified 评估上达到了最先进的性能,这是在我们对工具描述进行精确改进之后,大大降低了错误率并提高了任务完成率。
💡 工具描述最佳实践
| 方面 | ❌ 不推荐 | ✅ 推荐 |
|---|---|---|
| 参数命名 | user | user_id |
| 描述清晰度 | "搜索数据" | "按关键词搜索用户消息,返回匹配的消息及上下文" |
| 格式说明 | 无 | "日期格式:YYYY-MM-DD" |
| 示例 | 无 | "示例:search_messages(query='meeting', limit=10)" |
您可以在我们的开发者指南中找到工具定义的其他最佳实践。如果您为 Claude 构建工具,我们还建议阅读关于工具如何动态加载到 Claude 系统提示中的内容。最后,如果您正在为 MCP 服务器编写工具,工具注解有助于披露哪些工具需要开放世界访问或进行破坏性更改。
展望未来
要为 Agent 构建有效的工具,我们需要将软件开发实践从可预测的确定性模式重新定向到非确定性模式。
通过我们在本文中描述的迭代、评估驱动的过程,我们已经确定了工具成功的一致模式:
有效的工具具有以下特征:
- 有意识且清晰地定义
- 谨慎使用 Agent 上下文
- 可以在多样化的工作流中组合
- 使 Agent 能够直观地解决实际任务
在未来,我们预计 Agent 与世界交互的具体机制将会演变——从 MCP 协议的更新到底层 LLM 本身的升级。通过系统性的、评估驱动的方法来改进 Agent 工具,我们可以确保随着 Agent 变得更强大,它们使用的工具也会随之演进。
💡 核心要点回顾
graph TD
Root((为 Agent<br/>编写高效工具))
Root --> A[工具开发流程]
Root --> B[核心原则]
Root --> C[关键洞察]
A --> A1[构建原型 → 本地测试]
A --> A2[创建评估 → 运行测试]
A --> A3[分析结果 → 迭代优化]
B --> B1[选择正确的工具<br/>少即是多]
B --> B2[命名空间<br/>清晰边界]
B --> B3[返回高信号上下文]
B --> B4[优化 Token 效率]
B --> B5[提示工程描述]
C --> C1[Agent 友好的工具<br/>对人类也直观]
C --> C2[Claude 可以帮助<br/>优化自己的工具]
C --> C3[评估驱动的<br/>迭代改进]
📝 总结
核心要点
-
工具的本质:工具是确定性系统与非确定性 Agent 之间的新型契约,需要专门为 Agent 设计
-
迭代开发流程:构建原型 → 运行评估 → 分析结果 → 与 Agent 协作优化 → 重复
-
五大编写原则:
- 选择正确的工具(少即是多,整合功能)
- 使用命名空间管理(清晰的功能边界)
- 返回有意义的上下文(高信号、自然语言标识符)
- 优化 Token 效率(分页、过滤、截断)
- 提示工程工具描述(明确、详细、有示例)
-
关键洞察:对 Agent 最"符合人体工程学"的工具,对人类也出奇地直观易懂
-
实践验证:通过 Claude Code 协作优化工具,可以在留出测试集上显著提升性能(如 Slack、Asana 工具优化案例)
致谢
本文由 Ken Aizawa 撰写,得到了来自 Research(Barry Zhang、Zachary Witten、Daniel Jiang、Sami Al-Sheikh、Matt Bell、Maggie Vo)、MCP(Theodora Chu、John Welsh、David Soria Parra、Adam Jones)、Product Engineering(Santiago Seira)、Marketing(Molly Vorwerck)、Design(Drew Roper)和 Applied AI(Christian Ryan、Alexander Bricken)的同事们的宝贵贡献。
原文作者: Ken Aizawa