OpenAI 函数调用完全指南:让大模型连接外部世界的核心原理

在大模型应用开发中,单纯依靠模型的训练数据早已无法满足实际业务需求 ------ 我们需要让模型能查天气、调接口、执行自定义代码,甚至对接企业内部系统。OpenAI 推出的函数调用(Function Calling,也叫工具调用 Tool Calling) 正是解决这一问题的核心能力,它让大模型突破训练数据的边界,能与外部系统灵活交互,把 AI 的推理能力和实际业务的工具能力结合起来。

这篇文章将基于 OpenAI 官方指南,用通俗易懂的方式讲清函数调用的工作原理、实操步骤、核心配置和最佳实践,从基础的函数定义到高级的自定义工具、语法约束,再到真实落地的应用案例,一站式掌握这一核心开发技能。

一、函数调用的核心概念:搞懂这几个词就入门

在开始实操前,先明确 OpenAI 函数调用体系中的核心术语,避免概念混淆:

  • 工具(Tool):我们赋予模型的可执行功能,既可以是查询天气、获取星座运势的简单函数,也可以是执行代码、调用 API 的复杂能力;

  • 工具调用(Tool Calling):模型根据用户需求,向指定工具发出的执行请求;

  • 工具调用输出(Tool Calling Output):工具执行后返回的结果,需要回传给模型做后续处理;

  • 函数工具(Function Tool):基于 JSON Schema 定义的标准化工具,是最常用的工具类型,模型会按固定格式发起调用;

  • 自定义工具(Custom Tool):无需严格 JSON 格式,支持自由文本输入的工具,可通过语法约束实现定制化输入格式。

简单来说,函数调用的本质,就是让应用程序和 OpenAI 模型通过 API 完成多轮交互,让模型决定何时调用、调用哪个工具,应用程序执行工具并返回结果,最终模型基于结果生成用户能理解的答案

二、函数调用的通用流程:五步走实现模型与工具联动

无论是什么类型的工具,OpenAI 的函数调用都遵循一套标准化的五步流程,这是整个能力的核心框架,所有实操都基于此展开:

  1. 发起带工具的请求:向 OpenAI 模型发送用户请求时,附带模型可调用的工具列表(定义工具的功能、参数、格式);

  2. 接收模型的工具调用:模型分析用户需求后,返回工具调用请求(可能是 0 个、1 个或多个,需考虑多调用场景);

  3. 应用端执行工具:解析模型的调用请求,提取工具名称和参数,在应用程序中执行对应的代码 / 逻辑;

  4. 回传工具执行结果:将工具执行后的输出结果,以指定格式追加到请求中,再次向模型发起请求;

  5. 接收模型最终响应:模型基于工具执行结果,生成最终的自然语言响应;若模型判断结果不足,会返回更多工具调用请求,重复 2-4 步。

这套流程的核心是 "模型做决策,应用做执行",模型负责判断是否需要调用工具、调用哪个,而具体的执行逻辑由开发者控制,既保证了灵活性,也让业务逻辑更安全可控。

三、快速上手:函数工具的端到端实操示例

函数工具是 OpenAI 函数调用中最基础、最常用的类型,基于 JSON Schema 定义,适合绝大多数标准化的工具需求。下面以获取星座每日运势为例,通过完整的 Python 代码,演示函数工具的实际使用(基于 OpenAI 官方 SDK)。

3.1 核心准备:定义函数工具和执行逻辑

首先需要定义模型可调用的工具列表(按 JSON Schema 规范),并实现工具对应的本地执行函数:

python 复制代码
from openai import OpenAI

import json

# 初始化OpenAI客户端

client = OpenAI()

# 1. 定义模型可调用的函数工具列表

tools = [

   {

       "type": "function",  # 工具类型固定为function

       "name": "get_horoscope",  # 函数名称,需与本地执行函数一致

       "description": "Get today's horoscope for an astrological sign.",  # 函数描述,越详细模型判断越准确

       "parameters": {  # 函数参数,基于JSON Schema定义

           "type": "object",

           "properties": {

               "sign": {

                   "type": "string",

                   "description": "An astrological sign like Taurus or Aquarius",  # 参数描述,明确参数含义

               },

           },

           "required": ["sign"],  # 必传参数,模型会强制按此传参

       },

   },

]

# 定义工具对应的本地执行逻辑

def get_horoscope(sign):

   # 模拟星座运势查询,实际场景可替换为API调用、数据库查询等

   return f"{sign}: Next Tuesday you will befriend a baby otter."

