← 返回导航站首页

AI Agent工具调用实战指南:从工具设计到权限边界

模型决定"要不要用",工具决定"用得好不好"。

2026年,Agent能力的天花板基本等于它的工具集。同样的模型,工具设计得好能干一整天活,设计得差就只会在两个坑之间反复跳:要么选错工具,要么以为成功了。

工具调用(Tool Calling / Function Calling)看起来是"注册个函数"这么简单,实际生产环境里绝大多数翻车都出在四个地方:schema 写得模型看不懂、失败不分类就重试、工具自报成功、以及把能力当成了权限。

本文按「分域 → 描述 → 失败分类 → 假成功 → 权限 → 结果处理 → 并行 → 观测」的顺序讲清楚。

一、先给工具分域:读、写、算、通信

别按业务模块分工具,按副作用等级分。这是最省事的权限治理方式。

域 典型工具 副作用 默认策略
读 搜索、抓取、读文件、查库 无 默认开放,可并行、可重试
算 计算、转换、裁剪、渲染 无(纯函数) 默认开放,随便重试
写 发文、改文件、下单、入库 有,可回滚 显式注册 + 幂等键
通信 发消息、发邮件、调 webhook 有,对外可见且不可撤回 最严:白名单 + 审批

纯函数工具(算域)是最便宜的能力:无副作用就意味着可以无限重试、可以并行、出错也不留垃圾。能让模型"算"就别让它"写"。

写域和通信域必须区别对待:写错了能删,话发出去了收不回来。凡是发出去就收不回的工具,权限级别要单独提一档。

二、工具描述就是接口文档:模型只看得到 schema

模型看不到你的函数实现,也看不到宿主语言里的默认参数。它看到的只有 工具名 + 参数名 + 类型 + description。这四样写不好,再强的模型也会瞎调。

对照表:差写法 vs 好写法

差 好 原因
q: string search_keyword: string 参数名即提示,缩写要靠猜
format: string format: enum[png, jpg, webp] 枚举把无效值挡在模型侧,省一轮重试
t: int time_range_days: int, default 7 单位、含义、默认值都要显式
只写"搜索网页" "搜索公开网页;不要用于查本地文件或需要登录的私有页面" 负例比正例更能降低误调用
data: object 逐字段列出子字段与必填项 嵌套对象是最常见的调用失败源

三条经验:

  1. description 里写"什么时候不要用",比写十句"什么时候用"更有效。误调用大多发生在本该用 A 却用了 B 的时候。
  2. 默认值要显式写进描述。default 7 写进去,模型就不会每次瞎猜一个数字。
  3. 工具名要能自我解释:publish_article 优于 do_action。同名前缀(xhs_、tt_)能让模型分清同领域的多个工具。

工具多了要渐进披露,别全塞进上下文

常驻工具超过三四十个后,选择准确率明显下降,而且每次请求都要重发全部 schema——这就是一笔常驻上下文税。

做法是三跳:先给一个"工具检索"入口,按需拉到工具描述,再调用。

1) tool_search("发布文章到头条")        → 返回候选工具名
2) tool_describe("toutiao_publish")     → 返回该工具的完整参数
3) tool_call("toutiao_publish", {...})  → 真正执行

这样上下文里常驻的只有三个入口,而不是几百个 schema。能力可以很多,同时暴露的行为要很少。

三、失败分类:不是所有失败都该重试

把错误分成六类,每一类对应一个动作。"出错就重试"是最贵的反模式。

错误类型 典型信号 正确动作
参数/校验 400、schema invalid 改参数,不重试
目标不存在 404、空结果 换目标或换策略,不重试
限流/瞬时 429、503、无 Retry-After 的 5xx 退避重试,最多 2 次
权限/认证 401 / 403 / 登录页 停止,报人工;重试只会锁号
部分成功 批量返回逐条状态 只重试失败条目
结果未知 超时、连接重置 先查上次到底成没成,再决定

第五条和第六条是重点:

两条硬规则

规则一:同一原因失败两次,停下来想,不要第三次重试。

