[AI/LLM/Agent/工具调用] Function Calling(函数调用):让大模型"动手干活"的【标准化工具调用机制】

0 序

  • Function Calling 的定位:
  • AI Agent 的四大组件(系统能力): 规划、工具调用、记忆、反思;而 Function Calling 作为 【工具调用】能力最底层的标准化机制。
  • 作为大模型 API 提供的一种"契约"——你定义工具,模型决定何时调用并输出结构化参数,你的代码负责真正执行。它是从"聊天机器人"走向"AI Agent"的基石能力。

1 概述

1.1 概念介绍

  • Function Calling(函数调用,简称 FC,也叫 Tool Calling / Tool Use)是大型语言模型(LLM)与外部系统交互的一种标准化机制:

    • 开发者把可调用的函数清单(名称、用途说明、参数结构 JSON Schema)通过 API 传给模型;

    • 模型在推理时判断:当前用户请求是否需要调用某个工具?如果需要,就不直接输出自然语言答案,而是输出一个结构化请求:{"name": "get_weather", "arguments": {"city": "北京"}}

    • 真正执行函数的是你的代码(查数据库、调 HTTP 接口、发邮件等),执行结果再回传给模型;

    • 模型基于工具返回的结果,生成最终的自然语言回答。

  • 关键认知:大模型本身永远不会直接执行任何操作,它只是一个"会说话的调度器",负责决策和格式化参数,执行权始终在开发者的应用代码里。

官方定义(OpenAI):"函数调用工具调用,指的是模型在检查提示词后,判定为了执行指令而需要调用我们所提供的某个工具时,所发出的特殊类型响应。"[1]

1.2 诞生的背景与解决的核心问题

  • 背景:传统 LLM 只会"说",不会"做"——它无法访问实时数据(天气、行情、数据库)、无法操作系统(发邮件、下单、写文件)、无法执行动作(控制设备、运行代码),训练数据也有截止时间。

  • 解决的核心问题:让大模型突破"纯文本生成"的边界,与外部世界(实时数据源、业务系统、API、文件系统)建立受控、结构化、可执行的连接。没有 Function Calling 之前,开发者只能用"让模型按特定文本格式输出 → 正则表达式解析"的土办法(ReAct 风格),格式不稳定、解析痛苦。

1.3 发展历程 (必读)

时间 里程碑
2023 年初 Prompt 时代:工具调用靠提示词工程 + ReAct 文本协议(Thought/Action/Observation),用正则解析,格式极易出错,被戏称"每天 debug JSON 五小时"[2]
2023-06-13 里程碑:OpenAI 在 gpt-3.5-turbo-0613 / gpt-4-0613 中【首次正式发布】 Function Calling,引入 functions 参数与 function_call 返回字段,首次把工具调用做成 API 一等公民[3]
2023-11 OpenAI DevDay 升级为 tools / tool_calls 参数,支持并行调用,旧字段保留兼容[2][4]
2023-2024 【全行业跟进】:Anthropic(Claude)、Google(Gemini)、Meta(Llama)、国内 Qwen / DeepSeek / GLM / Kimi 等均推出对应能力,OpenAI 兼容协议成为事实标准
2024-08 OpenAI 推出 Structured Outputs / strict 模式,用约束解码保证函数调用严格符合 JSON Schema[5]
2024-11 里程碑:Anthropic 开源 MCP(Model Context Protocol),解决"每个应用都要重写一遍工具"的复用问题,工具生态进入协议化阶段[2]
2025-至今 MCP 规范持续演进(Streamable HTTP、OAuth 2.1 鉴权);Structured Outputs / xgrammar 等约束解码成为默认能力;各厂商推出托管 Agent 平台,Function Calling 被封装为"注册工具 + 写提示词"
  • 演进三阶段:Prompt 工程(正则解析,格式不稳)→ 模型专训(SFT 加入工具调用语料,仍有幻觉字段)→ 约束解码(解码时强制满足 JSON Schema,格式有硬保证)[2]。

1.4 核心优势 (必读)

  1. 稳定性:结构化 JSON 输出(尤其 strict 模式),替代脆弱的文本协议 + 正则解析
  2. 可执行性:模型只决策、代码来执行,天然支持鉴权、权限控制、审计,安全边界清晰;
  3. 实时性:让模型访问训练数据之外的最新信息(天气、行情、数据库);
  4. 可组合:支持多轮调用、并行调用、工具链串联,是构建 Agent 的地基
  5. 生态成熟:OpenAI 兼容协议被几乎所有主流模型支持,一份代码可切换多家。