3.2 执行函数调用流程:按五步走实现交互

python 复制代码
# 初始化输入列表,存储用户请求和后续的工具调用/输出

input_list = [

   {"role": "user", "content": "What is my horoscope? I am an Aquarius."}

]

# 2. 向模型发起带工具的请求

response = client.responses.create(

   model="gpt-5",  # 支持函数调用的模型,如gpt-5、gpt-4.1、o4-mini等

   tools=tools,

   input=input_list,

)

# 将模型的工具调用结果追加到输入列表

input_list += response.output

# 3. 解析工具调用并执行本地逻辑

for item in response.output:

   if item.type == "function_call" and item.name == "get_horoscope":

       # 提取函数参数并执行

       args = json.loads(item.arguments)

       horoscope_result = get_horoscope(**args)

       # 4. 将工具执行结果按格式追加到输入列表

       input_list.append({

           "type": "function_call_output",

           "call_id": item.call_id,  # 与工具调用的call_id对应,模型用于匹配结果

           "output": json.dumps({"horoscope": horoscope_result})

       })

# 向模型发起二次请求,传入工具执行结果

final_response = client.responses.create(

   model="gpt-5",

   instructions="Respond only with a horoscope generated by a tool.",  # 系统提示,约束模型输出

   tools=tools,

   input=input_list,

)

# 5. 输出模型最终响应

print("Final output:")

print(final_response.model_dump_json(indent=2))

print("n" + final_response.output_text)

3.3 关键说明

  • 函数工具的描述是核心 :模型会根据description判断何时调用该函数,描述越详细、越贴合用户需求场景,模型的调用准确率越高;

  • 必传参数需明确required:模型会按规范传参,避免因参数缺失导致工具执行失败;

  • 工具执行结果需带call_id:模型通过call_id匹配工具调用和执行结果,多工具调用时不可缺少。

四、函数定义的核心规范与最佳实践

函数工具的定义质量直接决定模型的调用准确率,OpenAI 官方给出了一套标准化的定义规范和落地最佳实践,开发者需严格遵循,减少模型调用错误。

4.1 函数定义的标准化属性

每个函数工具都需在tools列表中按固定属性定义,核心属性如下:

属性 类型 描述
type 字符串 固定为function,标识工具类型
name 字符串 函数名称,唯一标识,需与本地执行函数一致,建议使用英文小写 + 下划线
description 字符串 函数的用途描述,明确 "何时 / 为何使用该函数",是模型判断的关键
parameters 对象 基于 JSON Schema 定义的参数,包含参数类型、属性、必传项等
strict 布尔值 是否开启严格模式,开启后模型会严格遵循参数规范,建议始终设为true

其中parameters支持 JSON Schema 的丰富功能,如属性类型、枚举、嵌套对象、可选参数(通过type: ["string", "null"]定义)等,例如查询天气的函数可定义枚举参数units: ["celsius", "fahrenheit"],约束模型传参范围。

4.2 函数定义的落地最佳实践

  1. 描述要清晰具体:明确函数的用途、每个参数的含义和格式、输出结果的类型,避免模糊表述;

  2. 用好系统提示 :通过instructions告诉模型 "何时调用、何时不调用" 该函数,比如 "仅当用户询问天气时,调用 get_weather 函数";

  3. 遵循软件工程原则 :函数功能单一,避免一个函数处理多个逻辑;参数设计遵循 "最小惊讶原则",比如不要设计toggle_light(on: bool, off: bool)这种可能出现无效调用的参数;

  4. 减轻模型负担:模型已知的参数(如用户 ID、订单 ID)无需作为函数参数,直接在本地代码中传递,避免模型重复处理;

  5. 合并连续调用的函数 :如果总是先调用mark_location()再调用query_location(),直接将标记逻辑整合到查询函数中,减少模型调用次数;

  6. 控制函数数量:函数数量越多,模型的判断难度越大,建议按业务场景拆分,单次请求的函数数量不宜过多,可通过测试评估最优数量。

4.3 令牌使用优化

函数定义会被注入到系统消息中,占用模型的上下文令牌,若遇到令牌限制,可通过两种方式优化:

  1. 精简函数的description和参数描述,去掉冗余信息;

  2. 若有大量函数需要定义,可通过微调模型,减少函数定义占用的令牌数量。

五、高级配置:严格模式、并行调用、流式调用

掌握基础的函数定义后,还需要了解 OpenAI 函数调用的高级配置,适配复杂的业务场景,提升调用的稳定性和灵活性。

