Function Calling完整调用教程

Function Calling 完整调用教程:让模型自己调你的函数

Function Calling(函数调用)解决的是同一个核心问题:让模型输出的不再是自由文本,而是一个结构化的调用请求。

传统的 AI 对话,模型给你一段文字,你再自己解析这段文字去执行操作。Function Calling 改变了这个范式------模型直接告诉你「我要调用 get_weather,参数是 location=杭州」,你负责执行函数、把结果传回去,模型再基于真实数据给出最终回答。整个过程中,模型只负责决策,不负责执行。

这套机制是 OpenAI 在 2023 年 6 月引入的(gpt-3.5-turbo-0613 / gpt-4-0613),随后被 Claude、DeepSeek 等各大平台兼容。现在主流的 Function Calling 实现已经相当成熟,支持并行调用(一次回复触发多个函数)、严格模式(Structured Outputs 保证参数格式分毫不差)。

本文聚焦「怎么写对」,完整覆盖:tool 定义 → 调用 → 解析返回 → 执行函数 → 回传结果的闭环。关于报错的具体原因和解决方案,参见另一篇《JSON 模式/结构化输出报错怎么办?》。


1. 完整调用流程:一次对话,两次请求

Function Calling 通常需要两次模型调用才能完成一轮完整交互:

复制代码
用户输入
    ↓
第一次请求 → 模型判断要调哪个函数 → 返回 tool_calls
    ↓
你执行函数,获取结果
    ↓
第二次请求 → 把函数结果传回去 → 模型生成最终回复

下面用 Python 演示这个闭环。

1.1 定义工具(tools)

python 复制代码
from openai import OpenAI
import json

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="YOUR_BASE_URL",  # OpenAI 用 https://api.openai.com/v1
                                # DeepSeek 用 https://api.deepseek.com
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,如杭州、东京"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,默认 celsius"
                    }
                },
                "required": ["location"],
                "additionalProperties": False  # 严格模式建议加,禁止返回未定义字段
            }
        }
    }
]

参数定义要点:

  • type 必须是 "object"
  • required 列出所有必填字段
  • description 字段非常重要,模型靠它理解什么时候该调这个工具
  • additionalProperties: false 防止模型返回未定义的额外字段
  • 工具名称只允许 a-z、A-Z、0-9、_、-,最大 64 字符

1.2 第一次请求:让模型决定调用哪个函数

python 复制代码
messages = [
    {"role": "user", "content": "杭州今天热不热?"}
]

response = client.chat.completions.create(
    model="YOUR_MODEL",  # 如 gpt-4o、claude-sonnet-4-5、deepseek-v4-flash
    messages=messages,
    tools=tools,
    tool_choice="auto"  # auto: 模型自己决定是否调用;required: 强制调用
)

message = response.choices[0].message
print(message.tool_calls)

正常情况下,返回值中 tool_calls 不为空:

python 复制代码
# message.tool_calls 示例
[
    ToolCall(
        id="call_abc123",
        type="function",
        function=Function(
            name="get_weather",
            arguments='{"location": "杭州", "unit": "celsius"}'
        )
    )
]

1.3 解析并执行函数

python 复制代码
def execute_tool(tool_call):
    """根据 tool_call 的 name 和 arguments 执行对应函数"""
    function_name = tool_call.function.name
    arguments = json.loads(tool_call.function.arguments)

    if function_name == "get_weather":
        return get_weather(**arguments)
    else:
        raise ValueError(f"未知函数: {function_name}")


def get_weather(location: str, unit: str = "celsius") -> dict:
    """这里是你的业务逻辑,比如调用真实天气 API"""
    # 模拟返回
    return {
        "location": location,
        "temperature": 28 if unit == "celsius" else 82,
        "unit": unit,
        "condition": "晴转多云"
    }


# 执行所有调用的函数
if message.tool_calls:
    for tool_call in message.tool_calls:
        result = execute_tool(tool_call)
        print(f"函数 {tool_call.function.name} 返回: {result}")

1.4 第二次请求:把结果传回模型

python 复制代码
# 把模型的 tool_calls 和函数执行结果都加到对话历史里
messages.append(message)  # 模型返回的 tool_calls 消息

for tool_call in message.tool_calls:
    result = execute_tool(tool_call)
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(result, ensure_ascii=False)
    })

# 第二次请求
final_response = client.chat.completions.create(
    model="YOUR_MODEL",
    messages=messages,
    tools=tools
)

print(final_response.choices[0].message.content)
# 模型输出:「杭州今天天气晴转多云,气温 28°C。」

这才是用户最终看到的回复。