1.5 主要短板与局限性 (必读)

  1. 幻觉与误调用:模型可能漏参、填错参数、在不该调用时触发(误触发)或该调用时跳过(漏调用);
  2. Token 成本工具定义(schema)会注入到上下文并计入输入 token工具多则成本高
  3. 上下文压力:多轮 Agentic Loop 会把每次的工具结果都加入对话历史,【长任务】容易撑爆上下文
  4. 安全风险:工具结果可能被注入恶意指令(prompt injection / 工具污染),模型可能被诱导调用危险工具;
  5. 各家协议不统一:OpenAI、Claude、Gemini 的字段名和消息格式有差异,跨平台切换需要适配。

1.6 适用场景 (必读)

  • 实时信息查询:天气、股票行情、新闻、数据库检索;
  • 业务操作:下单、订票、发邮件、创建工单;
  • 代码与文件操作:运行代码沙箱、读写文件、数据转换;
  • 智能助手/客服:结合企业知识库与业务系统回答并代办;
  • Agent 工作流:多步骤、多工具串联的自动化任务;
  • 结构化数据提取:强制输出指定 JSON 结构(配合 tool_choice: required)。

1.7 相关概念辨析(最易混淆) (必读)

概念 定义 与 Function Calling 的关系
Function Calling 【模型】发出结构化【调用请求】的机制 本体,管"工具怎么调给模型"
Tool / Tool Use 实际可被调用的能力(自定义函数或厂商内置工具),Tool Use 是更广义的叫法 Tool 是 FC 的调用对象;Anthropic 一直叫 Tool Use
LLM --(Tool Use)--> 应用 --(Tool Call)--> Tool
MCP 连接、发现、调用工具的一套开放协议(Anthropic 2024-11 开源) MCP 管"工具从哪来",最终仍会转成 function call 给模型[2]
Skill 把指令、脚本、资料、流程打包并按需加载的能力包 比工具更高层,内部常借助 FC 实现
Structured Outputs 强制模型输出符合指定 JSON Schema 的格式约束 同源的约束能力;无副作用场景用 SO,有副作用场景用 FC[6]
ReAct 让模型"思考-行动-观察"循环的推理框架 FC 出现前用文本协议实现;FC 是其更稳定的实现载体
Agent 能自主规划、调用工具、多轮完成任务的应用 FC 是 Agent 的核心能力之一,没有 FC 的 Agent 是空谈

总结:MCP 管"工具从哪来",Function Calling 管"工具怎么调给模型",你的应用代码管"工具谁来执行"

1.8 主流模型支持情况(2026 年现状)

  • 接口协议对比(初级开发者在切换平台时最容易踩坑):
维度 OpenAI / 国内兼容模型 Anthropic Claude Google Gemini
工具注册字段 tools[].function + parameters tools[].input_schema tools[].functionDeclarations + .parameters
调用返回位置 message.tool_calls[] content[].type="tool_use" candidates[].content.parts[].functionCall
停止信号 finish_reason: "tool_calls" stop_reason: "tool_use" finishReason
工具结果回传 role: "tool" role: "user" 包裹 tool_result functionResponse part
并行调用
strict 模式 strict: true strict: true
  • 国内生态:阿里云百炼(DashScope)确认千问(Qwen)、DeepSeek、GLM、Kimi、MiniMax 等文本生成模型均支持 Function Calling[7];字节豆包(Doubao Seed 2.0 Pro)在 τ-bench 工具调用基准上表现领先(约 90.4%)[8];DeepSeek 提供 OpenAI 兼容接口并支持 strict 模式(Beta)[6]。

  • 开源可本地部署:Qwen、GLM、DeepSeek、Llama 等开源模型均支持工具调用,适合数据不出境/私有化场景。

1.9 发展趋势

  1. 协议即生态:MCP 正在走 LSP 当年走过的路,工具生态从"应用内绑定"走向"协议内流通"[2];
  2. 解码即合约:Structured Outputs + xgrammar 等硬约束会成为 API 默认能力,"让模型填对 schema"不再是提示词工程问题[2];
  3. Agent 即 Runtime:工具循环、状态机、观测性、权限被封装进托管 Agent 平台(OpenAI Agents Platform、Anthropic Agents、阿里 PAI、字节方舟),开发者只需"注册工具 + 写提示"[2];
  4. 安全左移:工具污染、间接注入、confused deputy 等安全问题被前置到开发流程[2]。