5.1 严格模式:强制模型遵循参数规范

开启strict: true后,模型会严格按照 JSON Schema 定义的参数发起调用,避免 "尽力而为" 的不规范传参,建议所有场景都开启

严格模式的核心要求
  1. parameters中的每个对象都需设置additionalProperties: false,禁止模型传递未定义的参数;

  2. 所有properties中的字段都需标记为required,可选参数通过type: [参数类型, null]定义;

  3. 若请求的模式不符合要求,API 会直接拒绝请求,并返回缺失的约束信息。

严格模式的示例
json 复制代码
{

   "type": "function",

   "name": "get_weather",

   "description": "Retrieves current weather for the given location.",

   "strict": true,

   "parameters": {

       "type": "object",

       "properties": {

           "location": {

               "type": "string",

               "description": "City and country e.g. Bogotá, Colombia"

           },

           "units": {

               "type": ["string", "null"],  // 可选参数,支持null

               "enum": ["celsius", "fahrenheit"],

               "description": "Units the temperature will be returned in."

           }

       },

       "required": ["location", "units"],

       "additionalProperties": false

   }

}

5.2 工具调用的控制:tool_choice 参数

默认情况下,模型会自动决定是否调用工具、调用多少个(tool_choice: "auto"),开发者可通过tool_choice参数强制控制模型的调用行为,适配不同业务场景:

  • auto(默认):模型自主判断,可调用 0 个、1 个或多个工具;

  • required:强制模型调用至少一个工具,适用于 "必须通过工具获取结果" 的场景,如查询实时天气;

  • {"type": "function", "name": "xxx"}:强制模型调用指定的单个函数,适用于业务逻辑固定的场景;

  • allowed_tools:限制模型只能调用指定的工具子集,无需修改原始tools列表,节省资源;

  • none:禁止模型调用任何工具,等同于不传递tools参数的普通请求。

5.3 并行函数调用:控制多工具调用行为

模型支持在一次回合中调用多个函数(并行调用),若需要禁止并行,可设置parallel_tool_calls: false,确保模型每次只调用 0 个或 1 个工具。

注意:gpt-4.1-nano-2025-04-14 等轻量模型开启并行调用时,可能会重复调用同一个工具,建议这类模型禁用并行调用。

5.4 流式函数调用:实时获取模型调用进度

和 OpenAI 的流式响应一样,函数调用也支持流式传输,通过设置stream: true,可以实时获取模型的工具调用过程,包括函数名称、参数的拼接过程,适合需要展示加载进度的前端场景。

流式调用的核心是解析模型返回的事件流,关键事件类型包括:

  • response.output_item.added:模型开始发起工具调用,返回函数名称、初始参数;

  • response.function_call_arguments.delta:模型逐步拼接函数参数,返回参数的增量内容;

  • response.function_call_arguments.done:函数参数拼接完成,返回完整的调用参数;

  • response.output_item.done:工具调用完成,返回完整的工具调用对象。

开发者只需将增量的参数内容拼接,即可得到完整的工具调用请求,实现实时处理。

六、进阶玩法:自定义工具与语法约束

基础的函数工具适合标准化场景,但实际开发中,我们可能需要模型输出自由文本(如执行 Python 代码、生成指定格式的字符串),此时自定义工具(Custom Tool) 就是最佳选择。

6.1 自定义工具的核心特点

  • 工具类型为custom,无需定义严格的 JSON Schema 参数,模型可返回任意自由文本作为工具输入;

  • 支持通过上下文无关文法(CFG) 约束模型的输入格式,避免模型输出无意义的内容;

  • 工具调用类型为custom_tool_call,解析逻辑与函数工具略有差异,但整体流程仍遵循五步走。

6.2 自定义工具的基础示例:执行 Python 代码

python 复制代码
from openai import OpenAI

client = OpenAI()

# 发起带自定义工具的请求

response = client.responses.create(

   model="gpt-5",

   input="Use the code_exec tool to print hello world to the console.",

   tools=[

       {

           "type": "custom",  # 工具类型为custom

           "name": "code_exec",  # 自定义工具名称

           "description": "Executes arbitrary Python code.",  # 工具描述

       }

   ]

)

# 输出模型的自定义工具调用结果

print(response.output)

模型会返回custom_tool_call类型的结果,其中input字段为纯文本的 Python 代码print("hello world"),开发者只需解析该字段并执行即可。

