[控场AI]
· 8 分钟阅读· 4,452 字

AI Agent 工具调用:让模型真正用对工具的接口设计指南

AI Agent 工具调用:让模型真正用对工具的接口设计指南

Agent 工具应像给无法提问的工程师设计 API 那样设计,定义清晰比调提示词更能修 bug。

文章指出,Agent 调错工具、编造参数、反复重试等问题的根源往往不在模型,而在工具本身的设计。模型只能看到工具的名称、描述和输入 Schema 三样东西,这构成全部契约。作者建议用写新人文档的方式写描述,回答做什么、何时用、何时不用、返回什么;用统一的动词_名词命名避免混淆;控制工具数量、合并边界重叠的工具,不要一比一映射 REST API。Schema 应承担约束职责,用枚举、格式说明和合理的 required 字段防止垃圾参数。工具输出要精简为模型下一步决策所需的信息,错误消息要说清问题和替代方案。此外还需为错误做设计,写操作幂等、读写分离、硬限制放在代码里,并通过 20-50 条真实请求的测试集系统性定位和修复问题。

给 Agent 挂上几个工具、写一段像样的系统提示词,跑几条测试请求——一切正常。真实用户一来,问题就暴露了:该调用 get_order 的时候它选了 search,该传订单 ID 的地方它填了客户姓名,同一个失败的调用连续重试五次,把 40,000 token 的 API 响应塞进上下文后彻底忘了用户问的是什么。

AI agent tool calling 文章配图

大多数人的第一反应是怪模型,或者往系统提示词里再堆更多指令。但真正的问题往往出在工具本身。模型对你的系统一无所知,它只能看到你给它的三样东西:名称、描述和参数 Schema。如果这三样东西含糊、重叠或者噪声太大,模型就只能靠猜——而且猜得信心十足。

这篇文章的核心观点是:把工具当作模型的接口来设计,就像你在为一个无法向你提问的工程师设计 API 一样。

模型眼中的工具到底是什么

不管你用什么框架,一个工具最终以三个字段的形式抵达模型:名称、描述、输入 Schema。这就是全部契约。

模型看不到你的实现代码、数据库结构,也看不到你留在代码里的注释。它判断"何时调用这个工具、该传什么参数"的全部依据,就是那几行定义。把这些定义当成项目里最重要的文档来写,因为对 Agent 来说它们确实是。

函数调用(Function Calling / Tool Calling)机制:这是现代大模型 Agent 的底层能力。模型本身不能执行任何代码,它只是在推理时输出一段结构化的 JSON,声明"我要调用哪个函数、传什么参数",由外部运行时(Agent 框架)解析后真正执行,再把结果作为一条新的消息喂回模型。这意味着模型的全部"世界知识"在调用那一刻仅来自工具定义,它无法反查你的代码或数据库来纠正自己的理解。也正因如此,工具的名称、描述和 Schema 实际上承担了"提示词"的角色——它们是模型决策时最直接、最可信的上下文,优先级往往高于系统提示词中泛泛的指令。理解这一点,就能明白为什么微调定义比堆砌提示词更有效。

用写新人文档的方式写工具描述

一个好的工具描述要回答四个问题:它做什么、什么时候用、什么时候不用、返回什么。

对比下面两种写法:

search: Searches for things.
search_help_articles: Full text search over the public help center.
Use it for general "how do I" questions about product features.
Do not use it to look up a specific customer, order or invoice;
use get_order or get_customer for those.
Returns up to 5 articles with title, url and a short snippet.

第二种更长,但这完全没问题。描述的成本远低于一次错误调用。"不要用它来做某某事"这句话是最多人跳过的,却恰恰是能阻止最多错误的一句,因为它告诉模型两个相似工具之间的边界在哪里。

命名同样重要。search 和 lookup 并排出现就是抛硬币。search_help_articles 和 get_order_by_id 则不会混淆。用统一的 动词_名词 模式,避免两个名称可以被合理理解为同一件事。

工具要少,边界要清晰

每增加一个工具,模型的决策难度就上升一档。一个有 40 个工具的 Agent 每一步都要扫描 40 条描述,而其中两个工具功能重叠的概率也在快速攀升。

几条实用的规则:

  • 只差一个参数的工具合并掉。 与其保留 get_open_orders、get_closed_orders、get_cancelled_orders,不如做一个 list_orders,加一个 status 枚举字段。
  • 不要一比一映射你的 REST API。 你的 API 是为确切知道自己需要什么的前端代码设计的。Agent 更适合围绕任务组织的工具,比如一个接受邮箱或姓名的 find_customer,而不是五个需要正确串联的底层端点。
  • 做完全不同事情的工具有必要拆开。 一个 manage_user 工具带 action 字段,可以 read、update 或 delete,等于把一个破坏性操作藏在了听起来无害的名字里。把 delete_user 独立出来,这样你可以清楚地描述它,并要求审批。

如果你发现自己在系统提示词里写了一大段"什么情况下用哪个工具",那就是工具本身需要更清晰边界的信号。

让 Schema 承担约束工作