2 工作原理与架构

2.1 概念术语

术语 含义
Schema(工具定义) 用 JSON Schema 描述工具的名称、用途、参数结构
tools 参数 请求中传给模型的工具清单(数组)
tool_choice 控制模型调用策略:auto(自主)/ required(必须调用)/ none(禁止)/ 指定某函数
tool_calls 模型返回的调用请求(含 name + arguments)
tool_result 你的代码执行后回传给模型的结果
Agentic Loop 应用驱动的 while 循环:反复"模型请求工具 → 代码执行 → 结果回传",直到模型给出最终答案
strict 模式 约束解码保证函数调用严格符合 Schema
并行调用 一次响应中模型调用多个独立工具
客户端工具 / 服务端工具 前者由你的代码执行;后者由模型厂商托管执行(如 Anthropic 的 web_search、code_execution)[6]

2.2 核心原理:三方契约

Function Calling 的本质是三方契约[6]:

你定义工具 Schema(名称 + 描述 + 参数类型)
        ↓
模型决定何时调用 + 输出结构化请求(不执行!)
        ↓
你的代码执行并返回结果
        ↓
模型基于结果生成最终回答
  • 模型 = 决策器:判断"该不该调用、调哪个、参数填什么";
  • 代码 = 执行器:真正跑函数,掌握数据和副作用;
  • Schema = 契约:让两方用统一的结构化语言对话。

2.3 工作流程(Agentic Loop)(必读)

  • 完整的多轮工具调用循环如下(理解 FC 的核心):
sequenceDiagram participant U as 用户 participant App as 你的应用(Agentic Loop) participant LLM as 大模型 participant Tool as 你的函数/外部API U->>App: 提问:"北京天气如何?" App->>LLM: 请求(tools清单 + 用户消息) LLM-->>App: 返回 tool_calls: get_weather(city=北京) App->>Tool: 执行 get_weather("北京") Tool-->>App: 返回 {"temp":22,"condition":"晴"} App->>LLM: 回传(原消息 + tool_calls + tool_result) LLM-->>App: 生成最终回答"北京今天22℃,晴" App-->>U: 展示回答
  • 规范化的循环判定(以 Claude 为例)[6]:
stop_reason 含义 你要做的
tool_use 模型要调用工具 执行工具 → 回传结果 → 继续循环
end_turn 模型给出最终答案 读取 content,循环结束
max_tokens 输出 token 超限 按需截断处理
refusal 模型拒绝继续 检查内容策略

总结:while stop_reason == tool_use: 执行工具、回传结果;直到模型返回最终答案。

2.4 关键技术点

  1. 工具定义的 Token 成本:函数定义会被注入到系统消息中,计入输入 token[1]。所以工具数量要克制description 要精简
  2. strict 模式要求:开启时要求 additionalProperties: false 且所有字段标 required,否则请求会被拒绝[1];
  3. 并行调用:一次可返回多个 tool_calls,需按 index 分别执行并逐个回传结果[1];
  4. 流式工具调用:arguments 会按 index 分段到达,需自行拼接 JSON,等 finish_reason 后再真正发起工具调用[2];
  5. 服务端工具:部分厂商提供托管执行的工具(搜索、代码执行、网页抓取),无需自己维护环境[6]。

3 使用指南

3.1 环境准备

  • Python 3.8+,安装 openai 库(国内模型如 DeepSeek/豆包等也大多提供 OpenAI 兼容接口,改 base_url 即可):

    • pip install openai
  • 注册对应模型服务商账号并获取 API Key(OpenAI / DeepSeek / 火山引擎豆包 / 阿里云百炼等)

3.2 最小可运行示例(OpenAI 兼容协议,Python)(必读)

以 DeepSeek 官方接口为例(国内直连;换其他 OpenAI 兼容服务只需改 base_urlmodel):

import json
from openai import OpenAI

client = OpenAI(api_key="你的KEY", base_url="https://api.deepseek.com/v1")

# ── 1. 定义工具(Schema)──
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "获取指定城市的当前天气。仅在用户明确询问天气时调用。",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名,如'北京'"}
            },
            "required": ["city"],
            "additionalProperties": False
        },
        "strict": True
    }
}]

# ── 2. 真正的函数(由你的代码执行)──
def get_weather(city: str) -> str:
    # 真实场景这里调用天气 API
    return json.dumps({"city": city, "temp": 22, "condition": "晴"})

