这篇解决一个被普遍低估的问题:Agent 表现差,原因常常不在模型而在工具写得不好,而且工具可以像代码一样被评测和重构。
最值得拿走的是一个闭环:搭原型 → 用真实任务生成评测集 → 程序化跑分并收集工具调用指标 → 把记录丢给 Claude Code 分析并改工具 → 留出测试集防过拟合。五条原则里最容易被忽视的是「不实现某些工具」和「返回高信号上下文」——返回全部联系人式的接口会直接烧穿 Agent 的注意力预算。误用高发区是只堆工具数量,不删旧工具。
中文读者要读这篇,是因为国内 MCP 生态正在快速铺开,但「为 Agent 设计」的意识还没有跟上——大量服务器只是 API 的直译。文中用评测驱动迭代工具的思路,同样适用于国内团队的内部平台与 OpenAPI 改造。
读完做一件事:给你现有工具集补 20 条真实评测任务,跑一遍准确率作为基线。
—— FDEChina编辑部 · 实战派
Agent 的效能取决于我们给它的工具。我们分享如何编写高质量的工具与评测,以及如何让 Claude 为它自己优化工具,从而提升表现。
模型上下文协议(Model Context Protocol,MCP)可以让 LLM Agent 获得可能多达数百个的工具来完成真实世界的任务。但我们要如何让这些工具尽可能高效?
本文描述我们在各类代理式 AI 系统¹中改进表现的最有效技巧。
我们先讲如何:
- 构建并测试工具原型;
- 用 Agent 创建并运行全面的工具评测;
- 与 Claude Code 这类 Agent 协作,自动提升工具性能。
最后我们给出一路走来总结出的高质量工具编写原则:
- 选择实现哪些工具(以及不实现哪些);
- 用命名空间(namespacing)划分清晰的功能边界;
- 让工具把有意义的上下文返回给 Agent;
- 为 token 效率优化工具响应;
- 对工具描述与规格做提示工程。
搭建一套评测,能让你系统性地度量工具的表现;随后你可以用 Claude Code 对照这套评测自动优化你的工具。
什么是工具?
在计算领域,确定性系统(deterministic system)在给定相同输入时每次都产生相同输出;而非确定性系统——比如 Agent——即使初始条件相同,也可能生成不同的响应。
传统上我们写软件,是在确定性系统之间建立契约。例如 getWeather(“NYC”) 这样的函数调用,每次被调用时都会以完全相同的方式获取纽约的天气。
工具则是一种新型软件,它体现的是确定性系统与非确定性 Agent 之间的契约。当用户问「今天要带伞吗?」,Agent 可能调用天气工具,可能凭常识作答,甚至可能先反问一个关于位置的澄清问题。偶尔,Agent 还可能产生幻觉,或者根本没搞懂该怎么用某个工具。
这意味着在为 Agent 编写软件时需要从根本上重新思考:与其像为其他开发者或系统写函数和 API 那样去写工具和 MCP 服务器,我们需要为 Agent 来设计它们。
我们的目标是扩大 Agent 能有效解决问题的面:让它们借助工具尝试多种成功策略,完成广泛的任务。幸运的是,根据我们的经验,对 Agent 最「符合人体工学」(ergonomic)的工具,对人类来说也往往出奇地直观。
如何编写工具
本节描述你如何与 Agent 协作,来编写并改进你交给它们的工具。先快速搭一个工具原型并在本地测试;接着运行一次全面评测,度量后续每一处改动;与 Agent 并肩工作,你可以反复执行「评测—改进」循环,直到你的 Agent 在真实世界任务上取得出色表现。
构建原型
不亲手试一试,很难预判哪些工具对 Agent 顺手、哪些不顺手。先快速搭一个工具原型。如果你用 Claude Code 来写工具(甚至可能一次成型),把工具所依赖的软件库、API 或 SDK(可能包括 MCP SDK)的文档喂给它会很有帮助。对 LLM 友好的文档,通常可以在官方文档站的扁平 llms.txt 文件里找到(比如我们 API 的这一份)。
把你的工具包进一个本地 MCP 服务器或桌面扩展(Desktop extension,DXT),就能在 Claude Code 或 Claude Desktop 应用里连接并测试它们。
- 要把本地 MCP 服务器连接到 Claude Code,运行 claude mcp add <name> <command> [args…];
- 要把本地 MCP 服务器或 DXT 连接到 Claude Desktop 应用,分别进入 Settings > Developer 或 Settings > Extensions。
工具也可以直接传入 Anthropic API 调用,做程序化测试。
自己上手测试工具,找出毛刺;收集用户反馈,围绕工具预期支撑的用例与提示建立直觉。
运行评测
接下来,你要通过运行评测来度量 Claude 使用你工具的表现。先基于真实世界用途生成大量评测任务。我们建议与一个 Agent 协作,让它帮你分析结果、确定如何改进工具。完整流程可端到端地参见我们的工具评测 cookbook。
生成评测任务
有了早期原型,Claude Code 可以快速探索你的工具,创建数十对提示与响应(prompt-response pairs)。提示应当源于真实世界用途,并基于真实的数据源与服务(例如内部知识库和微服务)。我们建议避免过于简单、表面的「沙盒」环境——它们无法用足够的复杂度压力测试你的工具。强的评测任务可能需要多次工具调用——可能多达数十次。
下面是一些强任务的例子:
- 下周与 Jane 安排一次会议,讨论我们最新的 Acme Corp 项目。附上我们上次项目规划会议的笔记,并预订一间会议室。
- 客户 ID 9182 反馈一笔购买被扣了三次款。找出所有相关日志条目,并判断是否还有其他客户受到同一问题影响。
- 客户 Sarah Chen 刚刚提交了取消请求。准备一个挽留方案。你需要确定:(1) 她离开的原因,(2) 什么样的挽留方案最有吸引力,(3) 出方案之前我们应该注意哪些风险因素。
下面则是一些弱任务:
- 下周与 jane@acme.corp 安排一次会议。
- 在支付日志中搜索 purchase_complete 和 customer_id=9182。
- 按客户 ID 45892 查找取消请求。
每条评测提示都应配有一个可验证的响应或结果。你的验证器(verifier)可以简单到在标准答案与采样响应之间做精确字符串比较,也可以先进到让 Claude 来裁判响应。避免过于严苛的验证器:不要因为格式、标点或合理的另一种措辞这类无关差异,就把正确响应判为错误。
对每对「提示-响应」,你还可以指定你预期 Agent 完成任务时会调用哪些工具,以度量 Agent 在评测中是否成功领会了每个工具的用途。但由于正确解题的路径可能不止一条,请尽量避免过度指定、对特定策略过拟合。
运行评测
我们建议用直接的 LLM API 调用,以程序化方式运行评测。使用简单的代理循环(agentic loop,即包裹交替的 LLM API 调用与工具调用的 while 循环):每个评测任务一个循环。给每个评测 Agent 一条任务提示和你的工具。
在评测 Agent 的系统提示中,我们建议要求 Agent 不仅输出结构化响应块(供验证),还要输出推理块与反馈块。指示 Agent 在工具调用与响应块之前先输出这些内容,可能通过触发思维链(chain-of-thought,CoT)行为,提升 LLM 的有效智力。
如果你用 Claude 跑评测,可以打开交错思考(interleaved thinking),「开箱即用」地获得类似功能。这会帮助你探明 Agent 为什么调用(或不调用)某些工具,并凸显工具描述与规格中值得改进的具体位置。
除了总体准确率,我们建议收集其他指标:单个工具调用与单个任务的总耗时、工具调用总次数、token 总消耗量以及工具错误数。跟踪工具调用能揭示 Agent 倾向采取的常见工作流,也为工具合并提供了一些机会。
分析结果
从互相矛盾的工具描述,到低效的工具实现、令人困惑的工具 schema,Agent 都是你发现问题、提供反馈的好帮手。但要记住:Agent 在反馈与响应中省略的东西,往往比它说出的更重要。LLM 不总是有话直说。
观察你的 Agent 在哪里卡住或困惑。通读评测 Agent 的推理与反馈(或 CoT),找出毛刺。翻看原始记录(transcript,包括工具调用与工具响应),捕捉 CoT 里没有明说的行为。要读出言外之意;记住,你的评测 Agent 并不一定知道正确答案与正确策略。
分析你的工具调用指标。大量冗余的工具调用,可能意味着分页(pagination)或 token 上限参数需要调整;大量因参数无效导致的工具错误,可能意味着工具需要更清晰的描述或更好的示例。我们上线 Claude 的网页搜索工具时发现,Claude 会不必要地在查询参数后附加 2025,导致搜索结果偏斜、性能下降(我们通过改进工具描述把 Claude 引回了正轨)。
与 Agent 协作
你甚至可以让 Agent 替你分析结果、改进工具。只需把评测 Agent 的记录拼接起来,粘贴进 Claude Code。Claude 是分析记录、一次重构大量工具的专家——例如确保在做出新改动时,各工具的实现与描述保持自洽。
事实上,本文的多数建议,都来自我们用 Claude Code 反复优化内部工具实现的过程。我们的评测建立在我们内部工作区之上,镜像了内部工作流的复杂度,包括真实的项目、文档与消息。
我们依靠留出测试集(held-out test set)来确保没有对「训练」评测过拟合。这些测试集表明:即便超出「专家」工具实现所达到的水平,我们仍能再压榨出额外的性能提升——无论那些工具是我们的研究员手写的,还是 Claude 自己生成的。
下一节,我们将分享从这一过程中学到的一些东西。
编写高效工具的原则
本节把我们的经验浓缩成几条编写高效工具的指导原则。
为 Agent 选对工具
工具越多,结果不一定越好。我们观察到一个常见错误:工具只是把现有软件功能或 API 端点包了一层,至于它是否适合 Agent 则另当别论。这是因为 Agent 与传统软件的「可供性」(affordance)不同——它们感知「我能用这些工具做什么」的方式不一样。
LLM Agent 的「上下文」有限(即一次能处理的信息量有上限),而计算机内存又便宜又充裕。考虑在通讯录里搜索联系人的任务:传统软件程序可以高效地逐条存储和处理联系人列表,逐个检查完再处理下一个。
但如果 LLM Agent 使用一个返回全部联系人的工具,然后不得不逐个 token 地读完每一条,那就是在把有限的上下文空间浪费在无关信息上(想象你在通讯录里找联系人时,从第一页自上而下读到最后——也就是暴力搜索)。更好、也更自然的做法(对 Agent 和人类都一样)是先翻到相关的那一页(比如按字母序找到它)。
我们建议先围绕少数几个精心设计、瞄准高影响力工作流的工具做起来,与你的评测任务相匹配,再逐步扩展。在通讯录的例子里,你可以选择实现 search_contacts 或 message_contacts 工具,而不是 list_contacts 工具。
工具可以整合功能,在底层处理多个离散操作(或 API 调用)。例如,工具可以在响应中补充相关元数据,或把经常被连续执行的多步任务合并进一次工具调用。
举几个例子:
- 与其实现 list_users、list_events 和 create_event,不如实现一个 schedule_event 工具,负责查找空闲并安排日程;
- 与其实现 read_logs,不如实现一个 search_logs 工具,只返回相关日志行及其上下文;
- 与其实现 get_customer_by_id、list_transactions 和 list_notes,不如实现一个 get_customer_context 工具,一次性汇编某位客户所有近期且相关的信息。
确保你构建的每个工具都有清晰、独立的目的。工具应当让 Agent 像一个拥有相同底层资源的人类那样去拆解和解决任务,同时减少中间输出本会消耗的上下文。
工具太多或功能重叠,也会分散 Agent 的注意力,让它无法执行高效策略。对「建哪些工具、不建哪些工具」做审慎而克制的规划,回报实实在在。
给工具加命名空间
你的 AI Agent 可能会接入几十个 MCP 服务器和上百个不同的工具——包括其他开发者提供的。当工具功能重叠或用途含糊时,Agent 会搞不清该用哪个。
命名空间(namespacing,即把相关工具归入共同前缀)有助于在大量工具之间划清边界;有些 MCP 客户端默认就会这么做。例如,按服务做命名空间(asana_search、jira_search),再按资源做命名空间(asana_projects_search、asana_users_search),能帮助 Agent 在正确的时机选对工具。
我们发现,选择前缀式还是后缀式命名空间,对工具使用评测的效果有不小的影响。效果因 LLM 而异,我们鼓励你根据自己的评测来选择命名方案。
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 控制工具返回「简洁」还是「详细」的响应。
你还可以增加更多格式以获得更大灵活性,类似 GraphQL 中你可以精确选择想接收的信息。下面是一个控制工具响应详略程度的 ResponseFormat 枚举:
enum ResponseFormat {
DETAILED = "detailed",
CONCISE = "concise"
}
以 Slack 为例:线程(thread)与线程回复由唯一的 thread_ts 标识,拉取线程回复必须用到它。thread_ts 与其他 ID(channel_id、user_id)可以从「详细」响应中取得,供后续需要这些 ID 的工具调用使用;「简洁」响应只返回线程内容、不含 ID。在这个例子里,「简洁」响应只用了约三分之一的 token。
连工具响应的结构——XML、JSON 还是 Markdown——都会影响评测表现:没有放之四海而皆准的答案。这是因为 LLM 基于下一 token 预测来训练,对与其训练数据相匹配的格式往往表现更好。最优响应结构因任务与 Agent 而大不相同,我们鼓励你基于自己的评测来选择最佳响应结构。
为 token 效率优化工具响应
优化上下文的质量很重要,优化工具响应返回给 Agent 的上下文数量同样重要。
对于任何可能消耗大量上下文的工具响应,我们建议组合使用分页(pagination)、范围选择、过滤与截断(truncation),并为参数设置合理的默认值。对 Claude Code,我们默认把工具响应限制在 25,000 token。我们预计 Agent 的有效上下文长度会随时间增长,但对上下文高效工具的需求不会消失。
如果你选择截断响应,务必用有帮助的指示来引导 Agent。你可以直接鼓励 Agent 采取更 token 高效的策略,比如在知识检索任务中做多次小而有针对性的搜索,而不是一次大而全的搜索。同样,如果工具调用报错(例如输入校验失败),你可以对错误响应做提示工程,清晰地传达具体、可操作的改进建议,而不是抛出难懂的错误码或堆栈。
工具截断与错误响应,可以把 Agent 引向更 token 高效的工具使用行为(改用过滤或分页),或给出正确格式化工具输入的示例。
对工具描述做提示工程
现在来到改进工具最有效的方法之一:对工具描述与规格做提示工程。它们会被载入 Agent 的上下文,因此能共同引导 Agent 做出高效的工具调用行为。
写工具描述与规格时,设想你在向团队里一位新同事描述这个工具。把你可能默认带上的那些上下文——专用的查询格式、生僻术语的定义、底层资源之间的关系——都显式写出来。通过清晰描述(并用严格的数据模型强制约束)预期的输入与输出,消除歧义。特别地,输入参数的命名应当毫无歧义:与其叫 user,不如叫 user_id。
有了评测,你就能更有把握地度量提示工程的影响。对工具描述哪怕很小的打磨,也可能带来戏剧性的提升。在我们对工具描述做了精细修订之后,Claude Sonnet 3.5 在 SWE-bench Verified 评测上取得了当时最先进的成绩,错误率大幅下降,任务完成率显著提升。
更多工具定义的最佳实践,可参见我们的开发者指南。如果你在为 Claude 构建工具,我们还建议阅读工具是如何被动态加载进 Claude 系统提示的。最后,如果你在为 MCP 服务器编写工具,工具注解(tool annotations)可以帮助声明哪些工具需要开放世界访问、或会做出破坏性更改。
展望
要为 Agent 构建高效的工具,我们需要把软件开发实践从可预测的确定性模式,重新调整到非确定性模式上。
通过本文所述的迭代式、评测驱动的流程,我们识别出了让工具成功的稳定模式:高效的工具经过刻意且清晰的定义、审慎地使用 Agent 上下文、能在多样的工作流中彼此组合,并让 Agent 能凭直觉解决真实世界的任务。
未来,我们预期 Agent 与世界交互的具体机制会持续演化——从 MCP 协议的更新,到底层 LLM 本身的升级。只要坚持以系统化、评测驱动的方式改进 Agent 工具,我们就能确保:随着 Agent 变得更强,它们使用的工具也会随之进化。
注 ¹:指在训练底层 LLM 之外的改进手段。
延伸阅读:想深入了解工具与协议生态,参见站内 MCP 专题与 Agent 专题;工具评测的方法论可配合 FDE 实践指南中的评测内容一起读;想知道这类能力在交付各阶段的位置,可看 FDE 项目生命周期。
本文由 FDEChina 团队翻译自原文,转载已注明出处;如需引用请以原文为准。