使用Agents为Agents编写有效的工具
原文:Writing effective tools for agents — with agents
时间: 2025年9月11日
阅读笔记
这篇文章,重点在于阐述为AI智能体(agents)编写高效工具时应遵循的核心原则和思维模式,而不是一份具体、详细的“手把手”实操指南。
文章的核心思想是,AI智能体是非确定性的,在为它设计工具时,要充分考虑到这个非确定性的来源,这点正是与为确定性的传统软件编写API有着本质的不同。
在文中提出了以下编写有效工具(Tools)的原则:
- 为智能体选择正确的工具: 工具并不是越多越好;提供工具时要注意返回的内容,不要返回太大量的没有实际作用的数据让智能体进行处理,而是返回有效的数据;谨慎地选择已有的工具
- 为你的工具划分命名空间: 这主要还是为了选择工具之用。智能体可能会调用错误的工具,用错误的参数调用正确的工具,调用太少的工具,或错误地处理工具响应。
- 从你的工具中返回有意义的上下文: 工具返回的内容应该简洁、信噪比高,并且对AI友好,以便于智能体理解和利用。
- 为令牌效率优化工具响应: 在编写工具时应当时刻将Token放在第一位,虽然现代的LLM上下文长度越来越大,但是太大的无效信息还是会影响Agent的处理效果。
- 对你的工具描述进行提示工程: 工具的名称、参数和描述本身就是一种“提示”,直接影响AI智能体的“推理”和调用工具的效率。
总而言之,在设计工具时,需要考虑Prompt的设计、对Token可能的使用情况、返回数据的有效性等因素,最终的目的还是要控制上下文(Context)的有效性。
智能体的效能取决于我们赋予它们的工具。在这篇文章中,我们分享了如何编写高质量的工具和评估方法,以及如何通过让 Claude 为自己优化工具来提升性能。
模型上下文协议(Model Context Protocol, MCP)可以让 LLM(大语言模型)智能体使用数百种工具来解决现实世界中的任务。但我们如何让这些工具发挥最大效用呢?
在这篇文章中,我们描述了在多种智能体AI系统中提高性能的最有效技术¹。
我们将从以下几点开始:
- 构建和测试你的工具原型
- 创建并对你的工具与智能体进行全面的评估
- 与 Claude Code 这样的智能体协作,自动提升你工具的性能
最后,我们总结了在此过程中发现的编写高质量工具的关键原则:
- 选择正确的工具进行实现(以及不实现哪些)
- 为工具进行命名空间划分,以定义清晰的功能边界
- 从工具向智能体返回有意义的上下文
- 为令牌(token)效率优化工具的响应
- 对工具的描述和规范进行提示工程(Prompt-engineering)

