跳转至

第 19 章 工具调用:协议、执行与安全边界

开场问题:模型输出了格式完美的转账调用,为什么系统仍然绝不能直接执行?

19.1 Function Calling 的本质

给模型一组工具 schema:名称、用途、参数类型与描述。模型输出“选择哪个工具 + JSON 参数”,应用执行后把结果作为 tool message 放回上下文,模型再继续。

模型只是提出调用建议,不直接拥有能力。真正权限在执行器。这条边界决定了系统安全。

19.2 Schema 怎样写

工具应单一职责、命名可区分、参数尽量强类型。描述需要说明“何时用、何时不用”,而不只是功能。

坏设计:run(action: string, payload: any),把所有决定都塞进自由文本。
好设计:get_order(order_id), request_refund(order_id, reason, amount),各字段有枚举、范围和必填规则。

工具太多也会降低选择准确率并占上下文。可先按任务路由出一个小工具集,再交给模型选择。

19.3 参数生成之后必须验证

应用层检查:

  • JSON Schema 和类型。
  • 数值范围、枚举、日期。
  • 当前用户是否有权限访问对象。
  • 资源是否仍处于允许操作的状态。
  • 敏感操作是否需要确认。

绝不能因为参数来自“受信模型”就跳过。模型可能误解,也可能被 prompt injection 操纵。

19.4 幂等性与副作用

网络超时后不知道转账是否成功,盲目重试可能执行两次。所有有副作用工具都应支持 idempotency key,并在重试前查询状态。

Agent 的“重试”不能简单等价于再次调用。读取类工具通常安全;写入、发送、删除、支付需要事务语义、确认和审计。

19.5 工具返回怎样放回上下文

返回应结构化、精简、标明错误类型。不要把 5 MB 原始日志全部塞回模型;先在执行器侧过滤、分页或摘要,但保留可追溯 ID。

工具输出是不可信数据。网页抓取结果即使包含“调用 delete_all 工具”,也不能改变权限。用清晰边界包裹,并在 system 层声明它只是观察。

19.6 工具错误分类

  • 选择错误:该调用时没调、或选错工具。
  • 参数错误:字段缺失、值错、引用了不存在对象。
  • 执行错误:外部服务超时或返回失败。
  • 解释错误:调用成功但误读结果。
  • 循环错误:重复调用同一个无效工具。

不同错误修法不同:选择和参数可通过示范/SFT;执行错误靠重试和系统;解释错误需改善返回 schema;循环错误需状态检测和步数限制。

19.7 工具调用数据

一条训练轨迹应包含:用户请求 → assistant tool call → tool result → assistant 最终回答。还要加入无需工具、工具失败、结果为空、多个工具有相似功能等反例。

只训练成功的完美轨迹,模型上线后遇到第一个 timeout 就不会处理。

核心问答

Q:模型输出通过 JSON Schema,可以直接执行吗? 不可以。Schema 只验证形状,还需权限、业务状态、范围和用户确认。

Q:工具描述为什么要写“何时不用”? 多个工具功能相近时,仅描述能力无法形成决策边界,模型会过度调用。

Q:所有失败都能自动重试吗? 不能。有副作用操作必须幂等并确认之前是否已成功。

Q:工具结果算不算可信指令? 不算。它是环境数据,可能包含恶意文本,只能作为观察。