6.3 语法约束:用 CFG 规范自定义工具的输入格式

为了避免模型输出的自由文本不符合业务要求,OpenAI 支持为自定义工具配置上下文无关文法(CFG) ,通过语法规则约束模型的输入格式,目前支持两种 CFG 语法:Lark (适合复杂的结构化语法)和Regex(适合简单的正则匹配)。

示例 1:Lark CFG 约束数学表达式格式
python 复制代码
from openai import OpenAI

client = OpenAI()

# 定义Lark语法规则:仅允许生成数字+加减乘除的数学表达式

grammar = """

start: expr

expr: term (SP ADD SP term)* -> add

| term

term: factor (SP MUL SP factor)* -> mul

| factor

factor: INT

SP: " "

ADD: "+"

MUL: "*"

%import common.INT

"""

# 带Lark语法约束的自定义工具

response = client.responses.create(

   model="gpt-5",

   input="Use the math_exp tool to add four plus four.",

   tools=[

       {

           "type": "custom",

           "name": "math_exp",

           "description": "Creates valid mathematical expressions",

           "format": {

               "type": "grammar",

               "syntax": "lark",  # 语法类型为lark

               "definition": grammar,  # 语法规则

           },

       }

   ]

)

print(response.output)

模型会严格遵循 Lark 语法,输出4 + 4而非其他格式,确保工具输入的规范性。

示例 2:Regex CFG 约束时间戳格式
python 复制代码
from openai import OpenAI

client = OpenAI()

# 定义正则规则:匹配「月份 日期 年份 at 时间AM/PM」的格式

grammar = r"^(?P<month>January|February|March)s+(?P<day>d{1,2})(?:st|nd|rd|th)?s+(?P<year>d{4})s+ats+(?P?[1-9]|1[0-2])(?Pm>AM|PM)$"

# 带正则语法约束的自定义工具

response = client.responses.create(

   model="gpt-5",

   input="Use the timestamp tool to save a timestamp for August 7th 2025 at 10AM.",

   tools=[

       {

           "type": "custom",

           "name": "timestamp",

           "description": "Saves a timestamp in date + time format.",

           "format": {

               "type": "grammar",

               "syntax": "regex",  # 语法类型为regex

               "definition": grammar,

           },

       }

   ]

)

print(response.output)

模型会输出符合正则规则的时间戳字符串,避免格式混乱。

6.4 CFG 语法的最佳实践

  1. Lark 语法适合复杂结构,Regex 适合简单匹配:若需要约束多层嵌套的结构化内容,用 Lark;若只是简单的字符串格式匹配,用 Regex 更高效;

  2. 语法规则尽量简化:过于复杂的语法会导致 API 拒绝请求,且模型的生成准确率会下降;

  3. 避免无界量词 :正则中尽量用{0,10}等有界量词代替*,防止模型输出过长内容;

  4. 显式处理空白字符:不要依赖开放式的忽略指令,用显式的规则约束空格、换行,提升语法的稳定性。

七、实际应用案例:函数调用的落地场景

理论学习后,结合真实场景的案例能更深刻理解函数调用的价值。下面选取 3 个高频应用场景,每个案例包含完整代码和核心解析,可直接复用或修改适配自身业务。

7.1 场景 1:实时天气查询(对接第三方 API)

场景说明:用户询问某个城市的实时天气,模型调用第三方天气 API 获取数据,再整理成自然语言回复。这里使用「高德地图天气 API」(需提前申请 key)。

完整代码
python 复制代码
from openai import OpenAI

import json

import requests

# 初始化OpenAI客户端和天气API配置

client = OpenAI()

AMAP_WEATHER_API = "https://restapi.amap.com/v3/weather/weatherInfo"

AMAP_API_KEY = "你的高德地图APIKey"  # 替换为自己的key

# 1. 定义天气查询函数工具

tools = [

   {

       "type": "function",

       "name": "get_real_time_weather",

       "description": "查询指定城市的实时天气,返回温度、天气状况、风力等信息",

       "strict": True,

       "parameters": {

           "type": "object",

           "properties": {

               "city": {

                   "type": "string",

                   "description": "城市名称,如北京、上海、广州(仅支持中国城市)"

               },

               "extensions": {

                   "type": ["string", "null"],

                   "enum": ["base", "all"],

                   "description": "返回信息类型:base(基础天气)、all(详细天气),默认base"

               }

           },

           "required": ["city"],

           "additionalProperties": False

       }

   }

]

# 2. 实现天气API调用逻辑

