资讯动态

AI Agent工具调用不稳定?TOOLS.md配置指南与避坑实践

发布时间:2026/9/19 19:32:13 来源:尧图企业网站定制
1. 为什么AI项目里会多出一个TOOLS.md第一次在别人的AI Agent项目目录里看到TOOLS.md这个文件时我的反应是这不是重复造轮子吗系统提示词里写一遍工具说明代码里再定义一遍函数签名现在又多一个Markdown文件到底图什么后来自己从零搭了一套本地Agent被工具调用的混乱折腾了整整两周才真正理解这个文件存在的意义。简单说TOOLS.md是给AI看的工具说明书它和SKILL.md技能定义、SOUL.md人格与行为准则构成了一套分工明确的配置体系。SOUL.md管我是谁、我该怎么说话SKILL.md管我会哪些高阶能力而TOOLS.md管的是最底层的一件事我手上到底有哪些可以调用的函数、每个函数吃什么参数、返回什么、什么时候该用、什么时候绝对不能用。这个文件解决的核心痛点是当Agent接入的工具超过五六个之后光靠系统提示词里零散描述模型会开始幻觉调用——编造不存在的参数名、把两个工具的功能搞混、在明显不该调用工具的时候硬调。把工具清单独立成文件等于给模型一本随时可查的字典而不是让它凭记忆瞎猜。这篇文章适合三类人看正在做AI Agent应用开发的工程师、用OpenClaw这类框架搭本地助手的玩家、以及任何想让大模型稳定调用外部函数的人。我会把TOOLS.md的结构设计、字段含义、和代码的对应关系、以及我踩过的那些坑全部摊开讲清楚。2. TOOLS.md、SKILL.md、SOUL.md三者的职责边界很多人一开始会把这三个文件混着用结果就是配置越写越乱模型行为越来越飘。我先把边界划清楚这是后面所有设计的前提。2.1 SOUL.md负责人格不碰具体能力SOUL.md定义的是Agent的性格、语气、价值观和基本行为约束。比如你是一个严谨的技术助手回答要简洁不确定的事情要明说不要编造。它回答的是我是谁这个问题。这个文件里不应该出现任何具体工具的名字。我见过有人在SOUL.md里写你可以使用search工具查资料这是典型的职责越界。人格文件一旦掺入工具描述后续换工具时就得改人格文件维护成本直接翻倍。2.2 SKILL.md负责高阶技能组合SKILL.md描述的是由多个基础工具编排而成的复合能力。举个例子帮我调研某个技术方案并写成报告这个技能底层可能要调用搜索工具、网页抓取工具、文件写入工具。SKILL.md里写的是这个技能的目标、触发条件、执行流程它站在更高的抽象层。一个技能可以调用多个工具但技能本身不是工具。这个区别很关键技能是做什么事工具是用什么手段。2.3 TOOLS.md负责原子能力清单TOOLS.md是最底层、最具体的一层。它列出的每一个条目都对应代码里一个真实存在的、可被调用的函数。它回答的是我手上具体有哪些螺丝刀和扳手。三者关系可以这样理解文件抽象层级回答的问题是否含具体函数名SOUL.md最高我是谁、怎么说话否SKILL.md中间我会哪些复合技能间接引用TOOLS.md最低我有哪些原子工具是必须精确提示判断一个描述该放哪个文件问自己一句——这句话换一套工具之后还成立吗成立就放SOUL或SKILL不成立就放TOOLS。把这三层分清楚之后你会发现TOOLS.md的定位非常清晰它就是一份机器可读、人类也能看懂的接口契约。接下来讲它具体该怎么写。3. 一个能用的TOOLS.md该包含哪些字段我前后改过七八版TOOLS.md的模板最后稳定下来的字段结构是这样的。每个字段都不是拍脑袋加的背后都有具体的失败教训。3.1 工具名与一句话功能描述每个工具条目开头必须是精确的函数名和代码里的定义一字不差。我早期犯过一个错文档里写search_web代码里实际是web_search结果模型调用时一直报函数不存在排查了半天才发现是命名不一致。功能描述要控制在一句话内说清楚这个工具干什么。不要写这是一个非常强大的搜索工具可以帮助你查找各种信息这种废话直接写根据关键词搜索网页返回标题和摘要列表。3.2 参数表名称、类型、是否必填、取值范围这是整个文件里最容易被写潦草、也最容易出事的部分。我的做法是用表格把每个参数列清楚参数名类型必填说明querystring是搜索关键词建议不超过50字limitinteger否返回条数默认5最大20langstring否语言代码如zh、en默认zh参数说明里一定要写边界值。我踩过的坑某个工具的参数没写最大值模型传了个limit1000直接把接口打爆返回超时。后来所有数值参数我都强制标注范围。3.3 返回值结构说明模型需要知道调用之后会拿到什么才能决定下一步怎么处理。返回值说明要写清楚是字符串、JSON对象还是数组关键字段叫什么。比如返回JSON数组每项含title、url、snippet三个字段这样模型就知道后续可以引用result[0].url。如果返回值结构不写模型经常会把整个返回当成一个字符串去处理导致解析失败。3.4 调用时机与禁用场景这一条是我认为最有价值、但最多人忽略的字段。光告诉模型这个工具能干什么不够还得告诉它什么时候该用、什么时候别用。举个例子一个发送消息的工具必须写明仅在用户明确要求发送时调用不要在生成草稿阶段调用。我见过Agent在用户只是说帮我写条消息的时候直接把消息发出去了就是因为没写禁用场景。3.5 一个完整的条目示例把上面几点合起来一个标准条目长这样### web_search 根据关键词搜索网页返回标题、链接和摘要。 参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | query | string | 是 | 搜索关键词建议不超过50字 | | limit | integer | 否 | 返回条数默认5最大20 | 返回JSON数组每项含 title、url、snippet 字段。 调用时机需要获取实时信息或外部资料时。 禁用场景用户只是闲聊或询问你已知的常识时不要调用。这个结构看起来简单但每个字段都是被坑出来的。下面讲几个我实际踩过的坑比模板本身更有参考价值。4. 工具描述写不好模型就会乱调用TOOLS.md写得好不好直接决定Agent稳不稳。我把自己踩过的坑按症状—根因—修复整理出来你可以对照检查自己的文件。4.1 症状模型编造不存在的参数根因参数表写得太简略只写了参数名没写类型和范围模型只能靠猜。或者参数名用了缩写模型理解偏差。修复参数名用完整英文单词类型和范围全部写死。我现在的习惯是任何参数都至少写清楚类型是否必填一个具体示例值。4.2 症状两个功能相近的工具被混用根因功能描述太笼统两个工具都写处理文件模型分不清哪个是读、哪个是写。修复功能描述里必须包含区分性动词。读文件的写读取指定路径的文件内容写文件的写将内容写入指定路径会覆盖原文件。把差异点直接怼到描述里。4.3 症状该调用时不调用不该调用时乱调用根因缺少调用时机和禁用场景字段模型没有判断依据。修复每个工具都补上这两个字段。特别是禁用场景要写得具体。比如用户询问天气时不要调用本地文件工具比谨慎调用有用一百倍。4.4 症状调用成功但结果处理错误根因返回值结构没写清楚模型不知道返回的是对象还是字符串。修复返回值说明精确到字段名和类型。如果返回可能为空也要写明无结果时返回空数组。注意工具描述不是写给人类同事看的文档是写给模型看的指令。人类能靠上下文脑补的东西模型脑补不了必须写死。4.5 一个反直觉的经验我一度以为工具描述写得越详细越好结果发现描述太长反而会稀释关键信息。模型在长描述里抓不住重点调用准确率反而下降。后来我的做法是核心信息前置细节放后面。第一句必须是这个工具干什么参数和返回值紧随其后调用时机和禁用场景放最后。这样即使模型只读了前半段也能做出基本正确的判断。5. TOOLS.md和代码如何保持同步这是最现实的问题文档和代码一旦不同步TOOLS.md就从资产变成了负债。模型照着过时的文档调用报错率飙升。5.1 单一数据源原则最理想的做法是从代码生成文档。如果你的工具是用装饰器或schema定义的完全可以写个脚本扫描所有工具定义自动生成TOOLS.md。这样代码改了文档跟着变永远不会脱节。我用Python的时候会给每个工具函数加一个结构化的docstring然后写个脚本解析docstring生成Markdown。虽然前期要花点时间搭这套流程但长期看省下的排查时间远超投入。5.2 手工维护时的检查清单如果项目小、工具少手工维护也能接受但必须建立检查机制。我每次改完代码会过一遍这个清单新增工具是否已加入TOOLS.md删除的工具是否已从文档移除参数名、类型、默认值是否和代码一致返回值结构是否和实际返回一致调用时机和禁用场景是否需要更新这个清单看着啰嗦但每次改动花不了两分钟能避免大量线上问题。5.3 版本标记我在TOOLS.md顶部会加一行版本标记比如对应代码版本 v1.3.2。这样出问题时能快速判断是不是文档和代码版本对不上。配合Git的提交记录追溯起来很方便。5.4 一个真实的同步事故有次我改了一个工具的参数名从path改成file_path代码改完测试通过就提交了忘了改文档。结果第二天模型调用时一直传path报参数错误。排查时我先看代码代码没问题最后才发现是文档没同步。从那以后我养成了一个习惯改工具代码的提交里必须同时包含TOOLS.md的改动。如果某次提交只改了代码没改文档review的时候直接打回。6. 在OpenClaw这类框架里落地TOOLS.md现在很多本地Agent框架比如OpenClaw都支持把工具定义放在独立文件里。落地的时候有几个细节值得注意。6.1 文件放哪、怎么被加载通常框架会约定一个固定路径比如项目根目录下的TOOLS.md启动时自动读取并注入到系统提示词里。你需要确认框架的加载逻辑是全文注入还是按需检索。如果是全文注入那TOOLS.md就不能太长否则会挤占上下文窗口。我一般控制在工具数量乘以每个条目150字以内。如果工具特别多就得考虑分组加载或者按需检索。6.2 和系统提示词的拼接顺序TOOLS.md的内容在系统提示词里的位置会影响模型注意力。我的经验是放在人格定义之后、具体任务指令之前。这样模型先建立身份认知再了解可用工具最后接收具体任务逻辑最顺。6.3 本地部署时的路径问题本地部署Agent时工具描述里如果涉及文件路径要特别注意跨平台差异。Windows用反斜杠Linux和Mac用正斜杠。我在TOOLS.md里会明确写路径使用正斜杠Windows环境也请用正斜杠避免模型生成错误路径。6.4 工具数量膨胀后的分组策略当工具超过15个全文注入就开始吃力了。我的做法是按功能分组比如文件操作组网络请求组数据处理组每组一个小节。模型在需要某类操作时注意力会自然聚焦到对应小节。如果框架支持更好的方案是动态加载根据当前任务类型只注入相关工具组的描述。这需要框架层面的支持但效果最好。6.5 调试工具调用的实用技巧调试阶段我会在TOOLS.md里临时加一个调试模式说明让模型在调用工具前先输出我准备调用XX工具参数是XX。这样能直观看到模型的决策过程快速定位是描述不清还是模型理解偏差。定位清楚之后再把这段调试说明删掉恢复正常模式。7. 几个容易被忽略的细节和我的实操心得最后这部分是我攒下来的零碎经验都是文档里不会写、但实际用起来很关键的东西。7.1 工具描述里的措辞会影响调用率同一个工具描述写可以用于搜索和搜索网页并返回结果后者的调用准确率明显更高。原因是可以用于是模糊的许可搜索并返回是明确的动作。用动词开头少用情态动词这是我总结的一条铁律。7.2 给工具起名要避免歧义工具名里不要用get、do、handle这种万能动词。get_data这种名字模型根本猜不出是取什么数据。改成fetch_user_profile、read_config_file这种一看就懂。7.3 参数默认值要显式写出即使代码里有默认值TOOLS.md里也要写出来。模型不知道代码里的默认值如果不写它可能每次都显式传一个自己猜的值反而覆盖了合理的默认值。7.4 错误返回也要说明工具调用失败时返回什么也要写清楚。是抛异常、返回错误码、还是返回带error字段的对象模型需要知道失败长什么样才能决定是重试还是换方案。我一般会写失败时返回{error: 错误描述}。7.5 定期回顾工具使用日志我每隔一段时间会翻一下Agent的工具调用日志看看哪些工具从没被调用过、哪些经常被误调用。从没调用的可能是描述有问题导致模型不知道何时用经常误调用的可能是禁用场景没写清楚。根据日志反过来优化TOOLS.md比凭空想有效得多。7.6 别把业务逻辑塞进工具描述工具描述只讲这个工具做什么、怎么调不要写业务规则。比如用户是VIP时才能调用这种逻辑应该放在SKILL层或者代码层判断塞进TOOLS.md会让文件越来越臃肿而且模型也执行不了这种条件判断。我个人在实际操作中的体会是TOOLS.md这个文件的价值随着工具数量增长呈指数上升。三五个工具的时候写不写都行一旦超过十个没有这个文件Agent的稳定性会断崖式下跌。它本质上是用一份结构化的契约换来了模型调用的确定性。前期多花两小时把字段写全后期能省下几十小时的排查时间这笔账怎么算都划算。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价