06.01概念更新于 2026年9月17日

Function Calling 原理

模型如何选择工具、填写参数、读取结果并继续判断。

06.01 · 课时讲解

Function Calling 原理

模型只输出结构化的调用请求,执行工具的始终是你的程序。

1 / 12

一句话理解:模型从来不亲自执行工具。它只会输出一段结构化的“调用请求”(调哪个工具、填什么参数),由你的程序去执行,再把结果交回给模型继续判断。

很多人第一次看到 Agent 查订单、发邮件、改代码,会以为模型“连上了系统”。其实模型仍然只会生成文本——只不过这段文本被约定成了机器可读的 JSON。真正碰到数据库、API 和文件系统的,始终是你写的程序。

弄懂这一点很关键:权限应该加在哪、错误从哪来、成本花在哪、为什么 Agent 会“编造”一个不存在的参数,答案都藏在这个往返过程里。

先看全景:下图把本模块的核心概念放在一张图里——模型读工具定义做判断,程序负责执行与把关,工具设计、安全与编排是让它可靠运行的支撑体系。

工具调用全景:用户任务进入程序,模型读工具定义并发出调用请求,程序校验、执行、回填,外加工具设计、安全与编排三条支撑体系 用户与任务 用户问题 系统指令 对话历史 每一轮请求 都重新带上 你的程序 · Harness:带上工具定义请求模型 → 校验并执行调用 → 回填结果 → 循环直到给出回答 工具定义:模型唯一能读到的 名称 name 能不能被找到 描述 description 会不会被用对 参数 Schema 填的值合不合法 strict:参数严格匹配 Schema 三要素决定模型的选择 模型:只负责判断 模型 LLM 要不要调 · 调哪个 · 填什么 tool_use 调用请求 + 参数 追加结果,再请求一次 不再请求工具时 输出最终回答,循环结束 程序:执行与把关 ① 校验参数 格式 · 归属 · 范围 ② 权限与确认 读自动 · 写需确认 ③ 执行工具 调用真实系统 ④ 回填 tool_result 结果或 is_error 工具来源 自定义工具 get_order 等 内置工具 搜索 · 代码 · 文件 MCP 工具 跨产品复用 外部系统 订单 · 支付 · 工单 工具设计 面向任务而非接口 · 粒度适中、数量克制 · 命名空间 · 返回精简高信号 · 错误信息可行动 安全设计 读写分离 · 身份参数由程序注入 · 参数校验 · 预演 / 确认 / 幂等 · 工具结果视为不可信 编排与性能 并行调用 · 依赖调用链 · 固定链路交给程序 · 分页 / 摘要 / 截断 · 超时与部分失败
图 1 · 工具调用全景:模型负责判断,程序负责执行与把关,底部三条是让工具可靠的支撑体系

直觉:模型是点菜的人,程序是后厨

把一次工具调用想象成在餐厅点菜:

  • 菜单是工具定义:每道菜叫什么、是什么、可以选什么口味。模型只能从菜单上点。
  • 点菜单是模型输出的调用请求:“我要 get_order,参数 order_id = ORD2026090142”。
  • 后厨是你的程序:检查点菜单合不合法、有没有这道菜、这位客人能不能点,然后真正去做。
  • 上菜是工具结果:程序把 JSON 结果送回对话里,模型看到结果后决定继续点菜,还是直接回答。

所以,模型负责 判断(要不要调用、调用哪个、参数怎么填、结果说明了什么),程序负责 执行与把关。这就是 一个 Agent 由什么构成 里 Model 与 Tools 的分工。

一次完整的往返

以电商客服场景为例,用户问:“订单 ORD2026090142 为什么还没发货?”

整个往返过程如下图:

一次工具调用的往返时序:程序、模型与订单系统之间的五个步骤 模型 只生成调用请求 你的程序 Harness 执行与把关 订单系统 真实数据 ① 用户问题 + 工具定义 ② 调用请求 get_order(...) ③ 校验参数、检查权限 调用订单接口 返回 JSON:待发货,支付待确认 ④ 追加 tool_result,再次请求 ⑤ 继续调用,或输出最终回答
图 2 · 一次完整的往返:模型只提出请求,碰到订单系统的始终是程序

几个容易忽略的细节:

  1. 每次请求都要带上工具定义。 模型没有记忆,工具的名称、描述和参数 Schema 每次都会作为输入 Token 计费。工具越多、描述越长,成本越高(详见 Token 与上下文窗口)。
  2. 一轮可能有多个调用。 模型可以在同一轮里同时请求 get_order 和 get_payment,你要把每个结果都对应回去,见 并行调用与调用链。
  3. 循环由程序驱动。 只要模型还在请求工具,程序就执行并回填;模型给出普通回答时循环结束。这正是 Agent Loop 详解 的最小形态。