def get_real_time_weather(city, extensions="base"):

   params = {

       "key": AMAP_API_KEY,

       "city": city,

       "extensions": extensions,

       "output": "json"

   }

   response = requests.get(AMAP_WEATHER_API, params=params)

   result = response.json()

   if result["status"] != "1":

       return f"天气查询失败:{result.get('info', '未知错误')}"

   # 解析返回结果

   lives = result["lives"][0]

   return {

       "city": lives["city"],

       "temperature": lives["temperature"],

       "weather": lives["weather"],

       "windDirection": lives["winddirection"],

       "windPower": lives["windpower"],

       "humidity": lives["humidity"],

       "reportTime": lives["reporttime"]

   }

# 3. 执行函数调用流程

input_list = [{"role": "user", "content": "查询深圳今天的天气,要详细信息"}]

# 第一步:发起带工具的请求

response = client.responses.create(

   model="gpt-4.1",

   tools=tools,

   input=input_list,

   instructions="用户询问天气时,必须调用get_real_time_weather函数,extensions参数根据用户需求选择"

)

input_list += response.output

# 第二步:解析工具调用并执行

for item in response.output:

   if item.type == "function_call" and item.name == "get_real_time_weather":

       args = json.loads(item.arguments)

       weather_data = get_real_time_weather(**args)

       # 追加执行结果

       input_list.append({

           "type": "function_call_output",

           "call_id": item.call_id,

           "output": json.dumps(weather_data, ensure_ascii=False)

       })

# 第三步:获取最终响应

final_response = client.responses.create(

   model="gpt-4.1",

   input=input_list,

   tools=tools,

   instructions="将天气数据整理成自然语言,清晰展示温度、天气状况、风力、湿度和更新时间"

)

print("最终回复:")

print(final_response.output_text)
关键解析
  • 对接第三方 API 时,函数工具的description需明确 "支持的城市范围""返回信息类型",避免模型传入无效参数;

  • 异常处理:API 调用失败时返回友好提示,模型会将错误信息告知用户;

  • 系统提示instructions强制模型 "必须调用工具",避免模型直接编造天气数据(大模型无法获取实时信息)。

7.2 场景 2:电商订单查询(对接数据库)

场景说明:用户作为电商平台用户,询问自己的订单状态,模型调用数据库查询工具,根据用户 ID 和订单号获取订单信息,再回复用户。这里用 SQLite 模拟数据库。

完整代码
python 复制代码
from openai import OpenAI

import json

import sqlite3

# 初始化OpenAI客户端和数据库

client = OpenAI()

DB_NAME = "ecommerce.db"

# 1. 初始化数据库(模拟订单数据)

def init_db():

   conn = sqlite3.connect(DB_NAME)

   cursor = conn.cursor()

   # 创建订单表

   cursor.execute('''

       CREATE TABLE IF NOT EXISTS orders (

           order_id TEXT PRIMARY KEY,

           user_id TEXT NOT NULL,

           product_name TEXT NOT NULL,

           create_time TEXT NOT NULL,

           status TEXT NOT NULL,

           total_amount REAL NOT NULL

       )

   ''')

   # 插入测试数据

   test_data = [

       ("ORDER20250101001", "USER123", "iPhone 15 Pro", "2025-01-01 10:30:00", "已发货", 7999.00),

       ("ORDER20250102002", "USER123", "AirPods Pro 2", "2025-01-02 14:20:00", "已签收", 1999.00),

       ("ORDER20250103003", "USER456", "MacBook Air", "2025-01-03 09:15:00", "待付款", 8999.00)

   ]

   cursor.executemany('''

       INSERT OR IGNORE INTO orders (order_id, user_id, product_name, create_time, status, total_amount)

       VALUES (?, ?, ?, ?, ?, ?)

   ''', test_data)

   conn.commit()

   conn.close()

# 2. 定义订单查询函数工具

tools = [

   {

       "type": "function",

       "name": "query_order",

       "description": "查询电商平台用户的订单状态,需提供用户ID和订单号",

       "strict": True,

       "parameters": {

           "type": "object",

           "properties": {

               "user_id": {

                   "type": "string",

                   "description": "用户唯一标识,如USER123、USER456"

               },

               "order_id": {

                   "type": "string",

                   "description": "订单号,如ORDER20250101001"

               }

           },

           "required": ["user_id", "order_id"],

           "additionalProperties": False

       }

   }

]

# 3. 实现数据库查询逻辑

