与 Agent 一起编写高效工具

举报
yd_298479158 发表于 2026/09/16 10:41:50 2026/09/16
【摘要】 模型上下文协议(Model Context Protocol,MCP)可以让 LLM Agent 使用数百种工具来解决现实世界中的任务。但我们该如何让这些工具尽可能高效?在本文中,我们将介绍一些在多种 Agentic AI 系统中提升性能最有效的方法。我们首先会介绍如何:构建并测试工具原型创建并运行覆盖全面的工具评测随后,我们会总结在实践中提炼出的高质量工具设计原则:选择真正值得实现的工具,...

模型上下文协议(Model Context Protocol,MCP)可以让 LLM Agent 使用数百种工具来解决现实世界中的任务。但我们该如何让这些工具尽可能高效?

在本文中,我们将介绍一些在多种 Agentic AI 系统中提升性能最有效的方法。

我们首先会介绍如何:

构建并测试工具原型

创建并运行覆盖全面的工具评测

随后,我们会总结在实践中提炼出的高质量工具设计原则:

选择真正值得实现的工具,以及哪些工具不该实现

通过命名空间为工具功能划定清晰边界

让工具向 Agent 返回真正有意义的上下文

优化工具响应,提高 Token 效率

对工具描述和规格进行提示词工程

构建评测后,你就可以系统地衡量工具的性能,并针对这些评测自动优化工具。

什么是工具?

在计算机系统中,确定性系统在输入完全相同时,每次都会产生相同的输出;而像 Agent 这样的非确定性系统,即使起始条件完全一致,也可能产生不同的响应。

传统的软件开发,本质上是在确定性系统之间建立契约。例如,像 getWeather(“NYC”) 这样的函数调用,每次都会以完全相同的方式获取纽约市的天气。

工具则是一类新的软件形式,它建立的是确定性系统与非确定性 Agent 之间的契约。当用户问“我今天需要带伞吗?”时,Agent 可能调用天气工具,也可能直接根据已有知识回答,还可能先询问用户所在的位置。偶尔,Agent 甚至可能产生幻觉,或者根本没有理解该如何使用某个工具。

这意味着,在为 Agent 编写软件时,我们需要从根本上重新思考设计方式:不能再像给其他开发者或系统设计函数与 API 那样设计工具和 MCP Server,而需要真正以 Agent 为使用者来设计它们。

我们的目标,是通过工具扩大 Agent 能够有效解决问题的范围,让它们可以采用多种不同的成功策略完成各种任务。幸运的是,根据我们的经验,那些对 Agent 来说最“顺手”的工具,通常对人类来说也意外地非常直观。

如何编写工具

本节将介绍如何与 Agent 协作,既编写工具,也持续改进工具。首先快速搭建一个工具原型,并在本地进行测试。然后运行全面的评测,用来衡量后续每次修改带来的影响。与 Agent 一起反复执行“评测—改进”的循环,直到 Agent 在真实任务上达到较好的表现。

构建原型

如果你自己没有真正动手使用过工具,就很难提前判断哪些工具对 Agent 来说好用,哪些不好用。因此,可以先快速搭建一个工具原型。

如果你使用 Claude Code 编写工具,甚至尝试一次性生成整个工具,那么最好向 Claude 提供这些工具依赖的软件库、API 或 SDK 文档,其中也可能包括 MCP SDK。官方文档网站上通常会提供适合 LLM 阅读的纯文本 llms.txt 文件,例如 Anthropic API 的 llms.txt。

将工具封装进本地 MCP Server或 Desktop Extension(DXT),就可以在 Claude Code 或 Claude Desktop 中连接并测试这些工具。

要将本地 MCP Server 连接到 Claude Code,可以运行:

claude mcp add <name> <command> [args…]

要将本地 MCP Server 或 DXT 连接到 Claude Desktop,分别进入:

Settings > Developer

或:

Settings > Extensions

你应该亲自使用这些工具,以发现交互上的粗糙之处。同时收集用户反馈,逐渐建立起对实际使用场景,以及你希望工具能够支持哪些提示词和工作流的直觉。

运行评测

接下来,需要通过评测来衡量使用这些工具的效果。可以先生成大量基于真实使用场景的评测任务。我们建议让 Agent 一起参与结果分析,帮助你判断工具应该如何改进。

内部 Slack 工具在留出测试集上的表现。

生成评测任务

拿着一个早期原型,Claude Code 就可以快速探索工具,并生成几十组提示词和响应对。