2. 多工具选择:让模型智能调度

当你有多个工具时,模型会根据用户意图自动选择最合适的一个,或者同时调用多个。

2.1 典型多工具场景

python 复制代码
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的当前天气和空气质量",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "城市名称"}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_flights",
            "description": "搜索两个城市间的航班信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "origin": {"type": "string", "description": "出发城市"},
                    "destination": {"type": "string", "description": "目的城市"},
                    "date": {"type": "string", "description": "出发日期,格式 YYYY-MM-DD"}
                },
                "required": ["origin", "destination", "date"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "发送邮件",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {"type": "string", "format": "email", "description": "收件人邮箱"},
                    "subject": {"type": "string", "description": "邮件主题"},
                    "body": {"type": "string", "description": "邮件正文"}
                },
                "required": ["to", "subject", "body"]
            }
        }
    }
]

2.2 并行调用处理

支持并行的模型(GPT-4o、o3、Claude 4.x、DeepSeek-v4 等)可以一次返回多个 tool_calls:

python 复制代码
# 处理并行调用
tool_results = []

if message.tool_calls:
    for tool_call in message.tool_calls:
        try:
            result = execute_tool(tool_call)
            tool_results.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result, ensure_ascii=False)
            })
        except Exception as e:
            tool_results.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps({"error": str(e)}, ensure_ascii=False)
            })

    messages.append(message)
    messages.extend(tool_results)

并行调用的注意事项:

  • 某些严格模式(strict: true)不支持并行调用,并行时可能不保证参数格式
  • 收到多个 tool_calls 时,所有结果必须全部传回模型才能得到最终回复
  • 如果某个函数执行失败,建议返回错误 JSON 而不是空内容,这样模型可以决定如何处理

2.3 强制调用特定工具

如果你希望模型必须调用某个工具(而不是自己选择是否调用):

python 复制代码
# 强制调用单个工具
response = client.chat.completions.create(
    model="YOUR_MODEL",
    messages=messages,
    tools=tools,
    tool_choice={
        "type": "function",
        "function": {"name": "get_weather"}
    }
)

3. 严格模式(Structured Outputs)

2024 年 6 月,OpenAI 推出 Structured Outputs,DeepSeek 在 Beta API 中也支持了 strict: true。开启后,模型生成的参数会数学级别保证符合你定义的 JSON Schema,不再依赖模型"努力写对"。

3.1 启用严格模式

python 复制代码
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "strict": True,  # 开启严格模式
            "description": "查询天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"],
                "additionalProperties": False
            }
        }
    }
]

严格模式对 Schema 的强制要求(OpenAI 规范):

  • 所有对象必须设置 additionalProperties: false
  • required 数组必须包含对象的所有属性
  • 不支持 prefixItems(用 items 代替)
  • 不支持 minItems / maxItems / minLength / maxLength
  • 支持 patternenumformat(email、hostname、ipv4、ipv6、uuid)

3.2 严格模式 vs 普通模式

特性 普通模式 严格模式(strict: true)
参数格式保证 模型 best-effort 100% 格式保证
Schema 约束 较宽松 严格(必须满足上述要求)
并行调用 支持 不支持
适用场景 快速迭代 生产环境

4. 与报错排查文章的区别

本文解决的是「代码怎么写、功能怎么实现」的问题。

那篇《JSON 模式/结构化输出报错怎么办?》解决的是「代码跑起来了但报错了」的问题。两篇文章分工如下:

维度 本文(调用教程) 那篇(报错排查)
核心问题 怎么写对 Function Calling 写错了怎么排查
关注点 流程、Schema 设计、多工具 错误码、模型兼容性、截断
典型场景 第一次接入,不会写 接入后遇到具体报错

5. 常见坑

坑 1:Schema 漏写 type: "object"

DeepSeek、部分第三方兼容端点会严格校验,缺少 "type": "object" 直接返回 400:

python 复制代码
# ❌ 错误:漏了 type
"parameters": {
    "properties": {"location": {"type": "string"}},
    "required": ["location"]
}

# ✅ 正确
"parameters": {
    "type": "object",
    "properties": {"location": {"type": "string"}},
    "required": ["location"]
}

坑 2:单属性 Schema 在某些端点报错

OpenAI 社区发现,只有单个属性 的 Schema 在部分模型/端点组合下会报 Invalid schema 错误。解决方法是加一个无意义的辅助字段:

python 复制代码
# ❌ 可能报错
"parameters": {
    "type": "object",
    "properties": {"items": {"type": "array", "items": {"type": "string"}}},
    "required": ["items"]
}

