TodayAI

GuidesAgents

用 OpenAI Function Calling 跑通工具闭环

在 Chat Completions 里声明 tools,处理 tool_calls,再用 tool 角色回传结果,跑完一轮完整工具调用。

基于 OpenAI Docs 整理 · 官方资料 ↗

模型自己不知道你的订单状态、库存或内部 API。Function calling(也叫 tool calling)做的事很具体:你把可调用的能力声明给模型,它在需要时返回结构化的工具调用,你在应用侧真正执行,再把结果送回去,让它继续生成最终回答。

OpenAI 官方文档现在同时覆盖 Responses API 与 Chat Completions。这篇按 Chat Completions 路径走完整闭环:声明 tools、读取 message.tool_calls、用 role: "tool" 回传,再发起第二次 completions 请求。

示例沿用官方 get_horoscope 演示。它故意很短,方便你先看清协议,再换成真实业务函数。

工具闭环到底在交换什么

整条链路可以压成五步:带着 tools 发请求 → 模型返回 tool_calls → 你在本地执行对应函数 → 用 tool 角色把结果写回 messages → 再请求一次模型拿到最终文本。

关键术语要分清:tool / function 是你声明给模型的能力;tool call 是模型这一轮决定调用哪一个、参数是什么;tool call output 是你执行后回传的内容。API 不会替你执行业务代码,它只负责提出调用请求和消化结果。

Chat Completions 里,助手消息可能带 tool_calls;回传时每条结果消息都是 role: "tool",并用 tool_call_id 对齐那一次调用。漏掉 id,或没把带 tool_calls 的助手消息一并 append 回去,下一轮很容易失败。

先声明模型可以调用的工具

Chat Completions 的 tools 项外层 type 是 function,细节放在 function 对象里:name、description、parameters,以及推荐打开的 strict。parameters 用 JSON Schema 描述入参。

安装 OpenAI Python SDK

bash
pip install -U openai

官方 get_horoscope 工具定义(Chat Completions)

python
from openai import OpenAI
import json

client = OpenAI()

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_horoscope",
            "description": "Get today's horoscope for an astrological sign.",
            "parameters": {
                "type": "object",
                "properties": {
                    "sign": {
                        "type": "string",
                        "description": "An astrological sign like Taurus or Aquarius",
                    },
                },
                "required": ["sign"],
                "additionalProperties": False,
            },
            "strict": True,
        },
    },
]


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

description 写清楚“何时用、参数含义、返回代表什么”。strict: True 时,object 需要 additionalProperties: False,且 properties 里的字段都要进 required;可选字段用 ["string", "null"] 这类联合类型表达。

第一次请求:让模型决定要不要调用

把用户问题和 tools 一起交给 chat.completions.create。模型若判断需要外部数据,会在 choices[0].message.tool_calls 里给出调用,而不是直接编一段星座运势。

发起带 tools 的 Chat Completions

python
messages = [{"role": "user", "content": "What is my horoscope? I am an Aquarius."}]

response = client.chat.completions.create(
    model="gpt-5.6",
    messages=messages,
    tools=tools,
)

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

官方示例模型是 gpt-5.6。若账号暂不可用,换成当前支持 function calling 的模型即可,协议不变。先确认环境变量 OPENAI_API_KEY 已设置。

执行 tool_calls,并用 tool 角色回传

先把助手那条带 tool_calls 的 message append 进 messages。再遍历 tool_calls:解析 function.arguments,执行本地函数,然后追加 role: "tool" 的结果,tool_call_id 必须等于这次调用的 id。

官方闭环:执行并回传 tool 结果

python
messages.append(response.choices[0].message)

for tool_call in response.choices[0].message.tool_calls or []:
    if tool_call.function.name == "get_horoscope":
        args = json.loads(tool_call.function.arguments)
        horoscope = get_horoscope(args["sign"])

        messages.append(
            {
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps({"horoscope": horoscope}),
            }
        )

一轮响应里可能有多个 tool_calls。按官方建议默认按“可能有若干次调用”来写循环,不要假设永远只有一个。

第二次请求:拿到最终回答

把更新后的 messages 再交给 chat.completions.create。模型此时已经看到工具输出,应返回面向用户的自然语言。

第二次 completions,打印最终 content

python
response = client.chat.completions.create(
    model="gpt-5.6",
    messages=messages,
    tools=tools,
)

print(response.choices[0].message.content)
  • 第一次响应出现 get_horoscope 的 tool_calls
  • arguments 里能解析出 sign(例如 Aquarius)
  • 第二次响应的 content 基于工具返回,而不是空转或编造无关内容

什么时候要约束调用行为

默认由模型决定是否调用。需要强制时,用 tool_choice:指定某个函数、设为 "none" 禁止调用,或配合 parallel_tool_calls=False 限制并行。

工具很多时,先保证名称与描述清晰,并控制首轮暴露的函数数量。官方也提到可用 tool search 延后加载不常用工具;那是进阶能力,最小闭环不必先上。

如果你改用 Responses API,声明形状和回传字段会不同(例如 function_call / function_call_output)。协议换了,但“模型提议 → 你执行 → 结果回灌”的分工不变。

最容易踩的坑

第二次请求报错,或模型完全无视工具结果

确认已 append 带 tool_calls 的助手消息;每条 tool 结果都带正确 tool_call_id;content 是字符串(可用 json.dumps)。

arguments 解析失败

先打印 tool_call.function.arguments。开启 strict: True,并保证 schema 满足 additionalProperties: false 与 required 完整。

模型从不调用工具,直接瞎答

把用户问题写得更依赖外部数据;检查 description;必要时用 tool_choice 强制指定函数。

模型名不可用

官方示例使用 gpt-5.6。换成账号可用且支持 function calling 的模型后重试。

官方资料

OpenAI Docs

Function calling