这些提示词应该来源于真实使用场景,并建立在真实的数据源和服务之上,例如内部知识库和微服务。我们建议避免使用过于简单、过于表面的“沙盒”环境,因为这样的环境无法用足够复杂的任务真正压力测试工具。

一个优秀的评测任务,往往需要多次工具调用,甚至几十次调用。

下面是一些较好的任务示例:

安排下周与 Jane 的会议,讨论我们最新的 Acme Corp 项目。附上上一次项目规划会议的笔记,并预订一个会议室。

客户 ID 9182 报告说,一次购买尝试被重复扣款三次。找到所有相关日志,并判断是否还有其他客户受到了同一问题的影响。

客户 Sarah Chen 刚刚提交了取消服务请求。准备一份客户挽留方案。判断:(1)她为什么离开;(2)什么样的挽留方案最有吸引力;(3)提出方案前需要注意哪些风险因素。

下面则是一些较弱的任务:

安排下周和 jane@acme.corp 的会议。

在支付日志中搜索 purchase_complete 和 customer_id=9182。

查找客户 ID 45892 的取消请求。

每一个评测提示词,都应该配有一个可以验证的正确响应或结果。

验证器可以非常简单,例如直接比较标准答案和模型响应字符串是否一致;也可以非常复杂,例如让 Claude 来判断回答是否正确。

应避免过度严格的验证器,防止仅仅因为格式、标点或合理的替代表述不同,就把正确回答判定为错误。

对于每一组提示词和响应,你还可以选择指定你预期 Agent 在完成任务时调用哪些工具。这样,就能衡量 Agent 是否真正理解了各个工具的用途。

不过,由于同一个任务可能存在多条正确路径,因此应尽量避免把具体策略规定得太死,也不要过度拟合某一种调用方式。

运行评测

我们建议直接通过 LLM API,以程序化方式运行评测。

可以使用非常简单的 Agent 循环:用 while 循环包裹 LLM API 调用和工具调用,并让它们交替执行。每个评测任务对应一个独立循环。每个评测 Agent 只需要得到一个任务提示词,以及可用的工具。

在评测 Agent 的系统提示词中,我们建议要求 Agent 不仅输出结构化响应块以供验证,还输出推理和反馈块。

如果要求 Agent 在工具调用和最终响应之前先输出这些内容,有时能够通过触发思维链(Chain-of-Thought,CoT)行为,提高 LLM 的有效推理能力。

如果你使用 Claude 进行评测,也可以直接开启交错思考(interleaved thinking),获得类似的能力。

这样可以帮助你调查 Agent 为什么会调用某些工具,或者为什么没有调用某些工具,同时更容易发现工具描述和规格中需要改进的具体位置。

除了总体准确率,我们还建议收集其他指标,例如:

每次工具调用的总耗时

每个任务的总耗时

工具调用总次数

Token 总消耗量

工具错误数量

追踪工具调用过程,可以帮助你发现 Agent 经常采用的工作流,并找出哪些地方适合把多个操作合并到一个工具中。

内部 Asana 工具在留出测试集上的表现。

分析结果

Agent 本身也非常适合作为分析问题的合作伙伴。它们可以帮助发现从“工具描述互相矛盾”到“工具实现低效”“工具 Schema 容易混淆”等各种问题。

但需要注意的是,Agent 在反馈和响应中没有提到什么,有时比它们明确说了什么更重要。LLM 并不总是会说出自己真正的思考过程。

观察 Agent 在哪些地方卡住或感到困惑。

阅读评测 Agent 的推理和反馈,也就是它们的 CoT,寻找工具中不好用的地方。同时检查原始执行记录,包括工具调用和工具响应,以发现 Agent 的 CoT 中没有明确描述的行为。

需要学会从字里行间判断问题,因为评测 Agent 本身也未必知道正确答案或最佳策略。

还应该分析工具调用指标。

如果存在大量重复工具调用,可能说明分页参数或 Token 上限需要重新调整。如果因为参数错误而出现大量工具调用失败,那么工具描述可能需要写得更清楚,也可能需要提供更好的示例。

在推出 Claude 的网页搜索工具时,我们发现 Claude 会毫无必要地在 query 参数中附加 2025,从而对搜索结果产生偏置并降低性能。最终,我们通过改进工具描述,把 Claude 引导回了正确方向。

与 Agent 协作

你甚至可以让 Agent 直接分析评测结果,并替你改进工具。

只需把各个评测 Agent 的运行记录拼接起来,然后粘贴进 Claude Code。Claude 非常擅长分析执行记录,也擅长一次性重构大量工具。例如,当工具发生修改时,它可以帮助保证工具实现和工具描述始终保持一致。

