工具调用:让智能体真正行动
智能体如何可靠地选择与调用外部函数——schema 设计、参数校验与失败恢复。
从输出到行动
纯语言模型只能产出文本。工具调用把它变成智能体:模型发出结构化请求,你的代码负责执行,结果再回到上下文。
模型 → { name: "search_orders", args: { customer_id: "C1024" } }
你的代码 → 执行 → 返回数据行
模型 → 基于数据推理 → 作答
模型自己从不执行任何东西,执行的是你的代码。模型只决定「调什么」和「传什么参数」。
Schema 设计几乎决定成败
工具描述本身就是提示词。描述写得差,调用就会出错,或者干脆不被调用。
要把「做什么」和「边界在哪」都写清楚:
反例:get_data — 获取数据。
正例:get_order_history — 返回单个客户的订单记录,按时间倒序。接受客户 id,不接受邮箱或姓名。最多返回 50 行,需要更早数据请使用 before 参数翻页。
三条规则能挡掉大部分故障:
- 枚举就穷举。 参数若为
day|week|month,就逐个列出,别写「一个时间范围」。 - 写明不接受什么。 「接受客户 id,不接受邮箱」直接消灭一整类错误调用。
- 交代限制。 最大行数、默认排序、翻页方向——模型猜不出你的 API 契约。
失败处理:别陷入重试死循环
工具调用失败的原因很固定:参数非法、瞬时错误、权限不足。把失败作为数据返回给模型,而不是抛异常把整轮对话打死。
{ ok: false, error: "客户 ID C1024 未找到", hint: "id 形如 C1000-C9999" }
模型通常能根据描述性错误自我纠正,但面对堆栈跟踪什么都做不了。
防死循环:给每轮的工具调用次数设上限,并检测重复的 (工具名, 参数) 组合。同一个失败调用出现两次就停下来交给人处理。
常见故障模式
| 症状 | 可能原因 |
|---|---|
| 工具从不被调用 | 描述缺少用户会用的触发关键词 |
| 参数每次都错 | 未穷举枚举、未说明单位 |
| 多个工具时选错 | 工具描述高度重叠,需拉开差异 |
| 报错后无限重试 | 错误以异常抛出,而非可检查的数据 |
| 结果凭空捏造 | 返回值从未真正回到上下文 |