如何打造高效AI智能体工具
文章来源:[Anthropic 工程团队]:https://www.anthropic.com/engineering/writing-tools-for-agents
智能体的效率完全取决于开发者给它们提供的工具。这篇文章分享了如何编写高质量的工具和评估方法,以及如何利用 Claude 来优化自身的工具,从而提升智能体的整体性能。
模型上下文协议 (Model Context Protocol, MCP) 可以为大型语言模型 (LLM) 智能体提供数百种工具,帮它们解决真实的任务。但问题是,怎样才能最大限度地提升这些工具的效果呢?
在这篇文章中,Anthropic 团队介绍了在各种智能体 AI 系统中¹提高性能的最有效技术。
首先,文章会探讨如何:
- 构建和测试工具原型
- 利用智能体创建并运行全面的工具评估
- 与像 Claude Code 这样的智能体协作,自动提升工具性能
最后,文章会总结在实践中发现的编写高质量工具的核心原则:
- 选择合适的工具来实现(以及哪些工具不该实现)
- 对工具进行命名空间管理,明确功能边界
- 从工具向智能体返回有意义的上下文信息
- 优化工具响应以提高令牌效率
- 精心设计工具描述和规范
建立评估机制后,开发者就可以系统地衡量工具的性能了。然后,可以使用 Claude Code 来自动优化工具以应对这些评估。
什么是工具?
在计算机领域,确定性系统在给定相同输入时总是产生相同的输出,而非确定性系统——比如智能体——即使在相同的初始条件下也可能生成不同的响应。
当开发者传统地编写软件时,其实是在确定性系统之间建立契约。比如说,一个函数调用 getWeather("NYC"
,每次调用时都会以完全相同的方式获取纽约市的天气信息。
而工具则是一种新型软件,它反映的是确定性系统与非确定性智能体之间的契约。当用户问"今天应该带伞吗?"时,智能体可能会调用天气工具,根据常识来回答,甚至可能先问问关于位置的细节。有时候,智能体可能会出现幻觉,或者压根不知道如何使用某个工具。
这意味着在为智能体编写软件时,开发者需要从根本上重新思考方法:不应该像为其他开发者或系统编写函数和 API 那样来编写工具和 MCP 服务器,而是需要专门为智能体设计它们。
目标是扩大智能体在解决各类任务时能发挥作用的范围,通过使用工具来探索各种成功的策略。幸运的是,根据 Anthropic 的经验,对智能体来说最"人性化"的工具,对人类来说往往也出奇地直观好懂。
如何编写工具
在这一部分,文章介绍了如何与智能体协作,既能编写又能改进提供给它们的工具。首先,快速搭建工具原型并在本地进行测试。接着,运行全面的评估来衡量后续的改进。开发者可以与智能体一起工作,重复评估和改进工具的过程,直到智能体在真实世界的任务中表现出色。
构建原型
在没有亲自动手之前,很难预测智能体会觉得哪些工具好用,哪些难用。所以,先快速搭建工具原型吧。如果开发者在使用 Claude Code 来编写工具(可能一次性完成),那么向 Claude 提供工具会依赖的任何软件库、API 或 SDK(包括可能的 MCP SDK)的文档会很有帮助。通常,在官方文档网站上可以找到对大型语言模型友好的文档,比如 llms.txt
文件格式(这是 Anthropic API 的文档)。
将工具封装在本地 MCP 服务器或桌面扩展 (Desktop extension, DXT) 中,这样就能在 Claude Code 或 Claude 桌面应用中连接并测试工具了。
要将本地 MCP 服务器连接到 Claude Code,运行 claude mcp add <name> <command> [args...]
。
要将本地 MCP 服务器或 DXT 连接到 Claude 桌面应用,分别导航至"设置 > 开发者"或"设置 > 扩展"。
工具还可以直接传入 Anthropic API 调用中进行程序化测试。
一定要亲自测试这些工具,找出任何不足的地方。收集用户反馈,对期望工具能够支持的使用场景和提示词建立直觉。
运行评估
接下来,需要通过运行评估来衡量 Claude 使用工具的效果。首先,基于真实世界的用途生成大量的评估任务。Anthropic 建议与智能体合作,帮助分析结果并确定如何改进工具。可以在他们的工具评估手册中查看这个端到端的过程。
Anthropic 内部 Slack 工具在测试集上的表现。
生成评估任务
通过早期原型,Claude Code 可以快速探索工具,创建数十个提示词和响应对。这些提示词应该来源于真实世界的使用场景,并基于实际的数据源和服务(比如说,内部知识库和微服务)。Anthropic 建议避免过于简化或肤浅的"沙盒"环境,这些环境无法通过足够的复杂性来充分测试工具。强大的评估任务可能需要多次工具调用,甚至可能多达数十次。
以下是一些高质量任务的例子:
- 下周与 Jane 安排一次会议,讨论最新的 Acme 公司项目。附上上次项目规划会议的笔记,并预订一间会议室。
- 客户 ID 为 9182 的用户报告,他在一次购买尝试中被重复收取了三次费用。查找所有相关的日志条目,并确定是否有其他客户受到了相同问题的影响。
- 客户 Sarah Chen 刚刚提交了取消请求。准备一份挽留方案。确定:(1) 他们离开的原因,(2) 哪种挽留方案最具吸引力,以及 (3) 在提供方案之前应该注意哪些风险因素。
以下是一些比较弱的任务例子:
- 下周与 jane@acme.corp 安排一次会议。
- 在支付日志中搜索
purchase_complete
和customer_id=9182
。 - 查找客户 ID 为 45892 的取消请求。
每个评估提示词都应该与一个可验证的响应或结果配对。验证器可以简单到只是在真实响应与抽样响应之间进行精确的字符串比较,也可以先进到让 Claude 来判断响应。请避免使用过于严格的验证器,这类验证器会因为格式、标点符号或有效的替代措辞等无关紧要的差异而拒绝正确的响应。
对于每个提示词-响应对,还可以选择指定期望智能体在解决任务时调用的工具,以衡量智能体在评估期间是否成功理解了每个工具的用途。不过,由于解决任务可能存在多种有效路径,请尽量避免过度指定或过度适应特定策略。
运行评估
Anthropic 建议通过直接调用 LLM API 以编程方式运行评估。使用简单的智能体循环(while
循环包裹交替的 LLM API 和工具调用):每个评估任务一个循环。每个评估智能体都应该获得一个单独的任务提示词和工具。
在评估智能体的系统提示词中,Anthropic 建议指示智能体不仅输出结构化的响应块(用于验证),还应该输出推理和反馈块。指示智能体在工具调用和响应块之前输出这些内容,可以通过触发思维链行为来提高 LLM 的有效智能。
如果使用 Claude 运行评估,可以开启它的交错思考功能,获得类似的"开箱即用"功能。这会帮助探究智能体调用或不调用某些工具的原因,并突出工具描述和规范中需要改进的具体领域。
除了顶级准确率,Anthropic 还建议收集其他指标,比如单个工具调用和任务的总运行时间、工具调用的总次数、总令牌消耗以及工具错误。跟踪工具调用有助于揭示智能体通常采用的工作流程,并为工具整合提供一些机会。
Anthropic 内部 Asana 工具在测试集上的表现。
分析结果
智能体是发现问题和提供反馈的好帮手,它们可以反馈从相互矛盾的工具描述到低效的工具实现再到令人困惑的工具模式等各个方面的问题。不过,请记住,智能体在其反馈和响应中遗漏的内容往往比其包含的内容更重要。大型语言模型并不总是能说出它们真正的想法。
观察智能体在哪里卡住了或感到困惑。仔细阅读评估智能体的推理和反馈(或思维链)来识别不足之处。查看原始记录(包括工具调用和工具响应),捕获智能体的思维链中未明确描述的任何行为。深入分析;记住,评估智能体不一定知道正确的答案和策略。
分析工具调用指标。大量的冗余工具调用可能表明分页或令牌限制参数需要进行适当调整;针对无效参数的大量工具错误可能表明工具需要更清晰的描述或更好的示例。当 Anthropic 推出 Claude 的网络搜索工具时,他们发现 Claude 不必要地向工具的 query
参数附加了 2025
,导致搜索结果出现偏差并降低了性能(他们通过改进工具描述把 Claude 引向了正确的方向)。
与智能体协作
开发者甚至可以让 AI 智能体来分析结果并改进工具。只需将评估智能体的记录连接起来,然后粘贴到 Claude Code 中。Claude 很擅长分析记录并一次性重构大量工具——比如说,确保在进行新更改时,工具的实现和描述保持自我一致。
事实上,这篇文章中的大部分建议都来自于 Anthropic 与 Claude Code 反复优化内部工具实现的过程。他们的评估是建立在内部工作空间之上的,反映了内部工作流程的复杂性,包括真实项目、文档和消息。
Anthropic 依靠预留的测试集来确保没有过度拟合"训练"评估。这些测试集表明,甚至可以在"专家"工具实现(无论是研究人员手动编写的工具还是 Claude 自身生成的工具)之外,进一步提取性能改进。
在下一部分中,文章会分享从这个过程中学到的一些经验。
编写高效工具的原则
在这一部分中,文章将所学的知识提炼为编写高效工具的几个指导原则。
为智能体选择合适的工具
更多的工具并不总是带来更好的结果。Anthropic 观察到的一个常见错误是,工具仅仅是现有软件功能或 API 端点的包装——不管这些工具是否适合智能体使用。这是因为智能体与传统软件具有不同的"可供性"——也就是说,它们感知与这些工具进行潜在操作的方式不同。
大型语言模型智能体的"上下文"有限(也就是说,它们一次可以处理的信息量有限),而计算机内存却便宜且充足。以在地址簿中搜索联系人为例。传统软件程序可以高效地一次存储和处理一个联系人列表,在检查完每个联系人后再继续。
但是,如果 LLM 智能体使用一个返回所有联系人的工具,然后不得不逐个令牌地读取每个联系人,它就会将有限的上下文空间浪费在不相关的信息上(想象一下在地址簿中搜索联系人,是通过从头到尾阅读每一页——也就是通过暴力搜索)。更好、更自然的方法(对智能体和人类都一样)是先跳到相关页面(也许是通过按字母顺序查找)。
Anthropic 建议,先构建一些经过深思熟虑、针对特定高影响力工作流程的工具——这些工作流程应该与评估任务相匹配——然后在此基础上逐步扩展。在地址簿的例子中,可能会选择实现一个 search_contacts
或 message_contact
工具,而不是 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
工具,它能一次性编译客户所有近期相关信息。
确保构建的每个工具都有清晰、明确的用途。工具应该使智能体能够像人类一样,在获得相同底层资源的情况下,将任务细分并解决,同时减少本应被中间输出消耗的上下文。
过多的工具或功能重叠的工具也可能分散智能体采取高效策略的注意力。精心、有选择地规划要构建(或不构建)的工具,会带来丰厚的回报。
对工具进行命名空间管理
AI 智能体最终可能会访问数十个 MCP 服务器和数百种不同的工具——其中也包括其他开发者开发的工具。当工具有功能重叠或目的模糊时,智能体可能会对使用哪个工具感到困惑。
命名空间(将相关工具分组到共同的前缀下)有助于划定大量工具之间的界限;MCP 客户端有时会默认这样做。比如,按服务(如 asana_search
、jira_search
)和按资源(如 asana_projects_search
、asana_users_search
)对工具进行命名空间管理,可以帮助智能体在正确的时间选择正确的工具。
Anthropic 发现,选择基于前缀或基于后缀的命名空间对工具使用评估产生了不可忽视的影响。效果因 LLM 而异,他们鼓励开发者根据自己的评估选择命名方案。
智能体可能会调用错误的工具,用错误的参数调用正确的工具,调用过少的工具,或者错误地处理工具响应。通过有选择地实现那些名称反映任务自然细分的工具,既可以减少加载到智能体上下文中的工具数量和工具描述,又可以将智能体计算从智能体上下文卸载回工具调用本身。这降低了智能体犯错的总体风险。
从工具中返回有意义的上下文信息
同理,工具实现应该注意只向智能体返回高价值信息。它们应该优先考虑上下文相关性而非灵活性,并避免使用低级技术标识符(比如:uuid
、256px_image_url
、mime_type
)。像 name
、image_url
和 file_type
这样的字段更有可能直接影响智能体的后续操作和响应。
智能体处理自然语言名称、术语或标识符通常比处理神秘标识符更成功。Anthropic 发现,仅仅将任意字母数字的 UUID 转换为更具语义意义和可解释的语言(甚至是 0 索引 ID 方案),就能显著提高 Claude 在检索任务中的准确性,从而减少幻觉。在某些情况下,智能体可能需要灵活性来同时处理自然语言和技术标识符输出,即便只是为了触发下游工具调用(比如, search_user(name='jane')
→ send_message(id=12345)
)。开发者可以通过在工具中暴露一个简单的 response_format
枚举参数来实现这一点,允许智能体控制工具是返回 "concise"
还是 "detailed"
响应(如下图所示)。
还可以添加更多格式以获得更大的灵活性,类似于 GraphQL,可以精确选择想要接收哪些信息。以下是一个 ResponseFormat
枚举示例,用于控制工具响应的详细程度:
enum ResponseFormat{DETAILED = "detailed",CONCISE = "concise"
}
这是一个详细工具响应的示例(206 个Token):
这是一个简洁工具响应的示例(72 个Token):
Slack 帖子和帖子回复通过唯一的 thread_ts
识别,该参数是获取帖子回复所必需的。 thread_ts
以及其他 ID(channel_id
, user_id
)可以从"detailed
"工具响应中检索,以启用需要这些 ID 的后续工具调用。"concise
"工具响应仅返回帖子内容,不包括 ID。在这个例子中,使用"concise
"工具响应可节省约三分之二的令牌。
即使是工具响应结构——比如 XML、JSON 或 Markdown——也可能对评估性能产生影响:没有一劳永逸的解决方案。这是因为 LLM 是基于下一个令牌预测进行训练的,并且在与其训练数据匹配的格式下往往表现更好。最佳响应结构会因任务和智能体而异。Anthropic 鼓励开发者根据自己的评估选择最佳响应结构。
优化工具响应以提高令牌效率
优化上下文的质量固然重要,但优化工具响应中返回给智能体的上下文数量同样不可忽视。
Anthropic 建议对任何可能消耗大量上下文的工具响应,实施分页、范围选择、过滤和/或截断的组合,并设置合理的默认参数值。对于 Claude Code,他们默认将工具响应限制为 25,000 个令牌。预计智能体的有效上下文长度会随着时间增长,但对上下文高效工具的需求会持续存在。
如果选择截断响应,请务必通过有用的指示来引导智能体。可以直接鼓励智能体采取更节省令牌的策略,比如在知识检索任务中进行多次小范围、有针对性的搜索,而不是进行一次广泛的搜索。同样,如果工具调用引发错误(比如,在输入验证期间),可以对错误响应进行提示工程,以清晰地传达具体且可操作的改进建议,而不是模糊的错误代码或堆栈跟踪。
这是一个截断工具响应的示例:
这是一个无用错误响应的示例:
这是一个有帮助错误响应的示例:
工具截断和错误响应可以引导智能体采取更节省令牌的工具使用行为(使用过滤器或分页),或者提供正确格式的工具输入示例。
精心设计工具描述
现在来到改进工具最有效的方法之一:精心设计工具描述和规范。由于这些都会加载到智能体的上下文中,它们可以共同引导智能体采取有效的工具调用行为。
在编写工具描述和规范时,可以设想如何向团队的新成员描述工具。考虑可能隐含地带来的上下文——专业的查询格式、特定术语的定义、底层资源之间的关系——并将其明确化。通过清晰地描述(并通过严格的数据模型强制执行)预期的输入和输出,避免歧义。特别是,输入参数应该命名明确:与其使用名为 user
的参数,不如尝试使用 user_id
。
通过评估,可以更自信地衡量精心设计提示词的效果。即使是工具描述上的微小改进,也能带来显著的性能提升。Claude Sonnet 3.5 在 Anthropic 对工具描述进行精确完善后,在 SWE-bench Verified 评估中取得了最先进的性能,显著降低了错误率并提高了任务完成度。