如何为 AI Agent 设计可靠的工具调用机制?
简化版
可靠的工具调用要过四道门:格式校验(Schema)、业务校验、权限校验、副作用保护(幂等/事务),再加错误分类、超时、重试、审计。核心红线——模型输出的工具名和参数只是「调用建议」,不能绕过用户权限或业务规则直接执行。Schema 管格式,执行层才管安全与正确性。
详细版
每个工具应职责单一,名称和描述说明适用条件、参数语义、副作用、返回值。执行链路:
模型提出调用 → 应用验证 Schema → 检查身份与权限 → 执行工具 → 规范化结果 → 把必要观察返回模型
结构化输出能减少格式错误,但不能保证参数业务正确、也不能证明用户有权执行。写操作还要用幂等键、乐观锁或事务,避免超时重试造成重复扣款、发信、建资源。
完整版教学
一、工具接口怎么设计:窄而明确
优先提供窄而明确的业务工具,别暴露万能接口:
✓ 好:get_order(order_id) request_refund(order_id, reason)
✗ 危险:run_sql(query) exec_shell(cmd) http_request(url) ← 权限过大、易被注入利用
枚举、范围、长度、必填都写进 Schema。关键认知:工具描述会影响模型选择,但描述文本本身不是安全策略——真正的限制必须在执行端实现。
二、四道门:Schema 之外还有三道
这是本题的核心框架。Schema 只是第一道,远远不够:
第1道 格式校验(Schema):类型、形状、枚举、必填对不对
第2道 业务校验:订单属于当前用户吗?金额≤额度吗?资源状态允许吗?动作和原始请求一致吗?
第3道 权限校验:当前用户对目标资源有权限吗?(身份凭证由运行时持有,不让模型生成/选择令牌)
第4道 副作用保护:写操作用幂等键/乐观锁/事务
记忆钩子:Schema 管格式、执行层管安全与正确性。 「参数形状对」离「这个操作可以安全执行」还差三道门。
写入类工具怎么把这几道门落到代码里,见「让模型通过工具写数据库,怎么防止它写错?」。
三、错误怎么分类返回
错误要分类,让模型和运行时区别对待:
| 错误类型 | 处理 | 能否靠重试解决 |
|---|---|---|
| 瞬时(超时/限流/暂时不可用) | 有界指数退避重试 | 能 |
| 校验错误(参数不对) | 让模型修正参数再试 | 改参后能 |
| 无权限 | 停止,不该重复调用 | 不能 |
| 业务终态失败 | 记录原因,不盲目重试 | 不能 |
返回稳定错误码 + 安全摘要,绝不把堆栈、密钥、内部网络信息塞回模型上下文(会泄露 + 被注入利用)。
四、重试与幂等(最容易出事故的地方)
写操作的重试是重灾区。看一个经典事故:
Agent 调 request_refund(order_123, 50元) → 服务端执行成功,但【响应在网络中丢失】
Agent 以为失败 → 重试 → 又退了一次 50 元 → 重复退款!
正解:客户端生成幂等键 idempotency_key=uuid
服务端记录同一 key 的执行结果 → 第二次请求直接返回第一次的结果,不重复执行
只对瞬时故障做有界指数退避;无法安全重试的动作应查询最终状态或转人工。
五、结果与上下文:工具返回值不可信
工具返回的网页、邮件、数据库文本等仍是不可信数据——不能因为”来自工具”就升级为指令(间接 Prompt Injection 的入口)。大结果存外部,上下文只放引用、摘要、关键字段。
可观测性:记录请求 ID、工具版本、参数摘要、授权结果、耗时、错误码、状态变化,并对 PII 和密钥脱敏——才能回放失败轨迹、算工具成功率、定位责任边界。
六、如何用失败注入验收执行链路
正常请求成功并不能证明机制可靠,验收时应主动注入参数越界、无权限、限流、响应丢失和恶意工具返回值。假设 10,000 次测试调用中有 400 次故障注入:100 次非法参数应全部在执行前拦截,100 次无权限请求不得产生副作用,100 次响应丢失只能产生 100 个业务动作,另 100 次恶意返回不得改变权限或触发未授权工具。
若响应丢失组实际产生 103 个退款,重复执行率为:
重复执行率 = (103 - 100) / 100 = 3%
即使整体调用成功率接近 99%,该实现仍不合格。应结合请求 ID 和幂等键定位重复动作,在修复后再次验证“业务动作数 = 唯一幂等键数”;同时统计格式拒绝率、权限拒绝准确率、未知终态数、P95 延迟和脱敏泄漏数,避免单一成功率掩盖安全问题。
七、常见误区与追问
- 误区:结构化输出(Schema)保证工具调用安全。 只保证格式,业务正确性/权限/副作用要另外三道门。
- 误区:模型说要调工具就执行。 模型输出只是建议,执行前必须过业务/权限校验。
- 误区:写操作直接重试就行。 会重复扣款/发信,必须用幂等键,服务端保证同 key 返回同结果。
- 误区:工具返回值可信、可当指令。 是不可信外部数据,可能藏间接注入,不能升级为指令。
- 误区:把错误堆栈返回给模型帮它调试。 会泄露密钥/内网信息并被注入利用,只返回稳定错误码+安全摘要。
- 追问:工具调用的四道门是什么? 格式校验(Schema)、业务校验、权限校验、副作用保护(幂等/事务)。
- 追问:为什么写操作要幂等键? “响应丢失但动作成功”会被误判失败并重试,导致重复执行,幂等键保证同 key 只生效一次。
- 追问:错误怎么分类? 瞬时(可退避重试)、校验错(改参重试)、无权限(停止)、业务终态失败(不重试)。
八、加强记忆
工具调用记「四道门 + 建议非命令」:格式校验(Schema)、业务校验(归属/额度/状态/一致性)、权限校验(真实身份,令牌由运行时持有)、副作用保护(幂等键/乐观锁/事务)。核心红线钉死:模型输出只是”调用建议”,Schema 管格式、执行层管安全与正确性;写操作必须幂等(防”响应丢失→重试→重复扣款”);工具返回值是不可信数据(可能藏注入、不能当指令);错误只返回稳定码+安全摘要(别回传堆栈密钥)。工具接口要窄而明确,别暴露 run_sql/exec_shell。
项目实战落地
项目里怎么做的
《AI Agent智能教务排课与教学质量分析系统》的排课 Agent 靠 8 个工具真正把课排进数据库,工具都写在 ScheduleAgentToolService 里:
- 按职责分三组:资源查询(课程教师班级教室节次、启用规则、教务知识检索)、约束检测(教师可用、教室开放、教室容量、时间点占用)、落库(写入一节课),写库只有
saveScheduleLessonTool一个入口; - 统一的返回外壳:8 个工具都不直接返回字符串,而是走
success或fail,把结果Map序列化成 JSON 作为发回模型的 tool 消息,同时写一条调用日志;用LinkedHashMap保证模型读到的字段顺序稳定; - 查库报错不往外抛:
fail把错误文本返回给模型,让它换个参数重试或跳过这一节课,整轮排课不会因为一次查询失败就终止; - 业务上不该做的事按正常结果返回:模型传了不存在的班级或教室 ID,容量检测返回一条让它重选的说明;写入时必填项缺失或时间点已被占用,也走
success返回拒绝原因,因为工具本身执行成功了,只是这次不该写入; - 写入前再查一遍:模型被要求先检测再写入,但它是否照做无法强制,写入工具会用同一个占用统计方法自己再查一次,重复占用一律拒绝。
为什么这样取舍
- 把「执行报错」和「业务拒绝」分开:前者可能换个参数就好,后者要换方案,模型拿到可读的说明才知道下一步该做什么。
- 写入口只有一个:所有校验集中在这一个方法里,不会出现某条路径绕过检测直接落库。
面试官还会追问
- 占用冲突检测为什么同一个方法调三次、每次只传一个资源 ID,而不是一次查完?
- 判断一个时间点有没有被占用,为什么要按整个学期算,而不是只看这一次排课任务?
学完《AI Agent智能教务排课与教学质量分析系统》,上面这些追问你都会迎刃而解。