最佳实践与反模式
本章把前面章节的工程经验浓缩为可执行的检查清单,并列出常见反模式及其危害。
Schema 设计
检查清单
- [ ] 工具名称唯一且语义清晰:使用小写 + 下划线,例如
search_orders而不是func1。 - [ ] 描述优先于命名:description 应说明工具用途、适用场景、返回内容。不要只重复名称。
- [ ] 必填字段明确:用
required列出所有必填参数,避免模型遗漏关键输入。 - [ ] 类型约束具体:尽量使用
string、number、boolean、array等具体类型,少用{}或anyOf。 - [ ] 枚举值列出选项:如果参数只有固定取值,使用
enum。 - [ ] 添加示例:在 description 或
examples中给出参数示例,降低模型理解成本。 - [ ] 字段说明完整:每个参数都写
description,说明格式、单位、取值范围。 - [ ] 控制 schema 大小:避免嵌套过深、字段过多。大型 schema 既消耗 token,也降低遵循率。
- [ ] 启用严格模式(如果可用):OpenAI strict mode 能显著提升参数格式正确率。
- [ ] 版本化 schema:破坏性变更走大版本,description 中标注 deprecation。
反模式
- “万能工具”:一个工具接收大量可选参数,试图覆盖所有场景。模型容易选错、填错参数。
- 描述含糊:描述只有“获取信息”,模型无法判断与其他工具的区别。
- 过度使用
additionalProperties: true:允许任意额外字段会降低可校验性。 - 把内部字段名暴露给模型:使用业务语义名称,而不是数据库列名。
执行治理
检查清单
- [ ] 超时必设:每个工具调用都有连接超时与整体超时。
- [ ] 重试策略区分错误类型:只对可重试错误(5xx、超时)重试,4xx 不重试。
- [ ] 重试次数有上限:避免无限重试压垮下游。
- [ ] 指数退避 + 抖动:防止重试风暴。
- [ ] 熔断器保护连续失败:连续失败达到阈值后快速失败,并暴露状态指标。
- [ ] 降级路径:主路径失败时返回缓存、兜底结果或友好提示。
- [ ] 并发控制:独立调用并行,依赖调用串行;设置最大并发数。
- [ ] 幂等性设计:写操作工具应支持幂等键(idempotency key)。
反模式
- 无超时:下游卡住时整个 Agent 挂起。
- 对所有错误都重试:对 400 Bad Request 反复重试只会浪费资源。
- 裸奔执行:直接把模型参数拼进 shell 或 SQL,存在注入风险。
- 忽略部分批量失败:并行调用中部分失败时静默丢弃,导致结果不完整。
校验与沙箱
检查清单
- [ ] 多层校验:语法校验 → JSON Schema 校验 → 语义校验 → 权限校验。
- [ ] 参数级授权:不仅校验“能不能调工具”,还要校验“能不能以该参数调”。
- [ ] 敏感操作 HITL:删除、扣款、发送外部消息等操作需要人工确认。
- [ ] 沙箱执行:外部代码或网络调用在受限容器/网络中运行。
- [ ] Secrets 不泄露:API Key、数据库密码由 Runtime 注入,不进入模型上下文。
- [ ] 输出脱敏:错误信息不暴露堆栈、内部 IP、数据库结构。
反模式
- 只校验类型不校验语义:例如
user_id是字符串但无权访问该用户。 - 把 secrets 当工具参数:让模型自己填 API Key。
- 在模型上下文中返回原始错误堆栈:既泄露信息,又浪费 token。
可观测性
检查清单
- [ ] 全链路 Trace:从模型调用到工具执行每个阶段都有 span。
- [ ] 核心指标可监控:延迟、成功率、schema 违规率、工具选择错误率、重试率、降级率、token 成本。
- [ ] 结构化日志:参数、结果、错误分类清晰,便于检索。
- [ ] 敏感字段脱敏:日志中不记录密码、token、PII。
- [ ] 定期评估工具选择准确率:用人工标注或 LLM Judge 抽检。
反模式
- 只打成功日志:失败时没有 trace,无法定位。
- 日志满天飞:不加区分地记录所有内容,既贵又难检索。
- 没有指标基线:无法判断“今天工具调用变慢了”是否正常。
模型交互
检查清单
- [ ] 错误信息可行动:返回给模型的错误应说明“哪里错了、如何修正”。
- [ ] 限制每轮工具数量:工具过多时选择准确率下降,可通过命名空间或检索动态筛选。
- [ ] 明确终止条件:设置 max-turns、max-tool-calls、no-progress 检测。
- [ ] 结果反馈完整:工具结果包括成功/失败、关键数据、错误说明,便于模型继续推理。
反模式
- 静默吞掉工具错误:模型以为调用成功,基于错误信息继续生成。
- 把工具当 Prompt 用:本可以用简单 prompt 解决,却注册一个无意义工具。
- 不控制循环深度:模型在两个工具之间来回调用,永远无法收敛。
一句话总结
好 Tool Use = 清晰的 schema + 坚固的校验 + 隔离的执行 + 完整的观测 + 明确的终止。