开始之前
你需要第 1 课的准备:环境变量 OPENAI_API_KEY 里的 API 密钥,装在 Python 3.10 或更高版本上的 openai 包,以及一个支持工具的模型。示例用的是 gpt-6-astra,也就是 2026 年 11 月 5 日文档里用的模型。GPT-6 Astra 和 GPT-6.1 Sol 只能通过 Responses API 调用工具,所以这一课不涉及 Chat Completions。
这一课靠三个词撑起来。函数是你用 JSON Schema 定义的工具,JSON Schema 是一份写明每个参数及其类型的 JSON 文档。函数调用是模型带着自己填好的参数来请求这个函数。函数调用输出是你的代码发回去的结果。模型只负责请求,运不运行由你的应用决定。Responses API 还接受自定义工具和远程 MCP 服务器,这一课只讲函数和两个内置的搜索工具。
如果你学过 Claude API 课程里的工具调用一课,这个循环会很眼熟,只是结构不同。Claude 返回 tool_use 内容块,要求你在一条 user 消息里放 tool_result 块。OpenAI 则在 output 列表里返回 function_call 项,要求你在下一次的 input 里放 function_call_output 项,两者靠 call_id 配对。
从头到尾跑通工具调用循环
带上一个函数,发送“What is my horoscope? I am an Aquarius.”,文档里的流程展示了接下来会发生什么。第一次响应里没有星座运势,只有一个 function_call 项,它写着 get_horoscope 这个名字,参数是一段 JSON 字符串,这里就是星座。你的代码解析这段字符串,运行函数,把结果追加进去。用户读到的文字,要等第二次请求才会生成。整个模式就是这样一分为二。
下面的示例是函数调用指南里的完整示例,原样照搬。JavaScript 版本做的是同一件事,它从 openai/lib/responses/ResponseInputItems 导入 toResponseInputItems,再把结果推进输入数组。Python 版本里有两行做的事很容易被忽略。input_list += response.output 把模型自己的输出项,也就是调用本身和所有推理项,放回历史记录里。指南要求的正是这一点,因为和工具调用一起返回的推理项,必须随着输出一起传回去。
第二次请求加了 instructions,告诉模型只能根据工具的结果来回答。第 1 课讲过,instructions 只对一次请求生效,所以它放在真正需要它的那次调用上。这个循环没有固定的长度。在 Responses 里,这个循环可以按任务需要重复任意多次,每一轮都走同样的五步。
from openai import OpenAI
import json
client = OpenAI()
# 1. Define a list of callable tools for the model
tools = [
{
"type": "function",
"name": "get_horoscope",
"strict": True,
"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,
},
},
]
def get_horoscope(sign):
return f"{sign}: Next Tuesday you will befriend a baby otter."
# Create a running input list we will add to over time
input_list = [{"role": "user", "content": "What is my horoscope? I am an Aquarius."}]
# 2. Prompt the model with tools defined
response = client.responses.create(
model="gpt-6-astra",
tools=tools,
input=input_list,
)
# Save function call outputs for subsequent requests
input_list += response.output
for item in response.output:
if item.type == "function_call":
if item.name == "get_horoscope":
# 3. Execute the function logic for get_horoscope
sign = json.loads(item.arguments)["sign"]
horoscope = get_horoscope(sign)
# 4. Provide function call results to the model
input_list.append(
{
"type": "function_call_output",
"call_id": item.call_id,
"output": horoscope,
}
)
response = client.responses.create(
model="gpt-6-astra",
instructions="Respond only with a horoscope generated by a tool.",
tools=tools,
input=input_list,
)
# 5. The model should be able to give a response!
print(response.output_text)- 带上
tools列表和用户消息发送请求。 - 在
response.output里找出每个function_call项,读取它的name和arguments。 - 用
json.loads解析参数,运行你自己的函数。 - 追加一个带相同
call_id的function_call_output,结果以字符串形式放进去。 - 把变长的输入列表再发一次。回复要么是文字,要么是更多要运行的调用。
定义模型用得好的函数
一个函数定义有五个字段,见下表。parameters 接受标准的 JSON Schema,所以枚举、嵌套对象和每个属性上的描述都能用,递归对象也可以。示例是指南里的 get_weather 定义,它的 units 枚举能防止模型编出一个你的代码读不懂的单位。
指南关于写定义的建议,归结起来是一个它称为“实习生测试”的检验:一个人只拿到你给模型的这些信息,能不能正确使用这个函数?如果不能,他会问的那些问题,就该写进描述里。写明每个参数的含义和格式,再在 instructions 里说清楚什么时候该用、什么时候不该用这个函数。加入示例和边界情况,有助于纠正反复出现的错误,不过指南也提醒,示例可能会让推理模型表现变差。
函数本身也要用心设计。像 toggle_light(on: bool, off: bool) 这样的签名,允许出现说不通的调用,应该改用枚举。别让模型去填你的代码已经知道的值。如果你已经有 order_id,就把 submit_refund() 定义成没有参数,在代码里传入这个 id。总是先后连着运行的两个函数,可以合并成一个。
数量也有讲究。指南建议,每个回合开始时可用的函数少于 20 个,这只是一个宽松的目标。函数定义会被注入系统消息,所以它们占用上下文窗口,并且每次请求都按输入 token 计费。函数多了,就把它们按命名空间分组,比如 crm 或 billing,再给不常用的标上 defer_loading。这样 tool_search 只在模型需要时才加载它们,这要求 gpt-5.4 或更新的模型。
严格模式是最后一个字段,也是最要紧的一个。把 strict 设为 true,模型给出的参数就会完全符合你的 schema,它和结构化输出用的是同一套机制。代价是 schema 要守两条规则:每个对象都把 additionalProperties 设为 false,properties 里的每个字段都要列进 required。可选字段要写成包含 null 的类型列表,比如 ["string", "null"]。
如果你发送 strict: true,而 schema 违反了规则,请求会被拒绝,并列出缺少的约束。如果你不写 strict,Responses 会尝试把 schema 转成严格模式,转不了就退回尽力而为的模式,响应里的工具随后会显示 strict: false。指南建议始终打开它。
{
"type": "function",
"name": "get_weather",
"description": "Retrieves current weather for the given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country e.g. Bogotá, Colombia"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Units the temperature will be returned in."
}
},
"required": ["location", "units"],
"additionalProperties": false
},
"strict": true
}| 字段 | 里面放什么 |
|---|---|
type | 始终是 function |
name | 模型调用时用的函数名,比如 get_weather |
description | 什么时候、怎样使用这个函数 |
parameters | 描述参数的 JSON Schema |
strict | 参数是否必须完全符合 schema |
处理多个调用,选择 tool_choice,流式读取参数
一次响应里可能有零个、一个或好几个函数调用,所以指南要求你默认按多个来处理。它的示例响应里有三项:查巴黎天气的 get_weather,查波哥大天气的 get_weather,以及发给 Bob 的 send_email,每一项都有一个以 fc_ 开头的 id、一个以 call_ 开头的 call_id、一个 name,以及 JSON 字符串形式的 arguments。一个小小的路由函数,也就是下面指南里的 call_function,把每个调用分派给对应的代码,遇到不认识的名字就抛出错误。
返回的内容通常是字符串,格式随你定,JSON、错误码或纯文本都行。像 send_email 这样没有返回值的函数,也该返回类似 "success" 的内容,让模型知道它已经运行过。生成图片或文件的函数,可以改为返回一组图片或文件对象。所有输出都追加好之后,下一次请求就把模型需要的东西都给齐了。指南示例里的最终回答是“It's about 15°C in Paris, 18°C in Bogotá, and I've sent that email to Bob.”
默认情况下,模型自己决定什么时候调用工具、调用几个。把 parallel_tool_calls 设为 false,它每个回合就只调用零个或一个工具。从 GPT-5 起的模型,在同时提供内置工具时也能并行调用函数,不过内置工具从不加入并行的函数调用批次。tool_choice 能让你控制得更牢,下表列出了它的各种设置。
allowed_tools 的用处不止于控制。你可以让每次请求的完整 tools 列表保持一模一样,只在每个回合收窄可调用的范围。工具列表不变,提示词缓存就能持续生效,提示词缓存会复用之前请求里已经处理过的提示词前缀。
函数调用也能流式输出。打开 stream 后,response.output_item.added 事件宣布每个 function_call 项的出现,response.function_call_arguments.delta 事件一段一段地送来参数文本,response.function_call_arguments.done 带来完整的调用。界面可以在模型还在写参数时,就显示正在运行哪个函数。这些增量是原始的 JSON 文本,要等 done 事件到了再解析,因为写到一半的参数字符串还不是合法的 JSON。
def call_function(name, args):
if name == "get_weather":
return get_weather(**args)
if name == "send_email":
return send_email(**args)
raise ValueError(f"Unknown function: {name}")functions让位给toolsChat Completions 弃用
functions参数。tool_choice新增required模型必须至少调用一个函数。
并行调用可以关闭
在 Chat Completions 和 Assistants API 里。
Responses API 发布
一个为智能体和工具打造的新 API。
strict 模式支持并行调用
并行调用时也能用严格的工具 schema。
自定义工具
工具调用可以用自由文本输入和输出,而不是 JSON。
工具搜索
模型可以把大量工具推迟到需要时再加载。
GPT-6 Astra
它的工具调用必须走 Responses API,Responses 同时加入了异步工具调用。
来源:OpenAI Docs: Changelog (2026年11月5日 查阅)
tool_choice | 模型可以做什么 |
|---|---|
"auto"(默认) | 调用零个、一个或多个函数 |
"required" | 调用一个或多个函数 |
{"type": "function", "name": "get_weather"} | 只调用指定的那一个函数 |
{"type": "allowed_tools", "mode": "auto", "tools": [...]} | 只在列出的子集里调用 |
"none" | 表现得像没有传入任何函数 |
加上网页搜索和文件搜索
内置工具在 OpenAI 那边运行。你像添加函数一样把它们加进 tools,用不用由模型决定,但你的代码不用执行任何东西,也不用发回任何输出。下面的示例是网页搜索指南里的第一个示例,只要一项 {"type": "web_search"} 就设置好了。旧的 web_search_preview 工具仍可用于老代码,但它不支持 filters 等较新的控制项。
这时响应里会有一个 web_search_call 项,它的 action 说明模型做了什么:search、open_page 或 find_in_page,后两种只在推理模型上出现。接着是 message,带着 url_citation 注释,给出每个被引用来源的 URL、标题和它在文字里的位置。指南规定,你的界面展示网页结果时,这些引用必须显示成醒目、可点击的链接。
网页搜索有几个设置。filters 可以把结果限定在最多 100 个 allowed_domains 里,或者排除最多 100 个 blocked_domains,域名不带 https 前缀。sources 字段列出模型查阅过的所有 URL,通常比它引用的多。search_context_size 设为 low、medium 或 high,控制模型读取多少搜索结果。external_web_access 设为 false,就只用缓存的结果。不管模型本身的窗口多大,搜索的上下文窗口上限都是 128,000 个 token。需要综合多个来源的问题,指南建议用推理模型,它会边读边调整搜索,耗时几分钟的任务则配合后台模式运行。
文件搜索的答案来自你的文档,而不是网页。你先上传文件,把它们加进一个向量存储,也就是 OpenAI 为了对这些文件做语义和关键词搜索而建立的索引,再等每个文件的状态变成 completed。然后在 tools 里加一个 file_search 项,把这个存储的 id 放进它的 vector_store_ids 列表。响应里会有一个 file_search_call 项,以及一条带 file_citation 注释、写明每个文件名的消息。
文件搜索有两个设置。max_num_results 限制返回多少条结果,能省 token、降延迟,但可能牺牲回答质量。原始搜索结果默认不放进响应,要用 include=["file_search_call.results"] 才能拿到。tool_choice 保持 auto 时,两种搜索都是可选的。搜索必须执行时,就把它设为 required。
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[{"type": "web_search"}],
input="What was a positive news story from today?",
)
print(response.output_text)Ilan Bigio 和 OpenAI Developers 谈怎样写函数、怎样找函数
Ilan Bigio 在 OpenAI 做研究和工具,他在 2025-01-13 在 X 上发帖,宣布函数调用指南重写完成。照他的说法,团队听取了反馈:新指南篇幅减半,加了新的最佳实践,还有一个基于天气 API 的完整示例。本课的天气 schema 就出自这份指南的当前版本,所以自己写描述之前,先读读它的最佳实践。
OpenAI Developers 在 2026-03-05 宣布 GPT-5.4,帖子里有一行是:在更大的工具生态里做可扩展的工具搜索。这就是本课定义函数时讲到的 defer_loading 加 tool_search,在 gpt-5.4 及之后的模型上可用。函数多到超过指南的软性目标时,就按命名空间分组,把不常用的推迟加载。
在更大的工具生态里,做可扩展的工具搜索。

