为智能体编写有效工具:MCP工具开发完全指南

为智能体编写有效工具:MCP工具开发完全指南

为智能体编写有效工具

智能体的效果取决于我们为它们提供的工具。本指南将分享如何编写高质量的工具和评估,以及如何通过Claude等AI助手来优化工具性能。

Model Context Protocol (MCP) 可以为LLM智能体提供数百种工具来解决现实世界的任务。但是,我们如何让这些工具发挥最大效果呢?

在本指南中,我们将介绍在各种智能体AI系统中提升性能的最有效技术。

工具的本质

在计算机科学中,确定性系统在相同输入下总是产生相同输出,而非确定性系统(如智能体)即使在相同的起始条件下也可能产生不同的响应。

当我们编写传统软件时,我们在确定性系统之间建立契约。例如,像 getWeather("NYC") 这样的函数调用每次都会以完全相同的方式获取纽约市的天气。

工具是一种新型软件,它反映了确定性系统与非确定性智能体之间的契约。当用户问"我今天应该带伞吗?“时,智能体可能会调用天气工具、从一般知识中回答,甚至首先询问位置的澄清问题。偶尔,智能体可能会产生幻觉,甚至无法理解如何使用工具。

这意味着我们在为智能体编写软件时需要从根本上重新思考我们的方法:我们不能像为其他开发者或系统编写函数和API那样编写工具和MCP服务器,而需要专门为智能体设计它们。

编写工具的方法

构建原型

在不亲自动手的情况下,很难预测智能体会发现哪些工具人性化,哪些不会。首先建立工具的快速原型。

如果你使用Claude Code来编写工具,提供任何软件库、API或SDK(包括可能的MCP SDK)的文档会很有帮助。LLM友好的文档通常可以在官方文档站点的扁平llms.txt文件中找到。

将你的工具包装在本地MCP服务器中,这将允许你在Claude Code或Claude Desktop应用中连接和测试你的工具。

要将本地MCP服务器连接到Claude Code,运行 claude mcp add <name> <command> [args...]

工具也可以直接传递到Anthropic API调用中进行程序化测试。

自己测试工具以识别任何粗糙的边缘。收集用户反馈,建立对你期望工具启用的用例和提示的直觉。

运行评估

接下来,你需要通过运行评估来衡量Claude使用你的工具的效果。首先生成大量基于现实世界用途的评估任务。我们建议与智能体协作来帮助分析结果并确定如何改进工具。

参考工具评估cookbook查看完整的端到端流程。

Claude Code工具评估流程 通过Claude Code构建评估系统,可以系统化地衡量和优化工具性能

工具性能对比案例

通过系统化的评估驱动开发,我们在内部工具测试中取得了显著的性能提升:

Slack工具性能测试结果 Slack MCP工具的测试集准确率对比:人工编写 vs Claude优化版本

Asana工具性能测试结果
Asana MCP工具的测试集准确率对比:展示了AI优化工具的显著效果

生成评估任务

使用早期原型,Claude Code可以快速探索你的工具并创建数十个提示和响应对。提示应该受到现实世界用途的启发,并基于真实的数据源和服务。我们建议避免过于简单或肤浅的"沙盒"环境,这些环境无法用足够的复杂性对工具进行压力测试。

强评估任务示例:

  • 安排下周与Jane的会议讨论我们最新的Acme Corp项目。附上我们上次项目规划会议的笔记并预订会议室。
  • 客户ID 9182报告他们为一次购买尝试被收费三次。找到所有相关的日志条目并确定是否有其他客户受到相同问题的影响。
  • 客户Sarah Chen刚刚提交了取消请求。准备一个留存优惠。确定:(1)他们为什么要离开,(2)什么样的留存优惠最有吸引力,(3)在提出优惠之前我们应该了解的任何风险因素。

弱评估任务示例:

  • 安排下周与[email protected]的会议。
  • 在支付日志中搜索purchase_completecustomer_id=9182
  • 通过客户ID 45892找到取消请求。

