引言

随着大模型能力的不断增强,仅依靠自然语言对话已难以满足真实业务场景的需求。模型不仅需要“会说话”,还需要能够访问外部数据、调用业务能力,并参与实际流程执行。为此,OpenAI 提供了 Function Calling(工具调用)机制,使模型能够在对话过程中主动选择并调用开发者定义的函数,从而实现与外部系统的深度集成。

Function Calling 本质上为大模型打开了一扇“连接现实世界”的大门:模型可以根据用户意图,结构化地生成函数调用参数,由应用程序执行真实逻辑后再将结果反馈给模型,最终生成面向用户的自然语言回复。这种模式极大地降低了 AI 与业务系统集成的复杂度,也成为构建 AI Agent、智能助手和自动化流程的核心能力之一。

本文将基于 OpenAI 官方文档与示例,系统性地介绍 Function Calling 的基本概念、工作流程以及完整的调用链路,并通过一个天气查询的实战示例,演示从工具定义、模型触发函数调用,到函数执行结果回传并生成最终回答的完整过程。希望通过这篇文章,帮助你快速理解并在实际项目中正确、稳定地使用 Function Calling 能力。

Function calling

Function calling(tool caling)为模型提供访问新功能和数据的权限,使其能够遵循指令并响应提示。

Function calling为OpenAI模型提供了一种强大且灵活的方式,可以与外部系统对接并访问器训练数据以外的数据(调用工具)。本节来展示它的工作原理。

工具-我们赋予模型的功能

工具(Tool)就是我们告诉模型它可以访问的额外功能,当模型根据提示词生成响应时,它可能会决定需要调用工具提供的数据或功能以遵循提示的指示。

当我想模型发送带有提示的API请求时,可以包含一个工具列表,让模型在处理时考虑使用这些工具,就和上篇文章一样。我们以获取某地天气情况的工具get_weather为例,它可能会带有location参数。

工具调用-模型请求使用工具的指令

工具调用(function call)是指模型在分析提示词后,如果认为为了遵循提示中的指令,需要调研我们为其提供的某个工具,则会返回的一种特殊响应。

如果模型接收到一个类似"what is the weather in Paris?" 的API请求,它可能会使用get_weather工具进行工具调用,其中Paris作为location的参数。

工作调用输出-我们为模型生成的输出

工作调用输出指的是工具使用模型的工具调用输入生成的响应,工具调用输出可以是结构化的JSON或纯文本,并包含对特定模型工具调用的引用(call_id)。为了完成我们的天气示例:

  • 模型可以访问get_weather工具,参数为location
  • 在接收到类似"what is the weather in Paris?" 这样的提示后,模型会返回一个包含location参数且值为Paris的工具调用。
  • 工具调用的输出可能返回一个JSON对象、图像内容或文件内容。

然后我们将所有工具定义、原始提示、模型的工具调用以及工具调用输出再次发送给模型,最终获得类似:

The weather in Paris today is 25C.

函数与工具

  • 函数是一种特定类似的工具,由JSON模式(schema)定义。函数定义允许模型将数据传递给你的应用程序,你的代码可以访问模型建议的数据或采取相应的操作。
  • 除了函数工具外, 还有自定义工具,它们可以与自由文本输入和输出配合使用。
  • 此外,还有OpenAI平台内建的工具。

Function calling流程

Function calling是通过OpenAI API在应用程序和模型之间进行的多轮对话,其流程包含五个高层次步骤:

  1. 向模型发起它可调用的工具的请求
  2. 从模型接收工具调用
  3. 在应用程序端使用工具调用的输入执行代码
  4. 使用工具调用的输出向模型发起第二次请求
  5. 接收来自模型的最终响应(或更多工具调用)

Function Calling Diagram Steps

(工具调用流程,图片来自于参考1)

函数工具示例

我们还是以获取天气为例,看一个工具调用流程的例子。

from openai import OpenAI
import json
import os

