Function Calling(函数调用)是让大模型能够调用外部工具的核心能力,也是开发 AI Agent 的基础。简单说,就是你告诉模型"你有哪些工具可以用",模型根据用户的问题决定调哪个工具、传什么参数。

听起来很简单?实际开发中坑不少。我上周就遇到一个:同一套工具定义,GPT-5.4 调用正常,Claude 4.6 偶尔漏参数,Gemini 2.5 直接把 JSON 搞坏。排查两天发现各家对参数格式的处理逻辑不一样。

这篇文章把三家的 Function Calling 能力做了系统实测,告诉你:该怎么选、怎么用、怎么避坑。

什么是 Function Calling?

先用一个例子说明白。假设你在做一个购物助手,用户说"帮我搜一下 500 块以下的蓝牙耳机"。

没有 Function Calling 的模型:只能回复一段文字,告诉你去哪里搜。

有 Function Calling 的模型:直接输出一个结构化的调用请求:

{
  "name": "search_products",
  "arguments": {
    "query": "蓝牙耳机",
    "price_range": { "min": 0, "max": 500 },
    "sort_by": "rating"
  }
}

你的后端拿到这个 JSON,调用真正的商品搜索 API,把结果返回给模型,模型再组织成自然语言回复用户。

整个流程:用户说话 → 模型决策调哪个工具 → 输出结构化参数 → 后端执行 → 结果返回模型 → 模型回复用户。

工具定义怎么写?

使用 Function Calling 的第一步是定义工具。三家模型都支持 JSON Schema 格式,这里给一个完整的例子:

{
  "tools": [
    {
      "name": "search_products",
      "description": "搜索商品信息,支持按类目、价格区间和排序方式筛选",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "搜索关键词,比如'蓝牙耳机'、'机械键盘'"
          },
          "category": {
            "type": "string",
            "enum": ["electronics", "clothing", "food"],
            "description": "商品类目:electronics=电子产品,clothing=服装,food=食品"
          },
          "price_range": {
            "type": "object",
            "description": "价格范围,包含最低价和最高价,单位为人民币元",
            "properties": {
              "min": { "type": "number", "description": "最低价(元)" },
              "max": { "type": "number", "description": "最高价(元)" }
            }
          },
          "sort_by": {
            "type": "string",
            "enum": ["price", "rating", "sales"],
            "description": "排序方式:price=按价格,rating=按评分,sales=按销量"
          }
        },
        "required": ["query"]
      }
    },
    {
      "name": "create_order",
      "description": "创建订单,需要商品ID、数量和收货地址",
      "parameters": {
        "type": "object",
        "properties": {
          "product_id": { "type": "string", "description": "商品唯一ID" },
          "quantity": { "type": "integer", "minimum": 1, "description": "购买数量" },
          "shipping_address": {
            "type": "object",
            "description": "收货地址,必须包含城市和街道",
            "properties": {
              "city": { "type": "string", "description": "城市" },
              "street": { "type": "string", "description": "详细街道地址" },
              "zip": { "type": "string", "description": "邮编(选填)" }
            },
            "required": ["city", "street"]
          }
        },
        "required": ["product_id", "quantity", "shipping_address"]
      }
    },
    {
      "name": "get_weather",
      "description": "获取指定城市的天气信息",
      "parameters": {
        "type": "object",
        "properties": {
          "location": { "type": "string", "description": "城市名称,如'北京'、'上海'" },
          "date": { "type": "string", "format": "date", "description": "日期,格式 YYYY-MM-DD" }
        },
        "required": ["location"]
      }
    }
  ]
}

关键技巧description 写得越详细,模型调用越准确。别只写"价格范围",要写清楚"包含最低价和最高价,单位为人民币元"。这一个改动就能把参数错误率降低 60%+。

三家模型实测对比

我设计了 900 个测试用例,覆盖 6 个维度,每个模型跑 3 轮取平均值。

测试维度说明

维度 测试什么 用例数 举例
基础调用 意图明确,参数齐全 200 “搜索蓝牙耳机”
参数推理 用户没说全,模型需要补全 200 “搜便宜的耳机”(没指定类目)
多工具编排 一个请求要调多个工具 150 “查北京天气,顺便搜户外外套”
嵌套对象 参数里有 object/array 150 带 shipping_address 的订单
拒绝调用 用户意图不匹配任何工具 100 “给我讲个笑话”(不该调工具)
错误恢复 工具返回错误后怎么处理 100 首次调用参数错,能否自行修正

结果汇总

模型 基础调用 参数推理 多工具编排 嵌套对象 拒绝调用 错误恢复 综合得分
Claude Opus 4.6 99.5% 94.2% 91.3% 96.7% 97.0% 88.3% 94.5%
GPT-5.4 99.0% 96.1% 93.7% 91.2% 95.0% 92.7% 94.6%
Gemini 2.5 Pro 98.5% 90.8% 86.4% 84.3% 93.0% 85.0% 89.7%

结论:Claude 和 GPT 第一梯队,几乎打平但各有偏科。Gemini 简单场景够用,复杂场景差距明显。

各家的坑和解决方案

Claude 4.6:稳但偶尔"懒"

优点:嵌套对象处理最稳(96.7%),拒绝调用最准(不该调的坚决不调)。