什么是工具?
在计算领域,确定性系统在给定相同输入的情况下每次都会产生相同的输出,而非确定性系统(non-deterministic systems)—比如智能体—即使在相同的起始条件下也可能生成不同的响应。
当我们传统地编写软件时,我们是在确定性系统之间建立一种契约。例如,像 getWeather("NYC") 这样的函数调用每次执行时都会以完全相同的方式获取纽约市的天气。
工具是一种新型软件,它反映了确定性系统与非确定性智能体之间的一种契约。当用户问“我今天需要带伞吗?”时,智能体可能会调用天气工具,也可能根据其通用知识回答,甚至可能会先问一个关于地点的澄清问题。偶尔,智能体可能会产生幻觉,甚至无法理解如何使用某个工具。
这意味着在为智能体编写软件时,我们需要从根本上重新思考我们的方法:我们不能像为其他开发者或系统编写函数和API那样来编写工具和MCP服务器,而是需要为智能体设计它们。
我们的目标是扩大智能体能够有效解决各类任务的范围,通过使用工具来追求多种成功的策略。幸运的是,根据我们的经验,那些对智能体最“符合人体工程学”的工具,最终对人类来说也出人意料地直观易懂。
如何编写工具
在本节中,我们描述了你如何可以与智能体协作,既编写工具,也改进你提供给它们的工具。首先,快速构建一个工具原型并在本地进行测试。接下来,进行全面的评估以衡量后续的改动。与智能体一起工作,你可以重复评估和改进工具的过程,直到你的智能体在真实世界的任务中达到强大的性能。
构建原型
在没有亲身实践的情况下,很难预见哪些工具智能体会觉得好用,哪些不会。从快速构建你的工具原型开始。如果你正在使用 Claude Code 编写工具(可能是一次性完成),为 Claude 提供你的工具将依赖的任何软件库、API或SDK(包括可能是MCP SDK)的文档会很有帮助。对LLM友好的文档通常可以在官方文档网站的扁平 llms.txt 文件中找到(这是我们API的例子)。
llms.txt的重要性
llms.txt可以理解为网站为AI准备的“站点地图”,类似于 robots.txt 或 sitemap.xml 之于传统搜索引擎的角色。
llms.txt 的核心作用是为大型语言模型提供一个清晰、简洁且对机器友好的网站内容指南。它解决了AI在直接处理标准网页时遇到的几个关键问题:
- 减少噪音,提升效率:传统网页富含HTML、JavaScript、CSS以及各种导航、广告等元素,这些对于LLM理解核心内容是“噪音”。llms.txt 文件会链接到页面的纯文本(Markdown)版本,让AI能够直接获取干净、结构化的内容,从而更高效、准确地进行处理。
- 引导AI关注核心内容:网站所有者可以通过 llms.txt 文件,明确地告诉AI哪些页面是网站最重要、最有价值的。这确保了AI在进行问答、生成摘要或执行任务时,能够基于最相关和最准确的信息。
- 优化上下文窗口的使用:大型语言模型的上下文窗口(即一次能处理的信息量)是有限的。通过提供一个精炼的内容版本,llms.txt 帮助AI在有限的窗口内装入更多实质性信息,从而进行更复杂的推理和执行更深入的任务。
- 成为AI智能体的“说明书”:对于AI智能体(Agents)而言,llms.txt 就像是一个说明书或索引,可以引导它们快速找到执行任务所需的文档、API参考或其他关键信息。例如,一个编程助手可以通过解析软件库的 llms.txt 文件,迅速定位到相关的函数说明和代码示例。
这个概念由FastAI的创始人Jeremy Howard在2024年9月提出,此后迅速获得了业界的关注和采纳[2][8][9][13]。虽然它还很新,但已经被许多开发者工具和AI公司所支持。
将你的工具包装在本地MCP服务器或桌面扩展(DXT)中,将允许你在 Claude Code 或 Claude 桌面应用中连接和测试你的工具。
- 要将你的本地MCP服务器连接到 Claude Code,运行
claude mcp add <name> <command> [args...]。 - 要将你的本地MCP服务器或DXT连接到 Claude 桌面应用,分别导航到
设置 > 开发者或设置 > 扩展。 - 工具也可以直接传递到 Anthropic API 调用中进行程序化测试。
自己测试这些工具以发现任何不顺畅的地方。收集用户的反馈,围绕你期望工具能够支持的用例和提示建立直觉。
进行评估
接下来,你需要通过进行评估来衡量 Claude 使用你工具的情况。首先,根据真实世界的使用场景生成大量的评估任务。我们建议与一个智能体合作,帮助分析你的结果并确定如何改进你的工具。在我们的工具评估指南中可以看到这个端到端的流程。