每个评估提示都应该与可验证的响应或结果配对。你的验证器可以简单到在真实情况和采样响应之间进行精确字符串比较,也可以高级到请Claude来判断响应。

运行评估

我们建议通过直接LLM API调用程序化地运行评估。使用简单的智能体循环(包装交替LLM API和工具调用的while循环):每个评估任务一个循环。

在评估智能体的系统提示中,我们建议指示智能体不仅输出结构化响应块(用于验证),还输出推理和反馈块。指示智能体在工具调用和响应块之前输出这些可能会通过触发思维链(CoT)行为来增加LLM的有效智能。

分析结果

智能体是你在发现问题和提供反馈方面的有用伙伴,从矛盾的工具描述到低效的工具实现和令人困惑的工具模式。但是,请记住,智能体在反馈和响应中遗漏的内容往往比它们包含的内容更重要。

观察你的智能体在哪里感到困惑或混乱。阅读评估智能体的推理和反馈(或CoT)以识别粗糙的边缘。审查原始记录(包括工具调用和工具响应)以捕获智能体CoT中未明确描述的任何行为。

与智能体协作

你甚至可以让智能体为你分析结果并改进工具。只需将评估智能体的记录连接起来并粘贴到Claude Code中。Claude是分析记录和同时重构大量工具的专家——例如,确保在进行新更改时工具实现和描述保持自一致。

实际上,本文中的大部分建议都来自于与Claude Code反复优化我们内部工具实现。我们依靠留出的测试集来确保我们没有过度拟合我们的"训练"评估。

编写有效工具的原则

为智能体选择正确的工具

更多工具并不总是导致更好的结果。我们观察到的一个常见错误是工具仅仅包装现有的软件功能或API端点——无论工具是否适合智能体。

LLM智能体具有有限的"上下文”(即它们一次可以处理的信息量有限),而计算机内存便宜且丰富。考虑在地址簿中搜索联系人的任务。传统软件程序可以高效地存储和处理联系人列表,一次检查一个。

但是,如果LLM智能体使用返回所有联系人的工具,然后必须逐个token地阅读每个联系人,它就是在无关信息上浪费其有限的上下文空间。更好、更自然的方法是首先跳到相关页面。

我们建议构建一些针对特定高影响工作流程的周到工具,这些工具与你的评估任务相匹配,然后从那里扩展。

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

更好的工具设计示例:

  • 不实现list_userslist_eventscreate_event工具,考虑实现一个schedule_event工具,它找到可用性并安排事件。
  • 不实现read_logs工具,考虑实现一个search_logs工具,它只返回相关的日志行和一些周围的上下文。
  • 不实现get_customer_by_idlist_transactionslist_notes工具,实现一个get_customer_context工具,它一次性编译客户的所有最近和相关信息。

工具命名空间

你的AI智能体可能会访问数十个MCP服务器和数百个不同的工具——包括其他开发者的工具。当工具在功能上重叠或目的模糊时,智能体可能会对使用哪些工具感到困惑。

命名空间(在公共前缀下分组相关工具)可以帮助在大量工具之间划定边界;MCP客户端有时默认这样做。例如,按服务(如asana_searchjira_search)和按资源(如asana_projects_searchasana_users_search)对工具进行命名空间,可以帮助智能体在正确的时间选择正确的工具。

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

同样,工具实现应该注意只向智能体返回高信号信息。它们应该优先考虑上下文相关性而非灵活性,并避免低级技术标识符(例如:uuid256px_image_urlmime_type)。像nameimage_urlfile_type这样的字段更可能直接影响智能体的下游操作和响应。

智能体也倾向于比处理神秘标识符更成功地处理自然语言名称、术语或标识符。我们发现,仅仅将任意的字母数字UUID解析为更语义上有意义和可解释的语言显著改善了Claude在检索任务中的精度。

在某些情况下,智能体可能需要与自然语言和技术标识符输出交互的灵活性,如果只是为了触发下游工具调用(例如,search_user(name='jane')send_message(id=12345))。你可以通过在工具中公开一个简单的response_format枚举参数来启用两者,允许智能体控制工具是否返回"concise""detailed"响应。

