Agent 开发学习站
Agent 开发›核心›核心

Function Calling:工具使用的底层协议

核心核心

一句话定义

Function Calling 是 LLM API 的原生机制:开发者声明工具的名称、描述与参数 schema,模型在需要时输出一个结构化的"调用请求",由开发者代码执行后再把结果交还模型——模型只"点菜",你的代码"做菜"。

为什么重要

这是 Agent 时代的分水岭技术。2023 年 6 月 OpenAI 函数调用上线之前,让模型用工具要靠提示词哄("请输出形如 TOOL: search 的文本")再用正则抠——脆弱得像在沙滩上盖楼。原生函数调用把工具使用变成模型经后训练习得的二等公民能力:稳定、结构化、可约束(kp-005 的 schema 约束直接生效)。此后 MCP(kp-011)、Agent SDK、Computer Use 全部构建在这层协议之上。

前置知识

kp-005(JSON Schema)、kp-006(消息流)。

核心概念

一次完整的函数调用协议流

python# ① 声明:你告诉 API 有哪些工具可用
tools = [{
  "name": "get_weather",
  "description": "查询指定城市未来 N 天的天气",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {"type": "string", "description": "城市名,如 '北京'"},
      "days": {"type": "integer", "minimum": 1, "maximum": 7}
    },
    "required": ["city"]
  }
}]

resp = client.messages.create(messages=..., tools=tools)

# ② 模型响应(若决定用工具):
# stop_reason="tool_use",内容包含
#   {name:"get_weather", input:{city:"北京", days:2}}
#   ——注意:模型没有执行任何东西,它只是"请求"

# ③ 你的代码执行真正的函数
result = weather_api.query("北京", 2)

# ④ 把结果作为 tool_result 消息回填,再次调用
messages += [assistant_tool_use, user_tool_result(result)]
resp = client.messages.create(messages=..., tools=tools)
# ⑤ 模型基于天气数据生成自然语言回答(或继续调用下一个工具)

关键事实

  1. 模型不执行工具。执行永远发生在你的代码/沙箱里(kp-018)——这个设计同时是安全边界(kp-026:权限闸门就设在③)与调试点。
  2. 工具与"聊天"共用一个上下文。工具定义占用窗口(每个约 100-500 token);工具结果以消息形式回流(kp-003)。
  3. 一次可请求多个调用(parallel tool calls):模型可同时要"查北京+查上海",你应并行执行(kp-027)。
  4. 决策完全由模型分布决定:同一个问题,工具描述写得差它就不用;描述里的措辞就是"决策特征"(kp-010)。

原理与机制

模型侧怎么学会函数调用的?后训练(kp-031):在大量"函数定义 + 对话 + 正确调用轨迹"数据上做 SFT 与 RL,让模型学会三件事——判断该不该调(用户问天气才调,不乱调)、选哪个(依据名称与描述的语义匹配)、参数从哪抽(从上下文实体抽取并填入 schema)。推理时,API 把 tools 编码进上下文,模型生成的调用请求由服务端做 schema 校验,保证返回的 input 一定是合法 JSON(合法≠正确——参数值仍可能是幻觉,需要你校验)。

为什么"参数抽取"值得专门强调:它是 Agent 数据事故的高发区——模型把"下周二"填成 2026-09-22(今天才 09-29)、把用户口述的价格单位搞错。参数级校验(日期范围、数值边界)是生产必做。

直观类比

模型是坐在办公室里的专家,不能出门。你在门口贴了服务清单(tools 声明)。他读清单后写下便条"帮我查北京天气,要 2 天的"(调用请求)递出来;秘书(你的代码)跑腿办事,把结果写在便条背面递回去(tool_result)。专家看到结果后,要么写最终报告,要么再递一张新便条。他永远不出门——这也意味着门卫(权限系统)能检查每一张便条。

实例与案例

一个参数幻觉的真实排查:航班 Agent 偶发把 date 填成过去的日期。

  • 原因:用户说"下周一",模型没有"今天几号"的信息,只能从上下文猜
  • 修复三选一(生产常用组合):① 系统提示注入当前日期;② 工具描述写明"date 必须晚于今天";③ 参数校验失败时返回明确错误 "date 不能早于 2026-09-29"——错误信息本身会引导模型自我修正(kp-010 的核心技巧)

常见误区

  • 误区一:"模型执行了我的函数"。它只输出了调用意图;执行、超时、重试、幂等全是你的责任。
  • 误区二:"工具越多越强"。超过约 20 个工具后选择错误率上升、窗口被挤占——该用工具检索/分组(kp-011 动态启用)或让 Agent 自己写代码组合(kp-018)。
  • 误区三:不处理 tool_choice。多数 API 允许强制/禁止调用(auto/none/required/指定函数),路由类 Workflow 用"强制指定"可消除不稳定性。
  • 误区四:工具结果返回超大 JSON。直接把 500 行数据塞回去 → 上下文爆炸;应返回模型需要的字段或摘要(kp-003)。

自测题

  1. 函数调用协议中,"执行"发生在哪一侧?为什么这个设计同时是安全边界?
  2. 模型的函数调用能力从何而来?schema 保证了什么、不保证什么?
  3. 并行 tool calls 为什么重要?

(参考答案:1. 开发者侧;权限与沙箱闸门设在执行前后,模型永远接触不到真实副作用;2. 后训练(SFT/RL);保证结构合法,不保证参数值正确;3. 独立调用并行执行可把延迟从"串行求和"降到"最大单次"。)

与其他知识点的关系

  • 工具的描述与错误设计 → kp-010;协议的标准化 → kp-011
  • 在最小 Agent 中的落地 → kp-017;执行环境安全 → kp-018

延伸阅读

  • OpenAI, Function Calling 文档与 2023.6 发布博客
  • Anthropic, Tool Use (function calling) 官方文档(tool_use/tool_result 消息结构)