def query_order(user_id, order_id):

   conn = sqlite3.connect(DB_NAME)

   cursor = conn.cursor()

   cursor.execute('''

       SELECT order_id, product_name, create_time, status, total_amount

       FROM orders

       WHERE user_id = ? AND order_id = ?

   ''', (user_id, order_id))

   result = cursor.fetchone()

   conn.close()

   if not result:

       return f"未查询到订单:用户ID={user_id},订单号={order_id}"

   return {

       "order_id": result[0],

       "product_name": result[1],

       "create_time": result[2],

       "status": result[3],

       "total_amount": result[4]

   }

# 4. 执行流程(模拟用户查询)

init_db()  # 初始化数据库

input_list = [{"role": "user", "content": "我是USER123,查询订单号ORDER20250101001的状态"}]

# 发起工具请求

response = client.responses.create(

   model="gpt-4.1",

   tools=tools,

   input=input_list,

   instructions="用户查询订单时,必须提取user_id和order_id参数,调用query_order函数"

)

input_list += response.output

# 执行工具并回传结果

for item in response.output:

   if item.type == "function_call" and item.name == "query_order":

       args = json.loads(item.arguments)

       order_data = query_order(**args)

       input_list.append({

           "type": "function_call_output",

           "call_id": item.call_id,

           "output": json.dumps(order_data, ensure_ascii=False)

       })

# 获取最终回复

final_response = client.responses.create(

   model="gpt-4.1",

   input=input_list,

   tools=tools,

   instructions="用亲切的语气回复用户订单状态,包含商品名称、创建时间、状态和金额"

)

print("最终回复:")

print(final_response.output_text)
关键解析
  • 参数提取:模型自动从用户的自然语言中提取user_idorder_id,无需用户按固定格式输入;

  • 数据安全:数据库查询逻辑在应用端执行,模型无法直接操作数据库,避免数据泄露;

  • 实用性:可扩展为 "订单物流查询""退款申请" 等联动工具,形成电商客服闭环。

7.3 场景 3:PDF 内容提取(自定义工具 + 本地文件处理)

场景说明 :用户需要提取本地 PDF 文件中的指定内容(如 "第三章的核心观点"),模型调用自定义工具,执行 PDF 解析逻辑,返回提取结果。这里使用PyPDF2库处理 PDF。

完整代码
python 复制代码
from openai import OpenAI

import json

from PyPDF2 import PdfReader

# 安装依赖:pip install PyPDF2

client = OpenAI()

# 1. 定义PDF提取自定义工具(带Lark语法约束)

# 语法规则:仅允许输入「文件名+提取范围」格式

pdf_grammar = """

start: pdf_request

pdf_request: filename SP extract_scope

filename: /[A-Za-z0-9_]+.pdf/  # 匹配PDF文件名

extract_scope: "整本书" | "第" INT "章" | "第" INT "页" | "关键词:" /[A-Za-z0-9u4e00-u9fa5]+/

SP: " "

%import common.INT

"""

tools = [

   {

       "type": "custom",

       "name": "pdf_content_extractor",

       "description": "提取本地PDF文件的指定内容,支持整本书、指定章节、指定页码、关键词匹配",

       "format": {

           "type": "grammar",

           "syntax": "lark",

           "definition": pdf_grammar

       }

   }

]

# 2. 实现PDF提取逻辑

def pdf_content_extractor(input_text):

   # 解析输入格式:filename 提取范围

   parts = input_text.split(" ", 1)

   filename = parts[0]

   extract_scope = parts[1]

  

   try:

       # 打开PDF文件

       reader = PdfReader(filename)

       total_pages = len(reader.pages)

       content = []

      

       # 根据提取范围提取内容

       if extract_scope == "整本书":

           for page in reader.pages:

               content.append(page.extract_text())

       elif extract_scope.startswith("第") and "章" in extract_scope:

           # 简化处理:假设每章对应连续页码(实际需根据PDF结构调整)

           chapter_num = int(extract_scope.replace("第", "").replace("章", ""))

           # 模拟章节对应页码:第1章=1-5页,第2章=6-10页...

           start_page = (chapter_num - 1) * 5

           end_page = min(chapter_num * 5 - 1, total_pages - 1)

           for i in range(start_page, end_page + 1):

               content.append(reader.pages[i].extract_text())

       elif extract_scope.startswith("第") and "页" in extract_scope:

           page_num = int(extract_scope.replace("第", "").replace("页", "")) - 1  # 转为0索引

           if 0  page_num

               content.append(reader.pages[page_num].extract_text())

           else:

               return f"页码超出范围,PDF共{total_pages}页"

       elif extract_scope.startswith("关键词:"):

           keyword = extract_scope.replace("关键词:", "")

           for page in reader.pages:

               page_text = page.extract_text()

               if keyword in page_text:

                   content.append(f"第{reader.pages.index(page)+1}页:{page_text[:200]}...")  # 截取前200字

      

       return {

           "filename": filename,

           "extract_scope": extract_scope,

           "total_pages": total_pages,

           "content": "nn".join(content) if content else "未提取到内容"

       }

   except FileNotFoundError:

       return f"文件不存在:{filename}"

   except Exception as e:

       return f"PDF提取失败:{str(e)}"