# ── 3. Agentic Loop ──
messages = [{"role": "user", "content": "北京今天天气怎么样?"}]

for _ in range(5):  # 设置最大轮数,防止死循环
    resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        tools=tools,
        tool_choice="auto"
    )
    msg = resp.choices[0].message
    if not msg.tool_calls:      # 模型直接给出最终回答
        print("回答:", msg.content)
        break
    # ── 4. 执行模型请求的每个工具并回传结果 ──
    messages.append(msg)        # 先把模型的 tool_calls 加入历史
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = get_weather(args["city"])
        messages.append({
            "role": "tool",
            "tool_call_id": tc.id,
            "content": result
        })
  • 运行逻辑:模型判断"需要调用 get_weather" → 返回 tool_calls → 你的代码执行 → 结果回传 → 模型生成最终回答"北京今天 22℃,晴"。

3.3 关键操作要点(最佳实践)(必读)

  1. description 写清楚"何时调用、何时不调用":这是模型判断的唯一依据,写得好坏直接影响调用成功率[6];
  2. 少即是多:单轮可用工具尽量 < 20 个,工具多准确率下降、token 成本上升[1];
  3. 把已确定的参数用代码传:不要让模型填你已经知道的参数(如 order_id),减少出错面[1];
  4. 默认开启 strict 模式:生产环境强烈建议 strict: true,保证格式合规[1][6];
  5. 工具报错要回传错误信息:把错误包装成 tool_result 回传,让模型决定重试还是换策略,而不是中断循环[6];
  6. 设置最大循环轮数 + 超时:防止多轮调用死循环;
  7. 做工具链路可观测:记录每次 tool_call 的名称、参数、耗时、状态,用于排查和账单归因[2]。

3.4 补充建议

  1. 先跑通上面的最小示例,理解"模型不执行、代码执行"这个核心心智;
  2. 上手 tool_choice 的四种取值,理解调用控制;
  3. 学习 strict 模式与 Structured Outputs 的区别;
  4. 练习多工具 + 并行调用场景;
  5. 再进阶到 ReAct/Agent 框架(如 LangChain/LlamaIndex)和 MCP 协议;
  6. 最后关注安全:工具白名单、结果注入防护、权限最小化。

Z FAQ for Function Calling

Q1:Function Calling 和普通 API 调用有什么区别?(必读)

  • 普通 API 是"人/程序直接调用接口";FC 是"模型替你决定调哪个接口、传什么参数,再由你的代码执行"。
    • 普通API:【开发者/程序】是决策者
    • FC:【模型】是决策者,不是执行者。

Q2:模型会真的"执行"我的函数吗?(必读)

不会。模型只输出结构化的调用意图(函数名 + JSON 参数),真正执行一定发生在你的应用代码里。这是设计使然——保证权限审计安全可控。

Q3:Function Calling 和 MCP 是同一个东西吗?(必读)

不是。

  • FC 是"模型 ↔ 工具"的调用机制(应用内);
  • MCP 是"跨应用复用工具"的开放协议(应用间)
    • MCP 的 server 最终仍会以 function call 形式把工具暴露给模型。

Q4:工具调用会消耗多少 token?为什么账单变贵了?

工具定义(tools 数组)、tool_use 块、tool_result 块都计入输入 token。降本手段:精简 description、减少一次暴露的工具数量、复用工具定义、开启 prompt caching[6]。

Q5:为什么模型老是漏参或填错参数?(必读)

常见原因:description 写得不清楚、参数没有 enum 约束、没有开 strict 模式、工具数量太多。逐项排查即可。

Q6:工具执行出错(如数据库超时)怎么办?

不要把错误吞掉或直接中断,把错误信息作为 tool_result 回传给模型,让它重试或换策略[6]。

Q7:什么时候该用 Structured Outputs 而不是 Function Calling?(必读)

  • 无副作用、只想强制输出某个 JSON 结构(如信息抽取)→ 用 Structured Outputs;
  • 需要触发外部操作(API 调用、写库)→ 用 Function Calling[6]。

Q8:OpenAI、Claude、国内模型能一套代码通用吗?

大部分国内模型和 OpenAI 兼容,改 base_url + model 即可;Claude 的字段名(input_schematool_resultstop_reason)不同,需单独适配。

Y 推荐文献

X 参考文献

posted @ 2026-08-27 13:06  数据知音  阅读(3)  评论(0)    收藏  举报