tool_choice: "auto" 模式下,约 3% 的情况会在该调工具时选择纯文本回复。多工具场景更容易触发。

解决方案

# 关键业务流程,强制模型必须调用工具
response = client.messages.create(
    model="claude-opus-4-6-20260325",
    messages=messages,
    tools=tools,
    tool_choice={"type": "any"}  # 强制调用工具
)

另一个小问题:多工具场景下,Claude 有 8.7% 概率串行调用(先调 A 等结果再调 B),虽然最终结果正确但多一轮交互。加个 system prompt 提示能缓解:

当用户请求涉及多个工具调用且工具之间没有数据依赖时,请并行调用以提高效率。

GPT-5.4:编排强但格式偶尔翻车

优点:多工具编排最强(93.7%),能稳定地并行调用多个工具,参数推理能力也最好。

:嵌套 object 参数偶尔被序列化成字符串,概率约 8.8%:

// ❌ 错误输出
{ "price_range": "{\"min\": 100, \"max\": 500}" }

// ✅ 正确应该是
{ "price_range": { "min": 100, "max": 500 } }

解决方案:在 schema 的 description 里显式写明结构(参考上面的工具定义示例),错误率从 8.8% 降到 2.1%。

Gemini 2.5 Pro:便宜但要多写防御代码

坑 1:嵌套对象 15.7% 的 JSON 格式错误(多逗号、少括号)。
坑 2:有时在调用工具之前先输出一段"分析文字",导致解析困难。

解决方案:必须加一个容错解析层:

import json
import re

def safe_parse_arguments(raw_args):
    """处理各种模型输出的参数格式差异"""
    if isinstance(raw_args, str):
        try:
            return json.loads(raw_args)
        except json.JSONDecodeError:
            # 修复常见格式问题:尾部多余逗号
            cleaned = re.sub(r',\s*}', '}', raw_args)
            cleaned = re.sub(r',\s*]', ']', cleaned)
            return json.loads(cleaned)
    return raw_args

选哪个?一张表说清楚

你的场景 推荐模型 原因
客服机器人 Claude 4.6 不该调工具时坚决不调,安全性最高
数据处理流水线 GPT-5.4 并行调用省时间,编排能力最强
简单单工具调用 三家都行 基础调用差距不大,选最便宜的
参数结构复杂 Claude 4.6 嵌套对象格式正确率最高
预算有限 Gemini 2.5 单价低,但要算上重试成本

实际花了多少钱?

900 个测试用例 × 3 轮的实际消耗:

模型 总费用 含重试的费用 重试成本占比
Claude Opus 4.6 $41.7 $43.2 3.6%
GPT-5.4 $38.5 $39.8 3.4%
Gemini 2.5 Pro $22.3 $26.1 17.0%

Gemini 单价最低,但重试成本占比高达 17%——因为格式错误需要重新调用。实际场景如果工具调用频繁,这个差距会更大。

4 个马上能用的优化技巧

1. Schema description 写详细

最简单有效的优化。把所有参数的 description 写清楚,包括类型、单位、示例值。对所有模型都有效。

2. 加错误重试机制

MAX_RETRIES = 2

async def call_with_retry(client, messages, tools):
    for attempt in range(MAX_RETRIES + 1):
        response = await client.chat(messages, tools=tools)
        tool_calls = parse_tool_calls(response)

        if validate_tool_calls(tool_calls, tools):
            return tool_calls

        # 把错误反馈给模型让它修正
        messages.append({
            "role": "tool",
            "content": f"参数格式错误: {get_validation_error(tool_calls)},请修正"
        })

    raise ToolCallError("重试次数耗尽")

3. 多工具场景加 prompt 提示

规则:
- 工具之间无数据依赖 → 并行调用
- 后一个工具需要前一个结果 → 串行调用并说明原因

4. 统一多模型调用接口

如果你需要在项目中对比或切换不同模型,维护三套 SDK 很麻烦。可以用 API 聚合方案统一 endpoint,比如我测试时用的 ofox,一个地址切换三家模型,省去了分别配置的工作量。代码里只需要改 model 参数:

from openai import OpenAI

client = OpenAI(
    api_key="your-key",
    base_url="https://api.ofox.ai/v1"  # 统一入口
)

# 切换模型只需改这一行
response = client.chat.completions.create(
    model="claude-opus-4-6-20260325",  # 或 "gpt-5.4" 或 "gemini-2.5-pro"
    messages=messages,
    tools=tools
)

常见问题

Function Calling 和 Prompt 让模型输出 JSON 有什么区别?

Function Calling 是模型原生支持的结构化输出能力,有专门的训练和优化,格式正确率远高于"请你输出 JSON"。而且 Function Calling 支持 tool_choice 参数控制模型是否必须调用工具,灵活度更高。

工具定义太多会影响性能吗?

会。工具定义会占用 context window,工具越多模型选择越困难。实测超过 15 个工具后准确率开始下降。建议按场景分组,每次只传相关的工具子集。

本地部署的开源模型支持 Function Calling 吗?

部分支持。Qwen2.5(72B+)和 Llama 3.3(70B+)都有 Function Calling 能力,但准确率和商用模型还有差距,尤其是多工具编排和嵌套对象场景。如果是生产环境,建议还是用商用 API。


最后更新:2026-03-25

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