两家主流 API 的字段对照

概念一样,字段名不同。评审研发方案或读 Trace 时,认得这些字段就够了(以下对照 Claude Messages API 与 OpenAI Responses API 的官方文档):

环节 Claude(Messages API) OpenAI(Responses API)
声明工具 tools:name、description、input_schema tools:type: "function"、name、description、parameters
模型发起调用 响应的 stop_reason 为 tool_use,content 里有 tool_use 块(id、name、input 对象) output 里有 function_call 项(call_id、name、arguments 为 JSON 字符串)
回传结果 下一条 user 消息里放 tool_result 块(tool_use_id、content,出错时 is_error: true) 输入里追加 function_call_output 项(call_id、output)
控制是否调用 tool_choice:auto / any / tool / none tool_choice:auto / required / 指定函数 / none,另有 allowed_tools
严格匹配 Schema 工具定义上加 strict: true 工具定义上加 strict: true
关闭并行调用 tool_choice 里设 disable_parallel_tool_use: true parallel_tool_calls: false

下面是 Claude 格式下的一轮真实结构(精简版):

{
  "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "我先查一下这个订单的状态。" },
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835lq9",
      "name": "get_order",
      "input": { "order_id": "ORD2026090142" }
    }
  ]
}

程序执行后回传:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "{\"status\":\"pending_shipment\",\"reason\":\"payment_pending\",\"updated_at\":\"2026-09-16 18:02\"}"
    }
  ]
}

Claude 的文档特别强调两条格式规则:tool_result 必须紧跟在包含 tool_use 的那条助手消息之后;同一条用户消息里,所有 tool_result 要排在文字前面。违反时会直接报 400 错误。

模型如何“选择”工具

模型能看到的只有三样东西:工具名称、描述、参数 Schema,加上对话上下文和你的系统指令。它不会读你的源码,也不知道接口背后连的是哪张表。因此:

  • 选错工具,多半是 描述写得含糊,或两个工具职责重叠(get_order 与 query_order_info 并存)。
  • 参数填错,多半是 Schema 没说清格式,比如没说订单号是 ORD 开头加 10 位数字。
  • 该调不调、不该调乱调,可以通过系统指令和 tool_choice 调节。默认的 auto 让模型自己决定;强制调用适合“这一步必须查数据”的环节。

还要记住,参数是模型 生成 出来的,可能缺字段、类型不对,甚至凭空猜一个值。Claude 文档就提到,缺少必填信息时,有的模型会追问用户,有的会自己推断一个看似合理的值。所以两道防线必不可少:开启 strict 让参数严格符合 Schema,程序在执行前再做一次业务校验(订单是否存在、用户有没有权限查)。这两件事分别在 工具定义三要素 和 安全的工具设计 里展开。

常见误区

  • “模型调用了我们的 API。” 调用 API 的是程序。模型只提出请求,程序可以拒绝、改写、要求人工确认。这是所有权限设计的前提。
  • “开了 Function Calling,参数就一定对。” 不开 strict 时,Schema 只是尽力遵循;开了 strict 也只保证 格式 对,不保证 业务上 对(格式正确的订单号可能根本不属于这个客户)。
  • “工具结果是可信的。” 工具返回的网页、邮件、工单备注可能夹带恶意指令。结果要当作不可信数据处理,见 Prompt 注入。

产品经理检查清单

  • 这个 Agent 需要哪些工具?每个工具对应用户 Job 里的哪一步?
  • 模型每一轮能看到哪些工具?工具定义大约占多少 Token?
  • 模型给出的参数,程序在执行前会做哪些校验?
  • 工具失败时,返回给模型的错误信息能不能帮它修正下一步?
  • 一次任务最多允许多少轮调用?超过之后怎么收尾(见 停止条件)?
  • Trace 里能不能看到每次调用的名称、参数、结果和耗时?

练习

不写代码,只用 JSON 在纸上走完一次往返(约 30 分钟):

  1. 为电商订单场景写一个 get_order 工具定义(名称、描述、参数 Schema)。
  2. 假设用户问“ORD2026090142 卡在哪一步了”,写出模型应该返回的 tool_use 块。
  3. 编一个订单系统的返回 JSON,写出对应的 tool_result 消息。
  4. 再写一个“订单号不存在”的 tool_result,设置 is_error: true,并让错误信息告诉模型下一步该怎么做。

如果你已经完成 实操:调通第一个 API,可以把第 3 步换成真实接口的返回。

延伸阅读

下一步

模型只能通过名称、描述和 Schema 理解工具。下一课「工具定义三要素」讲怎么把这三样写好。

试试:上下文、MCP、沙箱、Prompt 注入、外链