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
pip install -U openai官方 get_horoscope 工具定义(Chat Completions)
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
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 结果
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
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 ↗