# ✅ 加一个 count 字段
"parameters": {
    "type": "object",
    "properties": {
        "items": {"type": "array", "items": {"type": "string"}},
        "count": {"type": "integer"}
    },
    "required": ["items", "count"]
}

坑 3:并行调用 + 严格模式混用

严格模式下开启并行调用,参数格式不再保证。生产环境如需两者兼得,需要自己在解析后做 JSON Schema 校验。

坑 4:tool_call_id 不匹配

每次请求中 tool_call.id 都是新生成的,传回时必须使用上一次返回的 id,不能自己造一个。

坑 5:messages 历史里塞了 reasoning_content

DeepSeek-v4-pro 等模型会在响应中包含 reasoning_content(内部推理链),多轮对话时只应把 content 加入历史,reasoning_content 不应加入,否则会干扰后续调用。


快速排错表

错误信息 原因 解决方式
Invalid schema for function Schema 缺少 type: object 或使用了不支持的 JSON Schema 特性 检查 Schema,移除 prefixItemsminItems 等不支持的关键字
required is required to be an array including every key strict 模式下所有属性必须出现在 required 数组中 把所有属性加入 required,或把 optional 字段设为 null 联合类型
array schema missing items 数组参数用了 prefixItems 但缺少 items 改用 items 关键字
模型不调用任何函数 system prompt 或 tool description 写得不清楚 优化 description,确保 prompt 中明确要求调用工具
多个 tool_calls 但只有部分执行了 并行调用处理逻辑有遗漏 遍历所有 tool_calls,全部执行并传回结果
二次请求后模型回复很奇怪 messages 历史构建顺序错误 [user, assistant, tool_result, assistant, tool_result...] 顺序排列
finish_reason="length" max_tokens 太小导致输出截断 增大 max_tokens

配置检查清单

接入 Function Calling 前,逐项确认以下配置:

工具定义

  • type 字段设为 "function"(外层)和 "object"(parameters)
  • name 只含 a-zA-Z0-9_-,不超过 64 字符
  • 每个 property 有 description,模型靠它理解语义
  • required 数组列出了所有必填字段
  • 严格模式下所有对象加了 additionalProperties: false
  • 没有使用 prefixItemsminItemsmaxItems 等不支持的特性

API 调用

  • 使用支持 Function Calling 的模型(GPT-4o、Claude 4.x、DeepSeek-v4 等)
  • max_tokens 设置足够大,避免截断
  • 第一次请求正确传递 toolstool_choice 参数
  • 收到 tool_calls 后正确解析 tool_call.idfunction.arguments
  • 第二次请求把所有 tool 消息和原始 assistant 消息都加入 messages
  • tool_call_id 使用上一次返回的真实 id,不自行生成

结果处理

  • 函数执行异常时返回错误 JSON 而非抛异常
  • 多 tool_calls 场景下全部执行完毕再发起第二次请求
  • 不把 reasoning_content 加入多轮对话历史(DeepSeek 等模型)
  • 解析 argumentsjson.loads() 而非直接当 dict 用
相关推荐
诺伦5 小时前
RiseClaw玄策:GEO优化工程实战,从零搭建生成式引擎优化体系
人工智能·chatgpt
落魄大学生之流水线上谋生计5 小时前
Java并发核心机制详解:线程池、CAS、AQS、锁升级
java·开发语言·数据库
星禾元亨5 小时前
企业 AI 落地 5 步路线图:诊断、企业知识库与 GEO 获客的工程化实施步骤
大数据·人工智能·自动化·创业创新
苏打水com5 小时前
内容合规红线:大模型生成内容,哪些能做哪些会犯法?
人工智能·安全·大模型
南京兴帝文化传媒有限公司5 小时前
本地生活服务商户GEO优化技术实践:AI大模型收录机制与地图POI权重算法拆解
大数据·人工智能·算法·生活·geo优化实操·csdn运营技巧·ai内容收录
2601_962293535 小时前
人工智能 & 神经网络完整入门路线(零基础可走,分阶段)
人工智能·python·深度学习·神经网络·机器学习
平原20185 小时前
AI 鞋履设计升级:一张主图与多角度详情图的生成流程
人工智能
人工智能时代 准备好了吗5 小时前
品牌更名后,旧名称与新名称的AI表现如何连续观察?
人工智能
慧一居士5 小时前
QoderWork 和 QoderWake 区别和使用场景对比
人工智能
余槐i5 小时前
使用Ollama本地部署和测试Kimi、GLM等多款大语言模型实战
人工智能·语言模型·自然语言处理·大模型·api