跳到主要内容

原文:04-writing-tools-for-agents.md 来源:https://www.anthropic.com/engineering/writing-tools-for-agents

用 Agent 为 Agent 写好工具

发布于 2025 年 9 月 11 日

Agent 的能力取决于我们给它什么工具。本文分享如何写高质量的工具与评估,并展示如何用 Claude 自动优化它自己使用的工具。

模型上下文协议(MCP)可以让 LLM Agent 接入潜在的数百个工具来解决真实世界的任务。但如何让这些工具的效果最大化

本文描述我们在多种 Agent 系统中提升性能的最有效技术:

  • 构建并测试工具原型
  • 与 Agent 一起为工具创建并运行全面评估
  • 与 Claude Code 协作自动提升工具性能

最后总结写高质量工具的几条关键原则:

  • 选择该实现的工具(以及不该实现的)
  • 通过命名空间为工具设清晰的功能边界
  • 让工具返回有意义的上下文给 Agent
  • 优化工具响应以节省 token
  • 对工具描述和规格做提示词工程

什么是工具?

在计算中,确定性系统对相同输入总产生相同输出,而非确定性系统(如 Agent)即使初始条件相同也可能产生不同响应。

我们传统写软件时,是在为确定性系统之间建立契约。例如调用 getWeather("NYC") 总是以相同方式获取纽约天气。

工具是一种新型软件,反映了确定性系统与非确定性 Agent 之间的契约。当用户问"今天要带伞吗?",Agent 可能调用天气工具、用常识回答、甚至先反问位置。偶尔 Agent 也会幻觉或不会用工具。

这意味着我们需要从根本上重新思考为 Agent 写软件的方式:不是像给开发者或系统写函数和 API 那样写工具和 MCP 服务器,而是要为 Agent 设计

我们的目标是扩大 Agent 解决任务的有效面——让它们能用工具追求多样的成功策略。值得欣慰的是,对 Agent 最"人体工学"的工具,往往人也觉得直观


如何写工具

构建原型

很难凭想象判断哪些工具对 Agent 顺手哪些不顺手。从快速搭一个工具原型开始。

如果你用 Claude Code 写工具(甚至 one-shot),把工具依赖的库、API、SDK(包括 MCP SDK)的文档喂给 Claude 会有帮助。LLM 友好的文档常以 llms.txt 平文件形式存在于官方文档站。

把工具包装成本地 MCP 服务器或 Desktop Extension(DXT),就能在 Claude Code 或 Claude Desktop 应用中连接和测试。

接 Claude Code:claude mcp add <name> <command> [args...]

接 Claude Desktop:进 Settings > DeveloperSettings > Extensions

工具也可以直接传入 Anthropic API 调用做程序化测试。

亲自测试工具找出粗糙之处。从用户处收集反馈,建立对工具应支持的用例和提示词的直觉。


运行评估

接下来你需要通过运行评估来衡量 Claude 使用工具的效果。起点是基于真实世界用法生成大量评估任务。我们建议与 Agent 协作分析结果并决定如何改进工具。

生成评估任务

用早期原型,Claude Code 可以快速探索工具,创建数十对提示词-响应。提示词应基于真实世界用法,使用真实数据源和服务(例如内部知识库和微服务)。避免过于简单或表面化的"沙箱"环境,因为它们没法用足够的复杂度对工具压测。强评估任务可能需要多次工具调用——可能数十次。

强任务示例

  • "下周和 Jane 安排一个会议讨论我们最新的 Acme Corp 项目。附上上次项目规划会议的笔记,预订一间会议室。"
  • "客户 ID 9182 反馈一次购买被扣款三次。找出所有相关日志条目并判断是否有其他客户被同一问题影响。"
  • "客户 Sarah Chen 刚提交取消请求。准备一个挽留方案。判断:(1) 离开原因,(2) 什么挽留方案最有吸引力,(3) 提供方案前应注意的风险因素。"

弱任务示例

  • "下周与 jane@acme.corp 安排会议。"
  • "在支付日志中搜索 purchase_completecustomer_id=9182。"
  • "查找客户 ID 45892 的取消请求。"

每个评估提示应配可验证的响应或结果。验证器可以简单到字符串完全比较,也可以请 Claude 来评判。避免过严的验证器——不要因格式、标点、合法替代措辞这种不重要的差异拒绝正确响应。