os.environ["OPENAI_API_KEY"] = "sk-xx"
os.environ["OPENAI_BASE_URL"] = "xx"

model_id = "gpt-4o-mini" 

client = OpenAI()

# 定义一个辅助函数
def chat_completion_request(messages, tools=None, tool_choice="auto"):
    try:
        response = client.chat.completions.create(
            model=model_id,
            messages=messages,
            tools=tools,
            tool_choice=tool_choice, 
        )
        return response
    except Exception as e:
        print(f"Exception: {e}")
        return e
      
# 1. 为模型定义一个可调用工具列表
# OpenAI支持的工具定义Schema
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市,例如:深圳、北京",
                    }
                },
                "required": ["location"]
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_n_day_weather_forecast",
            "description": "获取N天的天气预报",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市,例如:深圳、北京",
                    },
                    "num_days": {
                        "type": "integer",
                        "description": "要预报的天数",
                    }
                },
                "required": ["location", "num_days"]
            },
        }
    },
]


定义两个模拟函数(也可以接入真实的查询天气API):

import random
from datetime import datetime, timedelta

def get_current_weather(location: str) -> dict:
    """
    模拟获取当前天气
    """
    # 随机生成温度
    temp = round(random.uniform(10, 30), 1)
    
    # 随机生成天气描述
    conditions = ["晴天", "多云", "小雨", "大雨", "雷阵雨", "阴天"]
    condition = random.choice(conditions)
    
    return {
        "location": location,
        "temperature": temp,
        "condition": condition,
        "unit": "摄氏度",
        "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }

def get_n_day_weather_forecast(location: str, num_days: int) -> dict:
    """
    模拟获取N天的天气预报
    """
    forecast = []
    conditions = ["晴天", "多云", "小雨", "大雨", "雷阵雨", "阴天"]
    
    for i in range(num_days):
        temp = round(random.uniform(10, 30), 1)

        
        day_forecast = {
            "date": (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d"),
            "temperature": temp,
            "unit": "摄氏度",
            "condition": random.choice(conditions)
        }
        forecast.append(day_forecast)
    
    return {
        "location": location,
        "forecast": forecast
    }

# 测试
print(get_current_weather("北京"))
print(get_n_day_weather_forecast("北京", 5))

{'location': '北京', 'temperature': 11.2, 'condition': '大雨', 'unit': '摄氏度', 'time': '2025-12-13 09:50:29'}
{'location': '北京', 'forecast': [{'date': '2025-12-13', 'temperature': 15.6, 'unit': '摄氏度', 'condition': '雷阵雨'}, {'date': '2025-12-14', 'temperature': 25.6, 'unit': '摄氏度', 'condition': '晴天'}, {'date': '2025-12-15', 'temperature': 28.3, 'unit': '摄氏度', 'condition': '阴天'}, {'date': '2025-12-16', 'temperature': 24.7, 'unit': '摄氏度', 'condition': '晴天'}, {'date': '2025-12-17', 'temperature': 15.5, 'unit': '摄氏度', 'condition': '雷阵雨'}]}

注册工具:

TOOLS = {
    "get_current_weather": get_current_weather,
    "get_n_day_weather_forecast": get_n_day_weather_forecast
}
messages = []
messages.append({"role": "system", "content": "当用户的问题需要使用工具时,请调用对应的工具,而不是直接回答"})
messages.append({"role": "user", "content": "未来5天深圳的天气会怎么样?"})

# 将定义好的工具传递给模型
chat_response = chat_completion_request(
    messages, tools=tools
)

result = chat_response.choices[0].message
print(result)
ChatCompletionMessage(content=None, refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=[ChatCompletionMessageFunctionToolCall(id='call_h68QzRexg3U1r4rSuDYRclUu', function=Function(arguments='{"location":"深圳","num_days":5}', name='get_n_day_weather_forecast'), type='function')])

这里构造一个系统消息,然后用户咨询未来五天的天气情况,把消息类别和定义好的工具传递给模型,这里tool_choice取的默认值auto,它的取值含义:

  • auto: 默认值,可以由模型自行决定调用零个、一个或多个函数。
  • none:禁止调用函数。
  • 调用指定函数:{"type": "function", "function": {"name": "my_function"}}
  • required:必须调用一个或多个函数。

接下来是很重要的一步:

# 3. 增加工具调用消息(注意格式)
messages.append({
    "role": "assistant",
    "content": None,
    "tool_calls":  [
        {
            "c": call.id,
            "type": "function",
            "function": {
                "name": call.function.name, 
                "arguments": call.function.arguments     
            }
            
        } for call in result.tool_calls]
})

我们必须增加工具调用消息到列表中,这里传入的id后面工具调用结果消息会用到,必须保持一致:

for item in result.tool_calls:
    if item.type == "function":
        func = TOOLS[item.function.name]
        # 4. 执行函数
        func_result = func(**json.loads(item.function.arguments))
        # 5. 告诉模型函数调用结果
        messages.append({
            "role": "tool",
            "tool_call_id": item.id,
            "content": str(func_result)
        })

print(messages)
messages = [
    {
        "role": "system",
        "content": "当用户的问题需要使用工具时,请调用对应的工具,而不是直接回答"
    },
    {
        "role": "user",
        "content": "未来5天深圳的天气会怎么样?"
    },
    {
        "role": "assistant",
        "content": None,
        "tool_calls": [
            {
                "id": "call_h68QzRexg3U1r4rSuDYRclUu",
                "type": "function",
                "function": {
                    "name": "get_n_day_weather_forecast",
                    "arguments": "{'location':'深圳','num_days':5}"
                }
            }
        ]
    },
    {
        "role": "tool",
        "tool_call_id": "call_h68QzRexg3U1r4rSuDYRclUu",
        "content": "{'location': '深圳', 'forecast': [{'date': '2025-12-13', 'temperature': 14.2, 'unit': '摄氏度', 'condition': '多云'}, {'date': '2025-12-14', 'temperature': 19.2, 'unit': '摄氏度', 'condition': '大雨'}, {'date': '2025-12-15', 'temperature': 19.5, 'unit': '摄氏度', 'condition': '晴天'}, {'date': '2025-12-16', 'temperature': 22.5, 'unit': '摄氏度', 'condition': '雷阵雨'}, {'date': '2025-12-17', 'temperature': 27.3, 'unit': '摄氏度', 'condition': '雷阵雨'}]}"
    }
]

整个消息列表如上所示,注意这里的assistant消息包含了工具调用,我们就应该把工具调用结果通过tool消息附加进去,并且tool_call_id需保持一致。

最后再次传递给大模型,让模型给出最终回答:

chat_completion_request(messages, tools)
ChatCompletion(id='chatcmpl-Cm9B8aWEcZYi5OBoXnTL7temNyD53', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='未来5天深圳的天气预报如下:\n\n- **12月13日**:多云,温度14.2°C\n- **12月14日**:大雨,温度19.2°C\n- **12月15日**:晴天,温度19.5°C\n- **12月16日**:雷阵雨,温度22.5°C\n- **12月17日**:雷阵雨,温度27.3°C\n\n请注意天气变化,出行时合理安排!', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None))], created=1765591062, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_efad92c60b', usage=CompletionUsage(completion_tokens=115, prompt_tokens=341, total_tokens=456, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))

(渲染后的结果如下:)


未来5天深圳的天气预报如下:

  • 12月13日:多云,温度14.2°C
  • 12月14日:大雨,温度19.2°C
  • 12月15日:晴天,温度19.5°C
  • 12月16日:雷阵雨,温度22.5°C
  • 12月17日:雷阵雨,温度27.3°C
    请注意天气变化,出行时合理安排!

参考

  1. Function calling (内容可能会变化)
  2. How to call functions with chat models
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