# 3. 执行流程

input_list = [{"role": "user", "content": "帮我提取test.pdf中第3章的核心观点,用自定义工具处理"}]

# 发起工具请求

response = client.responses.create(

   model="gpt-5",

   tools=tools,

   input=input_list,

   instructions="必须使用pdf_content_extractor工具,输入格式严格遵循语法规则,如test.pdf 第3章"

)

input_list += response.output

# 解析自定义工具调用并执行

for item in response.output:

   if item.type == "custom_tool_call" and item.name == "pdf_content_extractor":

       extract_input = item.input

       extract_result = pdf_content_extractor(extract_input)

       # 追加执行结果

       input_list.append({

           "type": "custom_tool_call_output",

           "call_id": item.call_id,

           "output": json.dumps(extract_result, ensure_ascii=False)

       })

# 获取最终回复

final_response = client.responses.create(

   model="gpt-5",

   input=input_list,

   tools=tools,

   instructions="将PDF提取的内容整理成清晰的结构,突出核心观点,去除冗余信息"

)

print("最终回复:")

print(final_response.output_text)
关键解析
  • 自定义工具 + 语法约束:通过 Lark 语法限制输入格式,避免模型输出无效的文件路径或提取范围;

  • 本地文件处理:工具执行逻辑在本地,无需上传文件到 OpenAI,保护文件隐私;

  • 扩展性:可扩展为 "PDF 转文字""PDF 表格提取" 等子工具,通过allowed_tools控制模型调用范围。

八、常见疑问解答:函数调用与 MCP 的关系

在函数调用学习过程中,很多开发者会混淆「函数调用」与「MCP(Model Control Plane)」,核心原因是二者都涉及 "模型与外部系统交互",但本质是完全不同的概念 ------函数调用是 OpenAI 提供的 "模型能力",MCP 是第三方 / 自建的 "中间件服务",二者是 "能力" 与 "载体" 的关系,而非同一事物。

8.1 核心概念定义

1. 函数调用(Function Calling)
  • 本质:OpenAI 模型(如 gpt-4.1、gpt-5)内置的「决策与交互能力」------ 模型能理解用户需求,判断是否需要调用外部工具(如查天气、查订单),并按 JSON Schema 规范输出调用指令(如函数名、参数)。

  • 核心作用:解决 "模型如何与外部工具对话" 的问题,是模型侧的 "接口协议" 和 "推理逻辑"。

  • 提供方 :OpenAI 官方直接提供,是大模型 API 的核心功能之一(需在请求中传入tools参数启用)。

2. MCP(Model Control Plane)与 MCP Server
  • 本质 :MCP 是一套工程化的中间件框架,MCP Server 是该框架的具体实现(服务器端),核心定位是「大模型与外部工具的 "调度中枢"」,相当于 "工具调用的中转站"。

  • 核心作用:解决 "多工具整合""跨平台调用""权限控制""日志监控" 等工程化问题,是应用侧的 "工具管理系统"。

  • 提供方不是 OpenAI 官方提供!常见于企业级应用或第三方开发框架(如 LangChain、LlamaIndex 的工具调度模块,或自研的中间件)。

8.2 核心区别(一张表看懂)

对比维度 函数调用(Function Calling) MCP Server
本质定位 模型的 "交互能力 / 协议规范" 应用的 "工具调度中间件"
提供方 OpenAI 官方 第三方框架 / 企业自研
核心功能 1. 模型判断是否调用工具;2. 按规范输出调用指令(函数名 + 参数) 1. 接收模型的调用指令;2. 路由到对应的工具 / API;3. 执行工具并返回结果;4. 管理工具权限、日志、重试
作用范围 仅在 OpenAI 模型侧生效(模型输出调用指令) 在应用侧生效(承接调用、执行工具)
依赖关系 不依赖 MCP Server,可直接对接工具 依赖模型的函数调用能力(需接收模型输出的规范调用指令)
典型场景 简单工具调用(如单 API、本地函数) 复杂工具集群(如同时对接 10 + 工具、跨团队工具共享、权限管控)

