工具调用
本文总结工具是 Agent 碰到真实系统的入口,每个入口都要写清楚谁能用、怎么用、出了错怎么办。
工具调用是 主线 第 5 站的核心概念:Agent 想做的每件事,最后都要经过某个工具落到真实系统上。面试向的完整题解见 工具 / 协议,这里只讲概念本身。
一个动作至少要说清楚什么
- 输入是什么,哪些参数允许模型来填。
- 前置条件是什么,谁有权限执行。
- 成功返回什么,失败怎么表示。
- 会不会写数据、花钱或者影响外部。
拿 create_reimbursement_ticket 套一下:输入是发票号、金额、事由和条款引用,员工 ID 不让模型填,从登录态注入;前置条件是这张发票属于当前员工、员工已经确认;成功返回工单号(比如 R-55821),失败返回带错误码的结构;它会写入工单系统,还会触发审批流程。
Agent 提出一个动作,不等于动作已经执行了。涉及发消息、删资料或者运行代码时,不能只靠模型自己判断。
提议、执行、核对是三件事
一次工具调用在运行时里至少要经过这几个状态:
proposed 模型输出调用意图(工具名 + 参数)
validated 参数通过 schema 与业务校验
authorized 按当前用户身份和策略允许执行(高风险动作还需人确认)
executed 真实调用下游,记录请求与响应
verified 结果通过校验,写回任务状态
模型只管第一步,后面的都归应用或 Harness。把这几步分开记,出了问题才答得上来到底是模型提错了、执行层放错了,还是下游返回了脏数据。比如员工发现工单里的金额是 0:日志里 proposed 那一步金额就是 0,问题在模型;proposed 是 118.5,executed 时成了 0,问题在参数转换;请求是对的,下游存成了 0,问题在工单系统。
结果不只有成功和失败
| 结果 | 含义 | 下一步 |
|---|---|---|
| 成功 | 下游确认完成,并返回业务对象 ID | 写回状态,继续任务 |
| 业务失败 | 下游明确拒绝(权限不足、单据已关闭) | 不重试,向用户解释 |
| 可修复错误 | 参数不合法 | 结构化错误回给模型修正,次数有限 |
| 结果未知 | 超时、连接中断 | 先用业务键查询对账,再决定是否重试 |
“结果未知”最容易被当成失败处理,一盲目重试就会产生重复副作用。具体场景是:建单请求发出去 10 秒没回音,其实工单系统已经建好了 R-55821,只是响应在网络上丢了。这时要先用 query_tickets 按行程和发票号查一下,查到了就直接用这张单,查不到再重试。处理方法见 可靠执行。
按风险给工具分级
| 级别 | 例子 | 默认策略 |
|---|---|---|
| 只读、内部 | 查制度、查自己的发票 | 自动执行,可缓存,可重试 |
| 只读、外部 | 搜索网页、读第三方文档 | 自动执行,但返回内容按不可信数据处理 |
| 写入、可撤销 | 起草工单、保存草稿 | 带幂等键,执行后通知用户 |
| 写入、不可撤销或涉及资金 | 提交报销、发送对外邮件、删除数据 | 必须经 [[human-in-the-loop |
分级写在工具契约里,由执行层强制执行,别写进提示词指望模型自觉,原因见 安全边界。
以论文检索工具为例
一个 search_papers(query, year_from, limit) 工具在研究型 Agent 里会碰到几类典型情况:
- 超时:检索是只读的,可以退避重试;连续超时就降级到备用数据源,并在结果里注明来源换了。
- 限流:下游返回 429 时,按 Retry-After 等够时间再发,别让模型立刻再试一次。同一任务里相同的查询走缓存。
- 空结果:别只返回一个空列表就完事,要返回“没有命中,可能是关键词太窄或年份限制太严”,让模型有依据地调整。光给一个
[],模型多半会随手换个词再查,换来换去就陷进了循环。 - 重试策略:瞬时错误由工具层处理,需要改参数的错误交给模型,总次数受任务预算约束。
这些结果都要写回 任务状态:哪些查询做过,得到了什么,哪些失败了。下一步规划时看得到这些记录,才不会把同一个查询再发一遍。
三个常见误解
- “参数符合 JSON Schema 就安全了”:schema 只管格式,不管日期合不合理、对象是不是当前用户的、金额有没有超限。比如
{"invoice_ids": ["INV-102"], "amount": 11850}完全符合 schema,金额却多写了两个零。业务校验和权限检查必须另外做。 - “工具越多 Agent 越强”:每个工具定义都要占上下文,描述相近的工具还会互相干扰。“差旅申请”和“差旅报销”放在一起,模型就可能选错。工具集要按任务精选,多了就分组、路由或者按需加载。
- “工具返回的内容可以直接相信”:网页、邮件、发票备注里都可能夹着指令式的文字,它们只能当数据参与推理,不能改变 Agent 的权限或目标。
放回主线
在报销案例里,query_invoices 是只读的内部工具,可以自动执行;create_reimbursement_ticket 是涉及资金的写操作,必须先让员工确认金额和超标说明(INV-102,118.5 元,超标 38.5 元),并带上由出差单号和发票集合生成的幂等键。员工身份始终由会话注入,不交给模型来填。