每个 prompt-response 对,可选地指定你期望 Agent 调用的工具集,用于衡量 Agent 是否成功理解每个工具的用途。但因为可能有多条正确路径,避免过度规定或过拟合到某种策略。

运行评估

我们建议程序化运行评估——直接 LLM API 调用。用简单的 Agent 循环(while 循环包裹交替的 LLM API 调用和工具调用),每个评估任务一个循环。每个评估 Agent 给一个任务提示和你的工具。

在评估 Agent 的系统提示中,建议指示 Agent 不仅输出结构化响应块(用于验证),还输出推理与反馈块。让它在工具调用和响应块之前输出,可以通过触发 chain-of-thought 行为提升 LLM 的有效智能。

如果用 Claude 跑评估,可以打开 interleaved thinking 获得开箱即用的类似功能。这有助于探究 Agent 为什么调用或不调用某些工具,并指出工具描述与规格的具体改进点。

除了顶层准确率,建议收集其他指标:单个工具调用和任务的总耗时、工具调用次数、token 总消耗、工具错误。追踪工具调用能揭示常见工作流,提供工具合并的机会

分析结果

Agent 是你的好搭档——发现问题、对工具描述、实现、混乱 schema 提供反馈。但记住,Agent 反馈中省略的部分往往比包含的部分更重要。LLM 不总是说出它真正的意思。

观察 Agent 卡住或困惑的地方。读评估 Agent 的推理和反馈(CoT)找出粗糙之处。审查原始转录(包括工具调用和响应)捕捉 CoT 中没有显式描述的行为。读字里行间——评估 Agent 不一定知道正确答案和策略。

分析工具调用指标。大量冗余调用可能暗示分页或 token 限制参数需要调整;大量参数错误可能暗示工具需要更清晰的描述或更好的示例。当我们上线 Claude 的 Web 搜索工具时,发现 Claude 不必要地把 2025 加到 query 参数后,导致搜索结果偏差和性能下降——通过改进工具描述把它引导回正轨。


与 Agent 协作

让 Agent 帮你分析结果并改进工具。把评估 Agent 的转录拼起来粘进 Claude Code。Claude 擅长分析转录、一次性重构大量工具——例如新改动后保证工具实现和描述自洽。

事实上,本文大多数建议都源自我们用 Claude Code 反复优化内部工具实现。我们的评估建立在内部工作空间之上,反映了内部工作流的复杂度(真实项目、文档、消息)。

我们用**留出测试集(held-out test sets)**确保不过拟合到"训练"评估上。这些测试集显示,即使在"专家级"工具实现(无论是研究员手写还是 Claude 自己生成的)之上,依然能挤出额外性能。


写有效工具的原则

为 Agent 选择正确的工具

更多工具不一定带来更好结果。一个常见错误是工具仅仅包装现有软件功能或 API 端点——不论是否适合 Agent。这是因为 Agent 与传统软件有不同的"affordance"——它们对可执行操作的感知方式不同。

LLM Agent 的"上下文"有限(一次能处理的信息有限),而计算机内存便宜且充裕。考虑搜索通讯录联系人的任务。传统软件能高效逐条存储和处理联系人。

但如果 LLM Agent 用一个返回所有联系人的工具,然后逐 token 读取,就在浪费有限的上下文空间在无关信息上(想象在通讯录中从头到尾翻每一页找联系人——蛮力搜索)。更好、更自然的做法(对 Agent 和人都一样)是先跳到相关页(按字母排序)。

我们建议针对高影响工作流构建少量精心设计的工具,匹配你的评估任务,再逐步扩展。在通讯录例子中,你会实现 search_contactsmessage_contact,而不是 list_contacts

工具可以整合功能,在底层处理多个离散操作(或 API 调用)。例如工具可以用相关元数据丰富响应,或在单次工具调用中处理频繁链式的多步任务。

示例:

  • 与其实现 list_userslist_eventscreate_event 三个工具,实现一个 schedule_event 工具找可用时间并安排事件
  • 与其实现 read_logs实现一个 search_logs 只返回相关日志行加少量上下文
  • 与其实现 get_customer_by_idlist_transactionslist_notes实现一个 get_customer_context 一次性整理客户最近相关信息