8.3 二者的联系:如何配合工作?

实际开发中,二者常一起使用,形成 "模型→MCP→工具" 的完整链路,以电商客服场景为例:

  1. 用户需求:"查询我的订单 ORDER20250101001 的物流状态";

  2. 函数调用(模型侧):OpenAI 模型通过函数调用能力,分析需求后输出规范的调用指令(如{"name":"query_logistics","parameters":{"order_id":"ORDER20250101001"}});

  3. MCP Server(应用侧):接收模型的调用指令,做三件事:

  • 权限校验:确认当前用户有权查询该订单;

  • 工具路由:将请求转发到 "物流查询 API"(而非订单查询 API);

  • 执行监控:记录调用日志,若 API 超时则自动重试;

  1. 工具执行:物流 API 返回结果,经 MCP Server 整理后回传给模型;

  2. 模型生成回复:OpenAI 模型基于结果,生成自然语言回复用户。

简单说:函数调用负责 "让模型会说话(输出规范指令)",MCP Server 负责 "让工具能听懂(执行规范指令)"

8.4 实操建议:什么时候需要 MCP Server?

  • 若你只需要调用 1-2 个简单工具(如本地函数、单 API):无需 MCP Server,直接用 OpenAI 的函数调用对接工具即可(如文章中天气查询、PDF 提取的案例);

  • 若你需要:① 管理多个工具(如 10+API、跨团队工具);② 控制权限(如部分用户只能调用查询工具,不能调用修改工具);③ 监控日志、重试失败、负载均衡:需要 MCP Server,统一调度工具调用。

九、总结:函数调用是大模型应用开发的核心基石

OpenAI 的函数调用能力,让大模型从 "纯文本生成器" 变成了 "智能决策器",它的核心价值在于将大模型的推理能力与开发者的工程能力结合:模型负责理解用户需求、做出调用决策,开发者负责实现具体的业务逻辑、控制工具执行,最终让大模型能对接真实的业务系统,解决实际问题。

掌握函数调用的关键,在于理解五步通用流程,做好函数定义的描述和规范,处理好多工具调用和异常场景,而高级的自定义工具和 CFG 语法,则能让你适配更复杂的业务需求。无论是做智能客服、数据分析、API 对接,还是开发企业级的大模型应用,函数调用都是必备的核心技能,也是打通大模型和外部世界的关键桥梁。

未来,大模型的竞争不再是模型本身的能力,而是模型与外部工具的融合能力,而 OpenAI 的函数调用,正是这套融合能力的最佳实践之一。

参考链接

https://platform.openai.com/docs/guides/function-calling

相关推荐
组合缺一3 小时前
Solon AI Remote Skills:开启分布式技能的“感知”时代
java·人工智能·分布式·agent·langgraph·mcp
猿小羽3 小时前
Spring AI + MCP 实战:构建标准化、可扩展的 AI Agent 架构体系
java·spring boot·llm·架构设计·ai agent·spring ai·mcp
玄同7654 小时前
让 Trae IDE 智能体 “读懂”文档 Excel+PDF+DOCX :mcp-documents-reader 工具使用指南
人工智能·git·语言模型·gitee·github·ai编程·mcp
猿小羽4 小时前
Spring AI + MCP 实战:构建标准化 AI 智能代理与上下文集成
java·spring boot·llm·ai agent·spring ai·anthropic·mcp
94甘蓝20 小时前
第 5 篇 Spring AI - Tool Calling 全面解析:从基础到高级应用
java·人工智能·函数调用·工具调用·spring ai·tool calling
10km1 天前
MCP:Trace IDE 正确配置 PDF 读取 MCP 的完整指南
ide·pdf·mcp·trae
带刺的坐椅1 天前
Solon AI Remote Skills:开启分布式技能的“感知”时代
java·llm·solon·mcp·skills
X.Cristiano1 天前
LLM调用工具协议:Plugin、Function Call与MCP的深度解析
llm·tools·mcp
带刺的坐椅1 天前
Claude Code Agent Skills vs. Solon AI Skills:从工具增强到框架规范的深度对齐
java·ai·agent·claude·solon·mcp·skills