事实上,本文中的大部分建议,都是我们使用 Claude Code 反复优化内部工具后总结出来的。

我们的评测建立在内部真实工作空间之上,尽可能复现实际工作流的复杂程度,其中包括真实项目、文档和消息。

我们还使用留出测试集,确保工具不会过拟合用于“训练”的那些评测。

这些测试集显示,即使是在“专家级”工具实现的基础上,我们仍然可以继续取得额外的性能提升。无论这些工具最初是由研究人员人工编写,还是由 Claude 自动生成,都存在进一步优化的空间。

接下来,我们将分享在这一过程中总结出的经验。

编写高效工具的原则

本节将把我们的实践经验提炼为几条设计高效工具的指导原则。

为 Agent 选择正确的工具

工具并不是越多越好。

我们经常看到的一类错误,是直接把现有软件功能或 API Endpoint 包装成工具,却没有考虑这些工具是否真的适合 Agent。

原因在于,Agent 与传统软件拥有不同的“可供性”(affordances),也就是说,它们理解和感知“自己能够使用这些工具做什么”的方式并不一样。

LLM Agent 的“上下文”是有限的,也就是说,它们一次能够处理的信息量存在上限;相比之下,计算机内存既便宜又充足。

以“从通讯录中查找一个联系人”为例。

传统软件可以高效地保存联系人列表,并一个接一个地处理这些联系人,检查完一条再继续下一条。

但是,如果一个 LLM Agent 使用的工具直接返回全部联系人,然后 Agent 必须逐 Token 阅读每一个联系人,那么它就在把有限的上下文空间浪费在大量无关信息上。

这就像你为了找通讯录里的某个人,从第一页开始逐页从上往下阅读,也就是采用暴力搜索。

对 Agent 和人类来说,更自然、更有效的做法,都是先跳到最相关的位置,例如先根据字母顺序定位到对应页面。

我们建议首先围绕少数几个影响较大的工作流,认真设计工具,并让它们与你的评测任务相匹配,然后再逐步扩展。

在通讯录这个例子里,与其实现一个 list_contacts 工具,不如实现:

search_contacts

或者:

message_contact

工具还可以把多个离散操作,也就是多个 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 Server 和数百个不同工具,其中还包括其他开发者提供的工具。

当多个工具功能重叠,或者用途模糊时,Agent 很容易搞不清应该使用哪个工具。

命名空间,也就是把相关工具统一放在共同前缀下,可以帮助明确大量工具之间的边界。有些 MCP Client 会默认做这件事。

例如,可以按服务命名:

asana_search

jira_search

也可以进一步按资源命名:

asana_projects_search

asana_users_search

这样能够帮助 Agent 在正确的时间选择正确的工具。

我们发现,在“前缀式命名空间”和“后缀式命名空间”之间选择,会对工具调用评测产生不可忽视的影响。

具体效果会因 LLM 而异,因此我们建议根据自己的评测结果来选择命名方案。

Agent 可能会:

调错工具

调对工具但传错参数

工具调用次数不足

错误处理工具返回结果

如果你只实现那些名称能够自然反映任务划分方式的工具,就可以同时达到两个目标:

一方面,减少加载到 Agent 上下文中的工具数量和工具描述数量;

另一方面,把一部分原本需要 Agent 在上下文中完成的“Agentic 计算”,转移到工具调用本身。

这样就能降低 Agent 出错的总体风险。

从工具返回真正有意义的上下文

同样地,工具实现也应该只向 Agent 返回高信号信息。

相比“提供最大的灵活性”,更应该优先考虑上下文相关性,并尽量避免返回底层技术标识符,例如:

uuid

256px_image_url

mime_type

相比之下,像下面这些字段更有可能直接帮助 Agent 决定后续操作和生成回答:

name

image_url

file_type

Agent 通常也更擅长处理自然语言形式的名称、术语和标识符,而不是难以理解的神秘 ID。

我们发现,仅仅把任意的字母数字 UUID 转换成语义更明确、更容易理解的语言,甚至只是转换成从 0 开始的编号,就能够显著提高 Claude 在检索任务中的精度,因为这样可以减少幻觉。

不过,在某些场景里,Agent 既需要自然语言信息,也需要底层技术 ID,尤其是为了继续发起后续工具调用。

例如:

search_user(name=‘jane’) → send_message(id=12345)

一种解决方式,是在工具中公开一个简单的 response_format 枚举参数,让 Agent 自己决定工具返回 “concise” 还是 “detailed” 结果。

如果想提供更高的灵活性,还可以支持更多格式,类似 GraphQL,让调用方精确选择希望接收哪些信息。