生成评估任务
借助你的早期原型,Claude Code 可以快速探索你的工具并创建数十个提示和响应对。提示应该受到真实世界使用的启发,并基于现实的数据源和服务(例如,内部知识库和微服务)。我们建议你避免过于简单化或肤浅的“沙箱”环境,因为这些环境无法以足够的复杂性来对你的工具进行压力测试。优秀的评估任务可能需要多次——甚至数十次——工具调用。
以下是一些优秀任务的例子:
- 安排下周与 Jane 开会,讨论我们最新的 Acme Corp 项目。附上我们上次项目规划会议的纪要,并预订一间会议室。
- 客户ID 9182 报告说,他们的一次购买尝试被收取了三次费用。找到所有相关的日志条目,并确定是否有其他客户受到同一问题的影响。
- 客户 Sarah Chen 刚刚提交了取消请求。准备一份挽留方案。确定:(1) 他们离开的原因,(2) 哪种挽留方案最具吸引力,以及 (3) 在提出方案前我们应该注意的任何风险因素。
而以下是一些较弱任务的例子:
- 安排下周与
[email protected]开会。 - 在支付日志中搜索
purchase_complete和customer_id=9182。 - 按客户ID
45892查找取消请求。
每个评估提示都应与一个可验证的响应或结果配对。你的验证器可以简单到只是对基准真相(ground truth)和采样响应进行精确的字符串比较,也可以高级到让 Claude 来评判响应。避免使用过于严格的验证器,它们会因为格式、标点或有效的替代措辞等无关紧要的差异而拒绝正确的响应。
对于每个提示-响应对,你还可以选择性地指定你期望智能体在解决任务时调用的工具,以衡量智能体在评估期间是否成功掌握了每个工具的用途。然而,由于可能有多种有效途径来正确解决任务,请尽量避免过度指定或对特定策略过拟合。
运行评估
我们建议使用直接的 LLM API 调用以编程方式运行你的评估。使用简单的智能体循环(while 循环包裹交替的 LLM API 和工具调用):每个评估任务一个循环。每个评估智能体都应被给予一个任务提示和你的工具。
在你的评估智能体的系统提示中,我们建议指示智能体不仅输出结构化的响应块(用于验证),还要输出推理和反馈块。指示智能体在工具调用和响应块之前输出这些内容,可能会通过触发思维链(Chain-of-Thought, CoT)行为来提高 LLM 的有效智能。
如果你正在使用 Claude 进行评估,你可以开启交错思考(interleaved thinking)功能以获得类似“开箱即用”的功能。这将帮助你探究智能体为什么会(或不会)调用某些工具,并突出工具描述和规范中需要改进的具体领域。
除了顶层准确性,我们还建议收集其他指标,如单个工具调用和任务的总运行时间、工具调用总数、总令牌消耗量和工具错误。跟踪工具调用可以帮助揭示智能体追求的常见工作流程,并为工具的整合提供一些机会。