X 官方嵌入卡片的截图,拍摄于 2026-11-05,时间为 UTC。点击截图可到 X 上打开原帖。X 用 Show more 截断了 Ilan Bigio 的帖子。
常见错误
- 响应里有三个
function_call,却只读了第一个。 - 发送
function_call_output时,没带上它所回应的那个调用的call_id。 - 追加了你的输出,却没追加模型自己的输出项,结果推理项一直没传回去。
- 打开了严格模式,却有字段没列进
required,然后纳闷请求为什么被拒。 - 让模型去填你的代码早就有的 id。
response.function_call_arguments.done还没到,就开始解析流式传来的参数。- 每个回合都加载几十个函数,每次都为它们的定义付费。
tool_choice还是 auto,却指望每个问题都触发网页搜索。- 展示搜索得来的回答,却不附引用链接。
- 新代码还用
web_search_preview,而它会忽略域名过滤。
自己试一试
- 运行星座运势示例,在第一次调用后打印
response.output。里面有哪些类型的输出项? - 把天气 schema 里的
"units"从required中去掉,保持strict开启,读一读报错。 - 同时问两个城市的天气,数一数有几个
function_call项。 - 把
parallel_tool_calls设为 false,再问一次同样的问题。 - 加上
web_search,用filters只允许一个你信任的域名,比较引用有什么不同。 - 把一个 PDF 上传到向量存储,问一个只有这份文件能回答的问题。
接下来
第 3 课把这一课的严格模式用到模型的回复本身。你把 schema 作为 text.format 传入,或者传入一个 Pydantic 或 Zod 类,就能拿到代码可以直接解析、无需再检查的 JSON。
远程 MCP 服务器是 Responses API 接受的另一种工具。等你自己的函数多到一个文件装不下时,可以去了解这个协议,以及服务器怎样连接到 AI 应用。
自测一下
模型决定调用 get_horoscope 时,由谁来运行它?
查看答案
C. 你的应用代码 模型只返回一个 function_call 项,由你的代码运行函数并发回输出。
打开严格模式后,parameters 里的每个对象必须遵守哪条规则?
查看答案
D. additionalProperties 为 false,且每个字段都在 required 里 严格模式要求 additionalProperties 设为 false,每个属性都列进 required,可选字段在类型里加上 null。
你把 parallel_tool_calls 设为 false,模型在这个回合能调用几个工具?
查看答案
A. 零个或一个 关闭 parallel_tool_calls 后,模型每个回合只调用零个或一个工具。
什么把 function_call_output 和它回应的那个调用联系起来?
查看答案
B. call_id 每个输出都带着对应 function_call 的 call_id,模型就靠它来配对。
常见问题
什么是自定义工具,什么时候比函数更合适?
自定义工具接收自由格式的文本,而不是 JSON 参数。模型会把一段字符串直接交给你的代码,比如交给 code_exec 工具的一段 Python。指南建议在两种情况下用它:把输入包成 JSON 毫无必要的时候,或者你想用语法来约束输入的时候,语法可以用 Lark 写,也可以写成正则表达式。
网页搜索除了 token 还要另外收费吗?
要。每一次 search 动作都按一次工具调用计费,另算在模型读写的 token 之外,当前的费率见价格页面。Responses 里的网页搜索,也和你调用它的那个模型共用速率限制。
Chat Completions 还能用函数调用吗?
能,只是请求和响应的结构不同,而且除非你手动打开,否则不是严格模式。迁移指南列出了所有改动。GPT-6 Astra 和 GPT-6.1 Sol 调用工具必须用 Responses API,指南自己的 Chat Completions 示例用的是关闭推理的 GPT-5.6 Terra。
在微调模型上,严格模式的表现一样吗?
不完全一样。微调模型会在第一次请求时处理每个新的 schema 并缓存下来,所以每次请求的 schema 都不同,就会增加延迟。缓存的 schema 不适用零数据保留。如果微调模型在一个回合里调用了好几个函数,这些调用的严格模式会被关闭。
资料来源
- OpenAI Docs: Function calling https://developers.openai.com/api/docs/guides/function-calling
- OpenAI Docs: Using tools https://developers.openai.com/api/docs/guides/tools
- OpenAI Docs: Web search https://developers.openai.com/api/docs/guides/tools-web-search
- OpenAI Docs: File search https://developers.openai.com/api/docs/guides/tools-file-search
- OpenAI Docs: Migrate to the Responses API https://developers.openai.com/api/docs/guides/migrate-to-responses
- OpenAI Docs: Changelog https://developers.openai.com/api/docs/changelog
- X: Ilan Bigio announces the rewritten function calling guide https://x.com/ilanbigio/status/1878940258349510764
- X: OpenAI Developers announce GPT-5.4 with tool search https://x.com/OpenAIDevs/status/2029620984853188738
- YouTube (OpenAI): Build Hour: Agentic Tool Calling https://www.youtube.com/watch?v=7E-qdsVEoB8