输入 Schema 是抵御垃圾参数的最佳防线。你在 Schema 里放的每一条约束,都是模型不需要从散文描述里记住的东西。一个设计良好的 Schema 应该做到:

  • 用枚举代替自由文本。 如果 Schema 只允许四个值,模型就编不出一个叫"pending"的状态。
  • 在描述里写清格式和来源。 "从 find_customer 获取,永远不要猜测"这样的说明能阻止模型编造看起来合理的 ID。
  • 只把真正必需的字段标为 required。 如果一个简单调用要求模型填五个必填字段,它会为不知道的字段编造值。
  • 设置合理的限制。 给 limit 加一个 maximum,可以防止一次调用返回上千行数据。

即便如此,你自己的代码里仍然要做校验。Schema 只是引导模型,不提供任何保证。在工具实现里检查参数,拒绝坏参数并给出清晰的消息。

结构化输出与 JSON Schema 约束:多数工具调用接口使用 JSON Schema 描述参数,主流模型在训练时已见过大量 Schema,因而对 enum、type、required、maximum 等关键字有较强的遵循倾向。但要注意,Schema 的约束强度取决于供应商是否启用了"约束解码"(constrained decoding)——部分 API 会强制模型输出符合 Schema 的 JSON,另一些仅将其作为提示。因此枚举能极大降低(而非完全消除)模型编造值的概率。这也是原文强调"你自己的代码里仍然要做校验"的原因:Schema 是概率性的引导,服务端校验才是确定性的防线,两者不可互相替代。

只返回模型需要的,不是你有的全部

工具输出会直接进入模型的上下文。一个返回完整 API 响应的工具,带着嵌套的 metadata、内部 ID 和每条记录 200 个字段,既消耗 token 又把有用的部分埋了起来。

为模型的下一个决策塑造输出。人类可读的值如 "2 x Blue T shirt" 和 "49.90 EUR",比原始的分单位和嵌套数组更容易让模型推理,也更容易向用户复述。对于列表类工具,返回每项的简短摘要加上获取详情的方式,并在输出中说明结果被截断了,比如 "note": "showing 5 of 132, narrow with status"。否则模型会以为它看到了全部。

错误信息就是指令

工具失败时,错误消息是模型决定下一步的唯一依据。一个堆栈跟踪或者光秃秃的 400 Bad Request 什么信息都没给,模型只能重试同样的调用或者直接放弃。

写错误消息就像写给一个初级工程师看。两个要素让它生效:消息用平实的语言说明出了什么问题,并且说明该怎么做。"Do not retry" 这一句就能砍掉一大类 Agent 反复捶打一个永远不会成功的工具的循环。

这些错误应该作为普通的工具结果返回给模型阅读,而不是作为异常让 Agent 循环崩溃。真正的异常留给模型无法修复的情况,比如数据库宕机。

幂等性(Idempotency):在分布式系统里,幂等指同一操作执行一次和执行多次产生的效果相同。对 Agent 尤其关键,因为模型重试是常态——网络超时、上下文误解、错误消息不清,都会触发重复调用。如果 create_refund 不是幂等的,一次重试就可能给客户退两次款。常见做法是让调用方传入一个幂等键(idempotency key),服务端记录该键已处理过,重复请求直接返回首次结果而不真正执行。Stripe 等支付 API 就采用这一模式。设计 Agent 写工具时,凡涉及金钱、库存或任何不可逆外部变更,都应默认加幂等键,并把它作为 Schema 中的一个字段明确暴露给模型。

为必然发生的错误做设计

即使工具设计得再好,模型有时还是会调错工具,或者把对的工具调两次。提前规划:

  • 写操作尽量做成幂等的。 在 create_refund 上接受一个幂等键,这样重复调用不会退款两次。
  • 读写分离。 读工具可以自由运行。涉及金钱、数据或任何外部变更的写工具,应该走审批步骤,至少需要一个确认字段。
  • 硬限制放在代码里,不是提示词里。 系统提示词里的"永远不要退款超过 100 欧元"只是建议。create_refund 里的同样检查才是规则。

像测试代码一样测试工具

工具设计得好不好,读代码是看不出来的,只有观察 Agent 使用它才能知道。

建一组真实请求,20 到 50 条就够起步,每条都标注你期望的工具调用。让 Agent 跑这些请求,记录每一次工具调用及其参数。然后找规律:

  • 选错工具: 描述重叠,或者缺少"不要用于"的说明。
  • 编造参数: 某个字段不该是 required,或者描述里没说值从哪里来。
  • 反复失败的调用: 错误消息没有告诉模型下一步该做什么。
  • 冗长散漫的执行: 输出太大,或者 Agent 需要太多个小工具才能完成一个任务。

每次只改一个工具然后重跑测试集。这和其他任何测试套件是同一个循环,也是把"Agent 不稳定"变成具体修复项的最快方式。

核心结论

模型的精确度上限就是你给它的工具的精确度。清晰的命名、说明何时不该用的描述、带枚举和真实约束的 Schema、紧凑的输出、以及解释下一步的错误消息,能修复的 Agent bug 比任何提示词调优都多。把工具设计成一个无法向你提问的人使用的 API,因为那正是使用它们的人。

#AI Agent#工具调用#tool calling#接口设计#Schema 设计#LLM Agent#提示词工程#AI 应用开发
分享:

相关推荐