下面是一个用于控制工具响应详细程度的 ResponseFormat 枚举示例:

enum ResponseFormat {
DETAILED = “detailed”,
CONCISE = “concise”
}

下面是详细版工具响应的例子:206 Tokens。

下面是精简版工具响应的例子:72 Tokens。

Slack 线程和线程回复使用唯一的 thread_ts 标识,而获取线程回复时又必须提供这个值。

thread_ts 以及其他 ID,例如 channel_id 和 user_id,都可以通过 “detailed” 工具响应获取,从而支持那些后续必须使用这些 ID 的工具调用。

而 “concise” 工具响应只返回线程内容,不包含这些 ID。

在这个例子中,使用 “concise” 响应时,只消耗了大约三分之一的 Token。

甚至工具响应本身采用什么结构,例如 XML、JSON 或 Markdown,也可能影响评测表现,并不存在一种适用于所有任务的最佳格式。

原因在于,LLM 是通过下一个 Token 预测训练出来的,因此通常会在更接近训练数据分布的格式上表现更好。

最优的响应结构会随任务和 Agent 而发生很大变化。

因此,我们建议根据自己的评测结果,选择真正合适的响应结构。

优化工具响应的 Token 效率

优化上下文的质量非常重要。

但工具响应返回多少上下文,也就是上下文的数量,同样重要。

对于任何可能消耗大量上下文的工具响应,我们建议结合使用以下一种或多种机制,并提供合理的默认参数:

分页

范围选择

过滤

截断

对于 Claude Code,我们默认把工具响应限制在 25,000 Tokens 以内。

我们预计未来 Agent 的有效上下文长度还会继续增长,但即使如此,高上下文效率工具的需求仍然不会消失。

如果你决定截断响应,就一定要通过清晰的说明引导 Agent。

你可以直接鼓励 Agent 采用更节省 Token 的策略。

例如,在知识检索任务中,与其进行一次极其宽泛的搜索,不如执行多次规模较小、目标明确的搜索。

同样,如果工具调用发生错误,例如输入校验失败,也可以对错误响应本身进行提示词工程。

相比返回晦涩的错误码或堆栈信息,更应该清楚告诉 Agent:

错在哪里

应该如何修正

下一次应该怎样调用

下面是一个被截断的工具响应示例。

下面是一个没有帮助的错误响应示例。

下面是一个有帮助的错误响应示例。

工具截断和错误响应都可以引导 Agent 形成更节省 Token 的工具使用行为,例如使用过滤器或分页,也可以直接给出正确工具输入格式的示例。

对工具描述进行提示词工程

现在来到提升工具效果最有效的方法之一:对工具描述和规格进行提示词工程。

因为这些内容会被直接加载到 Agent 的上下文中,所以它们能够整体影响并引导 Agent 的工具调用行为。

编写工具描述和规格时,可以想象你正在向团队里的新员工解释这个工具。

需要考虑有哪些背景知识是你默认已经知道,但新员工并不知道的,并把这些知识明确写出来,例如:

特殊查询格式

小众术语的定义

底层资源之间的关系

应该尽量消除歧义,并通过严格的数据模型,清楚描述和约束预期输入与输出。

尤其是输入参数名称应该毫无歧义。

例如,与其使用:

user

不如使用:

user_id

有了评测之后,你就可以更有把握地衡量提示词工程带来的实际影响。

即使只是很小的工具描述调整,也可能产生非常明显的性能提升。

其他工具定义方面的最佳实践,可以参考我们的开发者指南。

如果你正在为 Claude 构建工具,我们也建议阅读工具如何被动态加载进 Claude 的系统提示词。

最后,如果你正在为 MCP Server 编写工具,工具注释(tool annotations)可以用来说明哪些工具需要访问开放世界资源,或者哪些工具会执行破坏性修改。

展望未来

为了构建真正高效的 Agent 工具,我们需要把软件开发思维从可预测的确定性模式,转向非确定性模式。

通过本文介绍的这种反复迭代、评测驱动的流程,我们发现高效工具通常具有一些稳定的共同特征:

它们经过有意识而清晰的定义;

它们谨慎使用 Agent 的上下文;

它们可以组合进多种不同的工作流;

它们能够让 Agent 以直觉化方式解决现实世界中的任务。

未来,我们预计 Agent 与外部世界交互的具体机制还会继续演化。

这种变化可能来自 MCP 协议本身的更新,也可能来自底层 LLM 能力的提升。

只要坚持系统化、评测驱动的工具改进方法,就能够确保随着 Agent 变得越来越强,它们所使用的工具也能同步进化。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。