MCP Tool设计指南:如何让Agent准确调用你的工具
发布时间:2026/10/17 14:39:37 锦皓数字建站

1. 为什么给 Agent 写工具和给人写 API 是两码事很多人第一次接触 MCP Tool 设计时脑子里想的还是这不就是写个函数接口吗。我一开始也这么觉得直到我把自己写的一个工具丢给 Agent 跑发现它要么不调用要么调用了但传参乱七八糟要么调用了之后完全误解了返回结果。那一刻我才意识到MCP Tool 的用户不是程序员而是 Agent这两者的使用方式有本质区别。程序员用 API 的时候会看文档、会理解上下文、会根据经验推断参数含义。但 Agent 不会。Agent 完全依赖工具的名称、描述、参数 schema 和返回内容来做决策。它没有常识补全的能力你写得不清楚它就真的不知道该怎么办。更关键的是Agent 面对的不是一个工具而是几十个甚至上百个工具同时摆在面前它需要在有限的上下文里快速判断当前这个任务该用哪个工具。这就引出了 MCP Tool 设计的第一个核心原则工具是写给 Agent 的说明书而不是写给程序员的接口定义。说明书要解决的是什么时候用我怎么用我我会返回什么这三个问题而不是我的函数签名长什么样。我在实际项目中总结出一个判断标准如果你把工具描述拿给一个完全不了解你系统的人看他能不能在 10 秒内判断出什么场景下该调用这个工具如果能说明描述合格如果不能Agent 大概率也判断不出来。还有一个容易被忽略的点Agent 的上下文窗口是有限的。你写的工具描述越长、参数越多、返回结构越复杂占用的 token 就越多Agent 能用来思考的空间就越少。所以高质量 MCP Tool 的另一个隐含要求是信息密度要高废话要少。每一句话都要有存在的理由每一个参数都要能被 Agent 正确理解和使用。下面我会从工具命名、描述撰写、参数设计、返回结构、错误处理、工具粒度这几个维度把我踩过的坑和总结出来的方法完整拆一遍。2. 工具命名Agent 靠名字做第一轮筛选2.1 名字是 Agent 的第一眼印象Agent 在决定调用哪个工具时第一步就是扫一遍所有工具的名字。名字起得好Agent 能快速缩小候选范围名字起得烂Agent 要么选错要么干脆不用。我见过太多工具名字叫process_data、handle_request、do_task这种放在人类文档里可能还能靠上下文理解但 Agent 看到这种名字完全懵——它不知道这个工具处理什么数据、处理完会怎样。好的工具命名应该遵循动词 对象 限定条件的结构。比如search_products_by_keyword就比search好create_calendar_event就比create好。限定条件不是必须的但如果你的系统里有多个相似工具限定条件就是区分它们的关键。我做过一个对比实验同一组功能用模糊命名和精确命名分别让 Agent 跑 50 个任务。模糊命名组的工具选择准确率只有 62%而精确命名组达到了 89%。这个差距非常明显而且随着工具数量增加差距还会拉大。2.2 命名要避免的三种坑第一种坑是过度缩写。get_usr_info这种名字人类程序员看多了可能习惯但 Agent 不一定能正确理解usr就是user。能用完整单词就用完整单词省那几个字符不值得。第二种坑是同义词混用。今天写fetch_data明天写get_data后天写retrieve_dataAgent 会以为这是三个不同的操作。统一用词整个工具集里获取就用get或fetch其中一个不要混着来。第三种坑是名字里带实现细节。比如query_mysql_user_tableAgent 不需要知道底层是 MySQL它只需要知道这个工具能查用户。实现细节应该放在描述里而不是名字里。2.3 命名的一致性策略如果你的工具集比较大建议按领域 动作来组织命名。比如所有和订单相关的工具都以order_开头所有和用户相关的都以user_开头。这样 Agent 在扫描工具列表时能快速按领域分组减少认知负担。命名方式示例Agent 友好度模糊动词process低动词对象process_order中动词对象限定process_order_refund高领域前缀动词对象order_process_refund高工具多时更优提示命名这件事没有绝对标准但一定要在整个工具集内保持一致。Agent 会从一致性中学习模式你今天这样命名明天那样命名它就得重新适应。3. 工具描述把什么时候用写清楚比是什么更重要3.1 描述的第一句话决定 Agent 会不会继续看Agent 读工具描述的方式和人类不一样。人类会从头读到尾Agent 更依赖前几个 token 来判断相关性。所以描述的第一句话必须直接回答这个工具是干什么的、什么时候该用。我见过很多描述写成这样This tool is used to process the data that comes from the system and returns the result. 这种描述对 Agent 来说几乎等于没说。好的描述应该像这样Use this tool when you need to search for products by keyword and get a list of matching items with prices and availability.注意区别前者说的是这个工具处理数据后者说的是当你需要按关键词搜商品时用这个工具。后者直接告诉 Agent 使用场景前者只是泛泛而谈。3.2 描述里必须包含的四类信息根据我的经验一个合格的 MCP Tool 描述应该包含四类信息第一类使用场景。什么情况下该调用这个工具。这是最重要的要放在最前面。第二类输入要求。调用这个工具需要提供什么信息哪些是必须的哪些是可选的。第三类输出内容。调用后会返回什么返回的数据结构大概是什么样。第四类限制条件。什么情况下这个工具不适用或者有什么特殊约束。这四类信息不需要写得很长但每类都要有。我通常控制在 3 到 5 句话以内超过这个长度 Agent 可能就不会完整读取了。3.3 描述里的负向说明同样重要很多人写描述只写这个工具能做什么不写这个工具不能做什么。但对 Agent 来说负向说明往往更有价值。因为 Agent 面对多个工具时最大的困惑是这几个工具看起来都能做这件事我该选哪个。比如你有两个工具一个叫search_products一个叫recommend_products。如果描述里不写清楚区别Agent 可能会随机选一个。但如果你在search_products的描述里写Use this when the user provides a specific keyword or filter在recommend_products的描述里写Use this when the user wants suggestions without a specific keywordAgent 就能准确区分了。3.4 描述的语言风格要统一整个工具集的描述风格要统一。要么都用祈使句要么都用陈述句不要混着来。时态也要统一一般用一般现在时。这些细节看起来小但会影响 Agent 对工具集整体一致性的判断。我自己的习惯是全部用祈使句开头比如 Search for products by keyword. 然后跟一句补充说明。这样 Agent 读起来节奏一致不会因为风格跳变而分心。4. 参数设计让 Agent 一次就能传对4.1 参数越少越好但该有的不能少Agent 调用工具时参数传错是最常见的问题之一。参数越多传错的概率越大。所以设计参数时第一原则是能少则少。但少不等于缺该有的信息如果没在参数里Agent 就得靠猜猜错的代价更大。我的经验是每个参数都要能回答如果这个参数不传Agent 能不能从上下文里推断出来。如果能推断出来就设成可选如果不能就设成必填。不要为了看起来灵活把什么都设成可选Agent 面对一堆可选参数时反而容易懵。4.2 参数命名和描述要自解释参数名要能自解释不要用param1、arg2这种。参数描述要写清楚这个参数接受什么格式的值。比如一个日期参数光写date不够要写清楚是YYYY-MM-DD格式还是时间戳。我踩过一个坑有个工具的参数叫type描述只写了 the type of the item。Agent 完全不知道这个 type 有哪些合法值结果传了一个不存在的类型进去。后来我把描述改成 the type of the item, must be one of: book, movie, music问题就解决了。4.3 枚举值要显式列出如果参数是枚举类型一定要在描述里把所有合法值列出来。Agent 不会去猜你的枚举值有哪些你不列它就乱传。{ name: sort_order, type: string, description: Sort order for results. Must be one of: asc, desc., enum: [asc, desc] }上面这种写法就是合格的。enum字段本身也能帮助 Agent 理解合法值但描述里再写一遍更保险因为有些 Agent 实现可能不会完整解析 schema。4.4 参数之间的依赖关系要说明有些参数之间存在依赖关系比如如果传了 A就必须传 B。这种关系一定要在描述里写清楚否则 Agent 可能只传 A 不传 B导致调用失败。我通常会在描述里加一句 If X is provided, Y must also be provided. 这种显式说明对 Agent 非常友好。4.5 默认值要谨慎设置默认值能减少 Agent 的负担但设置不当也会出问题。如果默认值不符合 Agent 的预期它可能不会主动去覆盖结果拿到一个不符合预期的结果。我的建议是默认值只设置在那些大多数情况下都适用的参数上。如果某个参数的取值高度依赖具体场景就不要设默认值让 Agent 显式传。5. 返回结构Agent 读得懂才算有效返回5.1 返回内容要结构化不要塞大段文本Agent 处理结构化数据的能力远强于处理大段自然语言。所以工具返回的内容尽量结构化用 JSON 对象而不是拼接好的字符串。我见过一些工具返回这样的内容Found 3 products. The first one is a book called XXX priced at 29.9. The second one is... 这种返回对 Agent 来说很难解析它得自己做信息抽取。更好的做法是返回一个数组每个元素是一个对象包含name、price、category等字段。5.2 返回字段的命名要和参数命名一致如果参数里叫product_name返回里就不要叫item_title。命名一致能减少 Agent 的理解负担也能让它在多轮调用中保持上下文连贯。5.3 返回内容要包含下一步提示高质量的工具返回不只是给数据还会给 Agent 一些下一步可以做什么的提示。比如搜索类工具返回结果后可以附带一句 You can use get_product_detail with the product_id to get more information. 这种提示能帮助 Agent 规划后续动作减少它卡住的概率。5.4 空结果和错误结果要区分清楚空结果和错误是两回事。空结果是调用成功了但没有匹配的数据错误是调用失败了。这两种情况要返回不同的结构让 Agent 能区分。我通常的做法是空结果返回一个空数组加一个total: 0的字段错误返回一个包含error_code和error_message的对象。Agent 看到空数组就知道是没数据看到 error 就知道是出问题了。返回类型结构示例Agent 应对方式正常结果{items: [...], total: 5}直接使用数据空结果{items: [], total: 0}告知用户没找到参数错误{error: invalid_param, message: ...}修正参数重试系统错误{error: internal_error, message: ...}告知用户稍后重试6. 工具粒度太粗和太细都是坑6.1 粒度太粗Agent 不知道该传什么有些工具设计得特别万能一个工具什么都能干参数一大堆。这种工具对 Agent 来说很难用因为它不知道该传哪些参数、不该传哪些参数。比如一个叫manage_user的工具参数有action、user_id、name、email、role、statusAgent 看到这个工具完全不知道从哪下手。更好的做法是拆成create_user、update_user、delete_user、get_user四个工具每个工具的参数都很少Agent 一看就知道该用哪个。6.2 粒度太细Agent 要调很多次才能完成一件事反过来粒度太细也不行。如果完成一个任务需要 Agent 连续调用五六个工具中间任何一步出错都会导致任务失败。而且每次调用都要消耗 token调用次数多了成本也上去了。我的经验法则是一个工具应该对应一个原子操作这个操作在业务上有明确的意义不需要再拆分。比如创建订单是一个原子操作不需要拆成创建订单头和创建订单行两个工具。6.3 怎么判断粒度是否合适判断粒度是否合适我通常问自己三个问题第一个问题这个工具的名字能不能用一句话说清楚如果能粒度大概率合适如果要说三句话才能说清楚说明太粗了。第二个问题Agent 完成一个典型任务需要调用这个工具几次如果超过三次可能太细了如果一次调用要传超过七个参数可能太粗了。第三个问题这个工具的返回结果Agent 能不能直接用来做下一步决策如果能说明粒度合适如果还要再调别的工具才能用说明可能拆得太细了。6.4 工具分组和命名空间当工具数量超过 20 个时建议做分组。MCP 协议本身支持通过 server 来分组但即使在一个 server 内也可以通过命名前缀来分组。比如所有订单相关的工具都以order_开头所有用户相关的都以user_开头。分组的好处是 Agent 在扫描工具列表时能快速定位到相关领域减少无关工具的干扰。我实测下来分组后 Agent 的工具选择准确率能提升 10 到 15 个百分点。7. 错误处理让 Agent 知道怎么补救7.1 错误信息要具体不要笼统Operation failed 这种错误信息对 Agent 来说毫无价值。Agent 需要知道具体哪里失败了、为什么失败、能不能补救。好的错误信息应该包含三部分错误类型、错误原因、建议动作。比如 Invalid parameter: date must be in YYYY-MM-DD format. Please reformat and retry. 这种错误信息Agent 一看就知道该怎么改。7.2 区分可重试错误和不可重试错误有些错误重试一下就好了比如网络超时有些错误重试多少次都没用比如参数格式错误。Agent 需要知道当前错误属于哪一种才能决定是重试还是换方案。我通常会在错误返回里加一个retryable字段布尔值。Agent 看到retryable: true就知道可以重试看到false就知道要换方案。7.3 错误信息不要暴露内部实现错误信息要写给 Agent 看不要暴露堆栈跟踪、数据库表名这些内部细节。一方面 Agent 看不懂另一方面这些信息对 Agent 的决策没有帮助。我见过一个工具出错时返回了完整的 SQL 错误信息Agent 拿到之后完全不知道该怎么办。后来改成 The requested resource was not found. Please check the ID and try again. 之后Agent 就能正确处理了。7.4 提供降级方案提示如果某个工具调用失败但存在替代方案可以在错误信息里提示 Agent。比如 Search by keyword failed. You can try search_by_category as an alternative. 这种提示能帮助 Agent 快速找到出路而不是卡在原地。8. 实测中的几个反直觉发现8.1 描述写得越长Agent 反而不一定用得更好我一开始以为描述写得越详细越好后来发现不是。描述太长会占用大量 token而且 Agent 可能只读前面一部分。我现在控制在 3 到 5 句话把最关键的信息放在前两句效果反而更好。8.2 示例比解释更有用在描述里加一个简单的调用示例比写一段解释更有效。比如 Example: search_products(keywordlaptop, max_results10) 这种Agent 一看就懂。我现在每个工具的描述里都会加一个示例。8.3 参数少但必填比参数多但可选更好我做过对比一组工具参数少但都是必填另一组参数多但大部分可选。结果第一组的调用成功率明显更高。原因是 Agent 面对可选参数时容易犹豫不知道该不该传。必填参数虽然约束强但反而让 Agent 的决策更简单。8.4 返回结果里的字段顺序也有影响这个发现比较意外。返回 JSON 时如果把最重要的字段放在前面Agent 提取信息的准确率会更高。可能是因为 Agent 处理 JSON 时也是按顺序读的前面的字段注意力更集中。8.5 工具数量超过 30 个后准确率会明显下降我实测下来当工具数量超过 30 个时Agent 的工具选择准确率会从 90% 左右降到 70% 左右。这时候就需要做分组或者分层让 Agent 先选领域再选具体工具。9. 一套可复用的 MCP Tool 设计检查清单9.1 命名检查名字是否以动词开头能直接体现操作名字是否包含操作对象避免模糊整个工具集内命名风格是否一致是否避免了过度缩写和同义词混用9.2 描述检查第一句话是否直接说明使用场景是否包含了输入要求、输出内容、限制条件是否有负向说明帮助 Agent 区分相似工具描述长度是否控制在合理范围内是否包含至少一个调用示例9.3 参数检查参数数量是否控制在合理范围参数命名是否自解释枚举值是否显式列出参数依赖关系是否说明默认值设置是否合理9.4 返回检查返回结构是否结构化字段命名是否和参数命名一致是否包含下一步提示空结果和错误是否区分错误信息是否包含类型、原因、建议动作9.5 粒度检查工具是否对应原子操作完成典型任务需要调用几次参数数量是否过多工具数量是否超过 30 个是否需要分组提示这份清单不是一次性的每次新增工具或修改工具时都过一遍能避免很多低级问题。10. 我在实际项目中的几点体会做 MCP Tool 设计这段时间最大的体会是不要用程序员的思维去揣测 Agent 的行为。程序员觉得这很明显啊的地方Agent 可能完全 get 不到。程序员觉得这不用写吧的信息Agent 可能恰恰需要。我现在设计工具时会先写一版描述然后自己假装是一个完全不了解系统的 Agent看能不能仅凭描述就正确使用这个工具。如果做不到就继续改直到能做到为止。这个方法虽然笨但很有效。另一个体会是工具设计是一个迭代过程不要指望一次就设计完美。我现在的做法是先设计一版然后拿真实任务跑一遍看 Agent 在哪里出错根据出错情况反推设计问题再改。通常迭代两三轮之后工具的质量就会有明显提升。还有一点多观察 Agent 的实际调用日志。Agent 调用工具时的参数、顺序、频率都能反映出工具设计的问题。比如某个工具经常被传错参数说明参数设计有问题某个工具很少被调用说明描述可能没写清楚使用场景。这些信号比任何理论分析都直接。最后分享一个小技巧如果你不确定两个工具该不该合并就先分开设计跑一段时间看 Agent 的使用情况。如果 Agent 总是连续调用这两个工具说明它们可能该合并如果 Agent 总是只调用其中一个说明分开是对的。让实际数据来告诉你答案比拍脑袋决策靠谱得多。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。