第三次重试常常把一个 5 分钟能修的问题拖成 40 分钟的故障——尤其是那些越试越糟的路径(重试登录失败被风控、重试提交产生重复订单)。重试前先分类:这是"上下文错"还是"瞬时错"?只有后者值得退避重试。

规则二:写工具必须带幂等键。

# 幂等键 = 任务ID + 内容指纹,同一逻辑操作重试不产生第二条
key = f"{task_id}:{sha1(title + body)[:12]}"
if store.seen(key):
    return store.get(key)   # 直接返回上次的产物引用

没有幂等键的写工具,等于给 Agent 装了一个"重复扣款"按钮。

四、假成功:工具层最致命的缺陷

实测中最常见的坑:工具返回了 success: true,但事情根本没发生。

三个来源:

  1. 工具的布尔是"已提交"而不是"已完成"。任务只是进了队列,工具就返回成功,队列失败没人知道。
  2. HTTP 200 陷阱。接口 200 但 body 里 code != 0;或者站点在风控挑战下对所有路径都返回同一页 200 骨架 HTML——此时的 200 没有任何信息量。
  3. 静默截断。工具把结果截断到 2000 字返回,没告诉模型"还有 8 万字",模型基于半份数据下结论。

修法:写工具必须回读产物

写工具的返回值里,必须有一个可寻址的产物引用:URL、内容 ID、文件路径 + 修改时间戳。拿不到这个引用的写工具,设计就是不合格的。

# 不好的返回
return {"success": True}

# 好的返回
return {
    "accepted": True,          # 已受理
    "done": True,              # 已回读验证落地
    "artifact_url": "...",      # 可寻址产物
    "artifact_id": "7682...",
    "verified_at": "2026-09-16T09:44:12+08:00",
}

把"受理"和"完成"拆成两个字段,调用方才有机会做判断。一个布尔永远表达不了这三件事:提交成功了吗、执行成功了吗、产物在哪。

判据要外置:让"动作之外的证据"说话——列表页里的新条目、数据库里的新行、下游系统的读取痕迹。工具自报的成功率是自述,不是测量。

五、权限边界就是工具注册边界

对模型来说,能力和权限是同一件事:你没注册删除工具,它就删不了;你注册了,它就一定会有一天用上——不是在恶意场景,而是在"为了完成任务"的场景。

所以权限治理不写在提示词里,写在工具注册表里:

档位 手段 适用
只读默认 读/算域工具常态开放 检索、计算、转换
写需显式开 启动参数/白名单控制写工具注册 发文、改文件、入库
不可逆需审批 工具内强制外部确认,人当断路器 删除、支付、对公众发布
凭据不进上下文 密钥由工具内部持有,模型只看到工具名 所有涉及密钥的场景

另外三条工程纪律:

六、结果处理:上下文里放指针,不放载荷

一个抓取工具返回十万字正文,就吃掉了这一轮几乎全部预算,后面所有推理都在挤牙膏。

原则很朴素:

一句话:上下文是工作台,不是仓库。仓库在外面的文件系统里。

七、并行与顺序

八、调试与可观测:记录决策轨迹,而不只是结果

每次工具调用至少记六样:工具名、参数、开始时间、耗时、状态、产物引用。有了这六样,任何一次失败都能回放,而不是靠猜。

值得盯的五个指标:

指标 说明什么
分工具调用成功率 哪个工具在拖后腿
参数校验失败率 工具描述写得好不好(高 = 描述该改)
同因重试率 重试纪律有没有破
重试深度分布 长尾是否在放大故障
写工具回读失败率 假成功有没有被抓住

改动要跑基线。 换工具实现、换模型、换提示词之后,用同一批任务重放,比较产物,而不是比较"感觉变聪明了"。工具改动的收益必须能被外部复算,否则你不知道自己是改好了还是只是换了种错法。

九、上线检查清单

工具调用的水平,不体现在"能不能调起来",而体现在出错的时候你能不能立刻说清是哪一层坏的,以及成功的时候你能不能证明它真的成了。这两条做到了,Agent 才从演示进到生产。


相关阅读


📚 更多创作资源: https://afdian.com/a/felix007