每个工具都应有清晰、独特的目的。工具应让 Agent 像人类那样细分和解决任务(在相同底层资源下),同时减少本来会被中间输出消耗的上下文。

太多工具或重叠工具会分散 Agent 注意力。精心选择该构建(或不构建)的工具,回报很大


为工具命名空间

Agent 可能接入数十个 MCP 服务器和数百个工具。当工具功能重叠或目的模糊,Agent 会困惑该用哪个。

命名空间(用共同前缀分组相关工具)能在大量工具间划清边界;MCP 客户端有时会默认这样做。例如按服务命名(asana_searchjira_search)和按资源命名(asana_projects_searchasana_users_search)。

前缀 vs 后缀命名对评估有非平凡影响,效果因 LLM 而异——按你自己的评估选择。

通过选择性实现命名反映任务自然细分的工具,你同时减少了 Agent 上下文中加载的工具数量和描述,把 Agent 计算从上下文卸载到工具调用本身。降低 Agent 整体出错风险


让工具返回有意义的上下文

工具实现应只把高信号信息返回给 Agent。优先上下文相关性而非灵活性,避免低级技术标识符(如 uuid256px_image_urlmime_type)。nameimage_urlfile_type 等字段更可能直接驱动 Agent 后续动作和响应。

Agent 处理自然语言的名字、术语、标识符显著优于晦涩标识符。我们发现仅仅把任意字母数字 UUID 解析为更有语义、更可解读的语言(甚至 0-索引 ID 方案),就能显著提升 Claude 在检索任务中的精确度,减少幻觉。

某些情况 Agent 需要灵活地用自然语言和技术标识符(仅为触发后续工具调用),如 search_user(name='jane')send_message(id=12345)。可以在工具中暴露 response_format 枚举参数,让 Agent 控制工具返回 "concise" 还是 "detailed" 响应:

enum ResponseFormat {
DETAILED = "detailed",
CONCISE = "concise"
}

详细响应(206 token)和简洁响应(72 token)的差距能省掉约 ⅔ 的 token。

工具响应结构(XML、JSON、Markdown)也影响评估表现:没有放之四海皆准的方案。LLM 在 next-token prediction 上训练,倾向匹配训练数据格式。最佳响应结构因任务和 Agent 而异——按自己的评估选。


优化工具响应的 token 效率

上下文质量重要,返回上下文的数量也重要

建议为可能消耗大量上下文的工具响应实现:分页、范围选择、过滤、截断(带合理默认值)。Claude Code 默认把工具响应限制在 25,000 token。Agent 有效上下文长度会随时间增长,但对上下文高效工具的需求会持续

如果选择截断响应,用有用的指令引导 Agent。可以直接鼓励 Agent 追求更省 token 的策略(比如做多次小而精的搜索而非一次广泛搜索)。同样,工具调用出错时(如输入校验失败),可以提示词工程地写错误响应——清晰传达具体可执行的改进,而不是不透明的错误码或堆栈追踪。


对工具描述做提示词工程

最有效的改进方法之一:对工具描述和规格做提示词工程。它们被加载进 Agent 上下文,可以共同引导 Agent 走向有效的工具调用行为。

写工具描述和规格时,想象你在向团队新人介绍它。考虑你可能隐含带入的上下文——专门的查询格式、小众术语定义、底层资源关系——把它们说清楚。避免歧义——清晰描述(并用严格数据模型强制)期望的输入和输出。尤其参数应明确命名:与其叫 user,叫 user_id

有了评估,你能更自信地衡量提示词工程的影响。即使工具描述的小调整也能带来显著提升。Claude Sonnet 3.5 在 SWE-bench Verified 评估上达到 SOTA,是在我们对工具描述做了精确改进后——大幅降低了错误率,提升了任务完成度。


展望

要为 Agent 构建有效工具,我们需要把软件开发实践从可预测、确定性的模式转向非确定性的模式

通过本文描述的迭代、评估驱动的过程,我们识别出有效工具的一致模式:有效工具是有意识、清晰定义的,审慎使用 Agent 上下文,可以在多样工作流中组合,让 Agent 直观地解决真实世界任务

未来 Agent 与世界交互的具体机制会演进——MCP 协议会更新,底层 LLM 也会升级。用系统性、评估驱动的方法改进工具,能确保 Agent 变强时,它们使用的工具也跟着进化。