response_format.py
from enum import Enum

class ResponseFormat(Enum):
    DETAILED = "detailed"
    CONCISE = "concise"

响应格式对比示例

详细响应格式(206个token): 详细工具响应示例

简洁响应格式(72个token): 简洁工具响应示例

Slack线程和线程回复通过唯一的thread_ts标识,这是获取线程回复所必需的。thread_ts和其他ID(channel_iduser_id)可以从"详细"工具响应中获取以启用需要这些的进一步工具调用。“简洁"工具响应只返回线程内容并排除ID。在此示例中,使用"简洁"工具响应只消耗约⅓的token。

为token效率优化工具响应

优化上下文的质量很重要。但优化返回给智能体的工具响应中的上下文数量也同样重要。

我们建议为任何可能使用大量上下文的工具响应实现分页、范围选择、过滤和/或截断的某种组合,并使用合理的默认参数值。对于Claude Code,我们默认将工具响应限制为25,000个token。

如果选择截断响应,请确保用有用的指令引导智能体。你可以直接鼓励智能体追求更token效率的策略,比如进行许多小的、有针对性的搜索,而不是为知识检索任务进行单一的、广泛的搜索。

响应截断和错误处理示例

截断响应示例: 截断工具响应示例 智能引导智能体进行更精确的搜索

错误响应对比:

无用的错误响应: 无用错误响应示例

有用的错误响应: 有用错误响应示例

工具截断和错误响应可以引导智能体朝向更token效率的工具使用行为(使用过滤器或分页)或提供正确格式化工具输入的示例。

工具描述的提示工程

我们现在来到改进工具的最有效方法之一:对工具描述和规范进行提示工程。因为这些被加载到智能体的上下文中,它们可以集体引导智能体朝向有效的工具调用行为。

在编写工具描述和规范时,想想你如何向团队中的新员工描述你的工具。考虑你可能隐含地带来的上下文——专门的查询格式、小众术语的定义、底层资源之间的关系——并使其明确。

通过评估,你可以更有信心地衡量提示工程的影响。即使对工具描述的小改进也可能产生戏剧性的改进。

避免歧义,通过清楚地描述(并用严格的数据模型强制执行)预期的输入和输出。特别是,输入参数应该明确命名:不是名为user的参数,而是名为user_id的参数。

实践建议

工具开发工作流

  1. 快速原型 - 开始时构建简单的工具原型
  2. 用户测试 - 自己测试工具,收集反馈
  3. 创建评估 - 基于真实用例生成评估任务
  4. 运行测试 - 程序化地运行评估并收集指标
  5. 分析结果 - 与智能体协作分析结果
  6. 迭代改进 - 基于反馈优化工具
  7. 重复循环 - 持续改进直到达到强性能

常见陷阱避免

避免这些常见错误:

  • 为每个API端点创建一个工具
  • 返回过多的低级技术细节
  • 使用模糊或重叠的工具名称
  • 忽略工具描述的重要性
  • 不测试实际的智能体工作流程

性能优化技巧

  • 整合相关功能 - 将常用的工具链合并为单个工具
  • 智能默认值 - 为参数设置合理的默认值
  • 错误处理 - 提供清晰、可操作的错误消息
  • 响应格式 - 支持详细和简洁的响应模式
  • 上下文管理 - 优化返回信息的相关性和数量

总结

构建有效的智能体工具需要从传统的软件开发实践重新定向到非确定性模式。通过我们在本文中描述的迭代、评估驱动的过程,我们已经确定了使工具成功的一致模式:

有效的工具具有以下特征:

  • 有意图地和清晰地定义
  • 明智地使用智能体上下文
  • 可以在多样化的工作流程中组合使用
  • 能够让智能体直觉地解决现实世界的任务

随着智能体变得更加能干,它们使用的工具也将与它们一起演进。通过系统性的、评估驱动的方法来改进智能体工具,我们可以确保随着技术的发展,工具和智能体能够协同进步。

相关资源