分析结果
智能体是你发现问题并提供反馈的得力伙伴,反馈内容涵盖了从矛盾的工具描述到低效的工具实现和令人困惑的工具模式等方方面面。然而,请记住,智能体在其反馈和响应中省略的内容往往比它们包含的内容更重要。LLM 并不总是说出它们的真实想法。
观察你的智能体在哪里卡住或感到困惑。通读你的评估智能体的推理和反馈(或 CoT)以发现不顺畅之处。审查原始的交互记录(包括工具调用和工具响应)以捕捉智能体 CoT 中未明确描述的任何行为。要读懂言外之意;记住你的评估智能体不一定知道正确的答案和策略。
分析你的工具调用指标。大量的冗余工具调用可能表明需要对分页或令牌限制参数进行一些调整;无效参数导致的工具错误频发可能表明工具需要更清晰的描述或更好的示例。当我们推出 Claude 的网页搜索工具时,我们发现 Claude 会不必要地在工具的查询参数(query)中附加 2025,这导致搜索结果出现偏差并降低了性能(我们通过改进工具描述引导 Claude 回到正确的方向)。
与智能体协作
你甚至可以让智能体为你分析结果并改进你的工具。只需将你的评估智能体的交互记录连接起来,然后粘贴到 Claude Code 中。Claude 是分析交互记录和一次性重构大量工具的专家——例如,在进行新更改时确保工具的实现和描述保持自洽。
事实上,这篇文章中的大部分建议都来自于我们用 Claude Code 反复优化内部工具实现的过程。我们的评估是在我们的内部工作空间之上创建的,反映了我们内部工作流程的复杂性,包括真实的项目、文档和消息。
我们依赖留出的测试集来确保我们没有对我们的“训练”评估过拟合。这些测试集揭示了,即使是超越了我们通过“专家级”工具实现(无论是我们研究人员手动编写的还是由 Claude 自己生成的)所达到的性能,我们仍然可以获得额外的性能提升。
在下一节中,我们将分享我们从这个过程中学到的一些东西。
编写有效工具的原则
在本节中,我们将我们的学习经验提炼为几个编写有效工具的指导原则。
本节是这篇文章的核心
在本节中读到的这几种原则,核心还是针对上下文(Context)的管理,所以在编写Tools时,要时刻想着Token数据、上下文大小(Context Length),工具的选择、设计以及返回数据的经验也是从上下文和Token进行考虑的!
为智能体选择正确的工具
更多的工具并不总是带来更好的结果。我们观察到的一个常见错误是,有些工具仅仅是包装了现有的软件功能或API端点——而不管这些工具是否适合智能体。这是因为智能体相对于传统软件具有不同的“可供性(affordances)”——也就是说,它们感知可以利用这些工具采取的潜在行动的方式不同。
LLM 智能体的“上下文”有限(即,它们一次能处理的信息量有限),而计算机内存则便宜且充裕。考虑在地址簿中搜索联系人的任务。传统软件程序可以高效地存储和逐个处理联系人列表,检查每一个后再移动到下一个。
然而,如果一个 LLM 智能体使用的工具返回了所有联系人,然后它必须逐个令牌地阅读每一个,那么它就是在浪费其有限的上下文空间于不相关的信息上(想象一下通过从头到尾阅读每一页来在地址簿中查找联系人——也就是通过暴力搜索)。更好、更自然的方法(对智能体和人类都一样)是先跳到相关的页面(也许是按字母顺序查找)。
我们建议构建几个精心设计的工具,针对特定的高影响力工作流程,这些工作流程与你的评估任务相匹配,并从此基础上扩展。在地址簿的例子中,你可能会选择实现一个 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工具,它一次性编译一个客户所有近期和相关的信息。
这里反映的从传统API开发到Agent开发的核心差别
传统的API开发中,基本的增删改查(CRUD)的思路在Agent开发中会产生大量的无用数据,因为现在Agent已经不需要再先显示一个列表,让用户选择,处理流程已经更新,在这种情况下,应当按业务的含义,尽可能准确地一次(或少次)交到返回直接有用的数据,避免太多无效数据污染上下文空间,从而增加LLM的幻觉,选择结果的更大的不确定性。
确保你构建的每个工具都有一个清晰、独特的目标。工具应该使智能体能够像人类一样,在拥有相同底层资源的情况下,细分和解决任务,并同时减少中间输出本会消耗的上下文。
过多的工具或功能重叠的工具也可能分散智能体的注意力,使其无法追求高效的策略。谨慎、有选择地规划你构建(或不构建)的工具,真的可以带来回报。
为你的工具划分命名空间
你的 AI 智能体可能会接触到数十个 MCP 服务器和数百个不同的工具——包括其他开发者开发的。当工具功能重叠或目的模糊时,智能体可能会对使用哪个工具感到困惑。
命名空间(将相关工具按共同的前缀分组)可以帮助在众多工具之间划定界限;MCP 客户端有时会默认这样做。例如,按服务(如 asana_search、jira_search)和按资源(如 asana_projects_search、asana_users_search)对工具进行命名空间划分,可以帮助智能体在正确的时间选择正确的工具。
我们发现在基于前缀和后缀的命名空间之间进行选择,对我们的工具使用评估有不可忽视的影响。效果因 LLM 而异,我们鼓励你根据自己的评估选择命名方案。
智能体可能会调用错误的工具,用错误的参数调用正确的工具,调用太少的工具,或错误地处理工具响应。通过有选择地实现其名称反映了任务自然细分的工具,你同时减少了加载到智能体上下文中的工具和工具描述的数量,并将智能体的计算从其上下文中转移回工具调用本身。这降低了智能体犯错的总体风险。
从你的工具中返回有意义的上下文
同样地,工具的实现应该注意只向智能体返回高信号价值的信息。它们应该优先考虑上下文相关性而不是灵活性,并避免使用低级技术标识符(例如:uuid、256px_image_url、mime_type)。像 name、image_url 和 file_type 这样的字段更有可能直接为智能体的下游行动和响应提供信息。
智能体处理自然语言名称、术语或标识符也比处理神秘标识符要成功得多。我们发现,仅仅将任意的字母数字 UUID 解析为更具语义意义和可解释性的语言(甚至是0索引的ID方案),通过减少幻觉,显著提高了 Claude 在检索任务中的精确度。
在某些情况下,智能体可能需要灵活地与自然语言和技术标识符输出进行交互,哪怕只是为了触发下游的工具调用(例如,search_user(name='jane') → send_message(id=12345))。你可以通过在工具中暴露一个简单的 response_format 枚举参数来同时启用这两种方式,让你的智能体控制工具是返回“简洁”还是“详细”的响应(如下图所示)。
你可以添加更多格式以获得更大的灵活性,类似于 GraphQL,你可以精确选择你想要接收的信息片段。以下是一个控制工具响应详细程度的 ResponseFormat 枚举示例:
1 | enum ResponseFormat { |
这是一个详细工具响应的例子(206个令牌):

这是一个简洁工具响应的例子(72个令牌):

甚至你的工具响应结构——例如 XML、JSON 或 Markdown——也可能对评估性能产生影响:没有一刀切的解决方案。这是因为 LLM 是基于下一个令牌预测进行训练的,并且倾向于在与其训练数据相匹配的格式上表现更好。最优的响应结构将根据任务和智能体而大相径庭。我们鼓励你根据自己的评估选择最佳的响应结构。
为令牌效率优化工具响应
优化上下文的质量很重要,但优化工具响应中返回给智能体的上下文数量也同样重要。
我们建议为任何可能占用大量上下文的工具响应实现某种组合的分页、范围选择、过滤和/或截断,并设置合理的默认参数值。对于 Claude Code,我们默认将工具响应限制在 25,000 个令牌。我们预计智能体的有效上下文长度会随着时间增长,但对上下文高效工具的需求将持续存在。
如果你选择截断响应,请务必用有用的指令来引导智能体。你可以直接鼓励智能体追求更节省令牌的策略,例如在知识检索任务中进行多次小范围、有针对性的搜索,而不是一次大范围的搜索。同样,如果工具调用引发错误(例如,在输入验证期间),你可以通过提示工程来设计你的错误响应,以清晰地传达具体且可操作的改进建议,而不是不透明的错误代码或追溯信息。
这是一个截断的工具响应示例:

这是一个无益的错误响应示例:

这是一个有益的错误响应示例:

对你的工具描述进行提示工程
我们现在谈到改进工具最有效的方法之一:对你的工具描述和规范进行提示工程。因为这些内容会被加载到你的智能体上下文中,它们可以共同引导智能体采取有效的工具调用行为。
在编写工具描述和规范时,想象一下你会如何向团队中的新成员描述你的工具。考虑你可能不言自明带来的上下文——专门的查询格式、小众术语的定义、底层资源之间的关系——并将其明确化。通过清晰地描述(并用严格的数据模型强制执行)预期的输入和输出来避免歧义。特别是,输入参数应该被明确命名:不要用名为 user 的参数,而是尝试用名为 user_id 的参数。
通过你的评估,你可以更有信心地衡量你的提示工程的影响。即使是对工具描述的微小改进也能产生显著的提升。在我们对工具描述进行精确改进后,Claude Sonnet 3.5 在 SWE-bench Verified 评估中取得了最先进的性能,大幅降低了错误率并提高了任务完成率。
你可以在我们的开发者指南中找到其他关于工具定义的最佳实践。如果你正在为 Claude 构建工具,我们还建议阅读关于工具如何被动态加载到 Claude 的系统提示中。最后,如果你正在为 MCP 服务器编写工具,工具注解有助于揭示哪些工具需要访问开放世界或会进行破坏性更改。
Prompt Engineering还是非常重要
Prompt Engineering并没有过时,它只是成为了Agent开发的基本技能。
展望未来
为了给智能体构建有效的工具,我们需要将我们的软件开发实践从可预测的、确定性的模式重新定位到非确定性的模式。
通过本文中描述的迭代、评估驱动的过程,我们确定了使工具成功的持续模式:有效的工具被有意且清晰地定义,审慎地使用智能体上下文,可以在多样化的工作流程中组合使用,并使智能体能够直观地解决现实世界的任务。
在未来,我们预计智能体与世界交互的具体机制将会演变——从 MCP 协议的更新到基础 LLM 本身的升级。通过系统化的、评估驱动的方法来改进为智能体设计的工具,我们可以确保随着智能体变得越来越强大,它们使用的工具也将随之进化。