让 AI 真正做事:工具调用的设计方法 封面
AI 系列

让 AI 真正做事:工具调用的设计方法

工具调用是 Agent 的执行底座。读完你会知道:工具该怎么描述、多个工具如何编排、哪些操作必须拦一道、以及为什么"description 写得好不好"直接决定成功率。

工具调用 核心概念示意图
工具调用:模型决策与程序执行的分工边界
工具调用 工作流程示意图
一次工具调用从请求到结果回灌的完整链路
工具调用 实践检查示意图
权限、重试与人工确认的检查清单

Function Calling 协议长什么样

工具调用(业界常叫 Function Calling 或 Tool Use)的本质,是把"能力"用结构化 schema 描述出来,让模型只负责生成合法的调用请求,真正的执行交给你的程序。模型输出的是一段 JSON,而不是去真的干这件事。

一个工具定义通常包含:name(名字)、description(干什么用)、parameters(用 JSON Schema 描述参数名、类型、是否必填、取值范围)。模型看到这些,就能在合适的时候吐出 {"name": "query_order", "arguments": {"order_id": "8842"}}。注意:模型只给请求,你拿到后校验、执行、再把结果喂回去——这是安全边界的根本来源。

{
  "name": "query_order",
  "description": "按订单号查询订单的当前状态与金额,只读,不产生副作用",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {"type": "string", "description": "订单号,如 8842"}
    },
    "required": ["order_id"]
  }
}

并行调用与依赖编排

现代模型支持一次返回多个 tool_call。当几个子任务彼此独立(查天气、查汇率、查库存),应当并行执行以压低延迟;而当 B 依赖 A 的返回(先查订单号再查物流),就必须串行,等 A 的结果回灌上下文后再发起 B。

为什么重要:能不能并行,取决于数据依赖而非你"想快"。错误地把有依赖的调用并行,会让后一个调用拿到空参数。编排层要能识别"哪些 tool_call 之间无数据边",而不是无脑并发。

沙箱与最小权限原则

工具执行的"程序侧"必须被管住。核心原则是最小权限(Least Privilege):每个工具只拥有完成它那件事所需的最小能力,并且运行在沙箱里——限网络、限文件系统、限可调用资源。模型请求的 order_id 即便被注入恶意内容,沙箱也拦得住它越界访问。

工具执行闭环(含权限边界)
模型生成
tool_call
Schema 校验
+参数清洗
权限层
(只读自动/写需确认)
沙箱执行审计日志
+结果回灌
权限判断在"执行前"而非"执行后",危险操作默认拦截,而非事后补救
踩坑提醒:不要把工具函数直接暴露成"模型想调谁就调谁"。一旦用反射式 getattr(tools, call.name) dispatch,模型就可能拼出一个你没打算开放的函数名。务必用白名单映射:模型给的名字只在你显式登记过的表里查,查不到直接报错。

错误处理、重试与人类确认

工具会失败:超时、参数非法、下游 500、限流。设计上要做到三点:

def dispatch(call):
    if call.name not in REGISTRY:                 # 白名单,杜绝未登记函数
        return error("unknown tool")
    tool = REGISTRY[call.name]
    args = tool.schema.validate(call.arguments)   # 参数校验,失败即报错
    if tool.side_effect and not call.human_approved:
        return need_confirmation(call)            # 危险操作:先等人
    with sandbox(tool.permissions):               # 最小权限沙箱
        return tool.run(args, idempotency_key=call.id)

工具 description 直接决定成功率

这是最容易被低估的一点:模型靠 description 判断"什么时候该用这个工具、传什么参数"。写得含糊,模型就乱用或不用。好的 description 要交代:做什么、何时用、何时不用、参数怎么填、返回值含义、有无副作用。

写法示例结果
"获取用户信息"模型分不清要不要带 token、返回什么,常漏参数
"按 user_id 查询用户资料(昵称、等级),只读;需 user_id,无则先问用户"模型知道何时调用、必填什么、不会瞎编
工程经验:把工具的"使用说明"当成给同事写的文档来写。含糊的 description 等于让模型在猜,而猜错一次的代价是整条 Agent 链路跑偏。

从只读工具起步的安全实践

落地建议非常明确:先只做只读工具。天气、文档检索、数据库查询、知识库搜索——这些无副作用,可以放心自动执行,用来把"调用链路 + 校验 + 日志"跑顺。等这套闭环稳了,再逐步开放带确认的写操作。下面把上一节的闭环扩展成"只读自动、写需确认"的落地姿态:

安全落地:由只读到写
只读工具
(自动执行)
链路稳定
校验/日志/重试
写操作
(HITL 确认)
幂等+审计
才放行
权限边界随能力升级而收紧:能力越强,确认与留痕越强

工具调用把"AI 能想"和"程序能做"清晰地切开:模型只产出结构化的调用意图,真正的执行、权限与后果都由你的代码掌握。把 description 写清楚、把危险操作拦一道、把失败变成可纠正的 observation,Agent 才不至于失控——而这一切,正是上层 MCP 与 Skill 能复用的执行底座。