GPT-5 新参数和工具
我们正在为 GPT-5 系列引入新的开发者控件,让您能够更精细地控制模型响应——从塑造输出长度和风格到强制执行严格的格式。以下是最新功能的快速概述:
| # | 功能 | 概述 | 值 / 用途 |
# | 功能 | 概述 | 值 / 用途 |
---|---|---|---|
1. | 详细程度参数 | 允许您提示模型在其回复中更具扩展性或更不具扩展性。保持提示稳定,并使用参数而不是重写。 | • 低 → 简洁的用户体验,最少的文字。 • 中 (默认) → 平衡的细节。 • 高 → 冗长,非常适合审计、教学或交接。 |
2. | 自由格式函数调用 | 直接生成原始文本负载——从 Python 脚本到 SQL 查询——到您的自定义工具,无需 JSON 包装。为外部运行时提供更大的灵活性,例如: • 代码沙箱(Python、C++、Java 等) • SQL 数据库 • Shell 环境 • 配置生成器 |
当不需要结构化 JSON 且原始文本对于目标工具更自然时使用。 |
3. | 上下文无关文法 (CFG) | 定义语言中有效字符串的生成规则集。每条规则将一个非终结符重写为终结符和/或其它非终结符,独立于周围的上下文。适用于将输出限制为匹配 OpenAI 工具中编程语言或自定义格式的语法。 | 用作合同,以确保模型仅发出语法可接受的有效字符串。 |
4. | 最小推理 | 使用很少或没有推理令牌运行 GPT-5,以最小化延迟并加快首次令牌时间。适用于确定性、轻量级任务(提取、格式化、简短重写、简单分类),而无需解释。如果未指定,则默认努力程度为中等。 | 设置推理努力程度:“最小”。避免用于多步规划或大量使用工具的工作流。 |
支持的模型:
- gpt-5
- gpt-5-mini
- gpt-5-nano
支持的 API 端点
- 响应 API
- 聊天补全 API
注意:我们建议将响应 API 与 GPT-5 系列模型结合使用,以充分发挥模型的性能。
先决条件
让我们开始更新支持 GPT-5 新参数和工具的 OpenAI SDK。请确保您已将 OPENAI_API_KEY 设置为环境变量。
!pip install --quiet --upgrade openai pandas && \
echo -n "openai " && pip show openai | grep '^Version:' | cut -d' ' -f2 && \
echo -n "pandas " && pip show pandas | grep '^Version:' | cut -d' ' -f2
openai 1.99.2
pandas 2.3.1
1. 详细程度参数
1.1 概述
详细程度参数允许您提示模型在其回复中更具扩展性或更不具扩展性。
值:“低”、“中”、“高”
- 低 → 简洁的用户体验,最少的文字。
- 中 (默认) → 平衡的细节。
- 高 → 冗长,非常适合审计、教学或交接。
保持提示稳定,并使用参数而不是重写。
from openai import OpenAI
import pandas as pd
from IPython.display import display
client = OpenAI()
question = "写一首关于一个男孩和他第一只宠物狗的诗。"
data = []
for verbosity in ["low", "medium", "high"]:
response = client.responses.create(
model="gpt-5-mini",
input=question,
text={"verbosity": verbosity}
)
# 提取文本
output_text = ""
for item in response.output:
if hasattr(item, "content"):
for content in item.content:
if hasattr(content, "text"):
output_text += content.text
usage = response.usage
data.append({
"详细程度": verbosity,
"示例输出": output_text,
"输出令牌": usage.output_tokens
})
# 创建 DataFrame
df = pd.DataFrame(data)
# 漂亮地显示带居中标题的表格
pd.set_option('display.max_colwidth', None)
styled_df = df.style.set_table_styles(
[
{'selector': 'th', 'props': [('text-align', 'center')]}, # 居中列标题
{'selector': 'td', 'props': [('text-align', 'left')]} # 左对齐表格单元格
]
)
display(styled_df)
详细程度 | 示例输出 | 输出令牌 | |
---|---|---|---|
0 | low | 他发现一个春天下午,在棚子后面有一团毛茸茸的东西, 在灰尘和光线之下,有一个小而快速的心跳。 世界缩小到两个人——运动鞋上的泥土,摇摆,笨拙的曲调—— 名字像弹珠一样从他嘴里滚出来,简单,确定,明亮。 他们学会了彼此的双手地图:耳朵下面的抓挠, 一场风暴如何改变勇敢的形状变成颤抖。 早晨是吐司和阳光,下午是奔跑在附近 河岸,树叶为他们每一次的跳跃鼓掌。 晚上,他们分享一条毯子和黑暗的秘密, 男孩低语着故事,狗则呼吸均匀。 岁月编织成脚印——先是擦伤的膝盖,然后是口鼻处 一丝谷仓般的灰色,缓慢而温柔,如同花环。 当时间打开它的大门,男孩仍然带着小东西: 一个项圈,一双咬过的鞋子,一声教会他如何希望的吠叫的回声。 他学会了爱可以看起来像留下普通国王的碎屑, 以及有些第一次,即使它们已经消失,也会装在你的口袋里。 | 560 |
1 | medium | 他发现他蜷缩在一个纸板箱里, 一个小小的胸腔像一个想法一样抽搐着。 男孩的口袋里装满了硬币和承诺; 狗的眼睛像两个小问题。 他们一起学会了名字——男孩说了一个, 狗歪着头接受了。 早晨是笨拙的课程:手里拿着牵引绳, 狗以一种惊奇的喷嚏声发现了人行道, 男孩在绳子的尽头发现了勇气。 他们追逐下午进入水坑, 泥土亲吻着男孩的膝盖和狗的胡须。 狗教会他如何扔出永远不会回来的棍子 以及如何原谅它们没有回来。 晚上是秘密的时刻: 狗的呼吸是男孩脚踝旁一个温暖的标点, 天空变成了蓝黑色。 家庭作业成了他们世界之间的一个小岛, 一支铅笔,一次抚摸,地毯上忠实的爪子。 狗学会了坐下吃苹果, 如何藏起一个冰冷的鼻子在毯子般的指缝间。 男孩学会了如何缝补一只撕破的泰迪熊, 如何说对不起并且是认真的。 有雷雨的日子,男孩的膝盖在打颤, 而狗,一副严肃的责任感,把头 埋进男孩的恐惧的空虚里,在那里支撑着 仿佛可以用下巴锚定闪电。 他们一起练习勇敢:门为新学校打开, 新路,第一辆没有辅助轮的自行车, 狗是尾巴和温暖的稳定节拍器, 从不要求成为别的,只是存在。 季节像往常一样展开。 雪来了,在院子里铺上白色的问题; 夏天伸出它懒洋洋的手,留下草在八月里枯黄。 男孩长高了,后来,狗的动作变慢了, 但下午晚些时候,他们仍然分享着同样的光—— 阳光和阴影的私人货币。 当男孩学会了告别的语言, 是狗教会了他如何缓和它们。 最后的凝视,手在衣服上徘徊, 以及一个承诺,在所有平凡的小日子里 有什么不可战胜的东西被编织在一起: 两颗心,一条牵引绳,门槛上的爪印地图。 多年以后,男孩——现在长大了——把一张照片 放进外套口袋。他感觉到那个曾经 放着温暖头颅的凹陷处,笑了。 有些羁绊拒绝被收起来。 在回忆的低语中, 他仍然能听到项圈的叮当声和一声小小的、快乐的吠叫: 第一个家,第一个朋友,第一个永远。 | 849 |
2 | high | 男孩遇见他的狗的那天,世界变得更广阔了—— 一团小小的毛发和一个湿漉漉的、认真的鼻子 像一个秘密一样压在他的手掌上。 他们站在门廊上,太阳好奇地倾斜着, 仿佛天空来观看两个新事物 如何契合在一起。 他以一个漫画英雄的名字命名他, 或者他一开始根本没有给他起名字, 只是笑着,让笑声变成一个名字。 他们互相了解对方的重量:狗的沉重喜悦, 男孩瘦弱、谨慎的手变成了能够 稳稳握住一颗跳动的心。 早晨变成爪子和麦片的合唱, 项圈的叮当声和椅子的刮擦声。 家庭作业只有在狗的批准下才能完成; 数学题在摇摆的尾巴下, 拼写测试被口水沾湿的元音打断。 他们把秘密藏在床底下,在灰尘兔子之间, 并分享着活着的小而完美的阴谋。 下午是一张冒险地图:裂开的人行道, 闻起来像石头和苔藓的河流,风 感觉像是奔跑的许可的山丘。 狗学会了捡棍子和被遗忘的勇气, 而男孩则学会了勇敢可以像 放在膝盖上的温暖的头一样柔软, 或者像吓跑雷声的吠叫一样响亮。 夏天教会了他们一天有多长。 他们追逐影子和彼此,制定小规则: 不要在郁金香里挖洞,不要追逐邮递员, 除了郁金香从来没有机会。 男孩的膝盖收集了故事——擦伤愈合了, 弄脏了袜子但没有弄脏笑容的泥土。 狗的耳朵学会了男孩呼吸的节奏, 它像船找到港口一样倾斜入睡。 岁月像旧书页一样折叠。男孩长高了, 他的声音卡在曾经吞咽的词语上。 学校占据了下午;朋友们占据了电话号码。 然而,狗总能找到方法成为一个国家,男孩可以在其中消失 并总是再次被找到——在门廊上,在后门旁, 那里尾巴敲打着家的节奏。 时间像冬天一样一点点到来。 狗的口鼻变得银白;他的步伐变得谨慎。 他不再适合他曾经拥有的空间 并学会了要求休息。男孩——不再是男孩—— 坐得更久,描摹着每一道疤痕和胡须的灰色地图。 有些夜晚,狗的呼吸微弱而诚实, 男孩把额头抵着狗的额头说出声来: 我在这里。你是对的。你教会了我如何做。 最后一个早晨很安静,就像结局通常那样: 一种不需要匆忙的光,一片保持蓝色的天空。 曾经瘦弱的手承载着告别的重量, 而那只教会他一切告别的狗, 温柔地像一个故事的结尾。 他们在苹果树下埋了一根骨头,那里的阴影还记得他们。 黄昏时,男孩——长大了,双手布满劳作的痕迹,心中却有孩童般的记忆—— 看着草向大地倾斜的地方,侧耳倾听。 曾经,房子一模一样却又不是, 他发誓在厨房里听到了熟悉的轻柔的叮当声, 一阵脚步声朝着门走来。 那一刻,世界回到了过去的样子: 门廊灯,项圈,笑声像硬币一样洒进口袋。 岁月会教会你如何在没有你所爱之物的身体的情况下生活, 但它们无法教会你它爱的形状。 在细微之处,他带着狗——棚子后面一个旧球, 雨水打在热土上的气味,忠诚感 像一块温热的石头压在肋骨下。有时,晚上, 他仍然呼唤一个名字,就像你呼唤大海一样: 感受一个声音传来,即时而轻柔, 并回忆起被选中的简单奇迹。 第一只狗是爱的第一张地图: 袖子上的毛发,总是回家的脚步声。 它教会男孩如何在风雨中站稳, 如何通过善良来勇敢,以及如何保持一个地方的温暖。 如果你倾听,有时过去仍然会回应, 带着叮当声,摇尾巴,以及一声小而完美的呼吸的回声。 | 1288 |
输出令牌大致与详细程度成线性比例:低(560)→ 中(849)→ 高(1288)。
1.3 经验总结
新的详细程度参数可靠地扩展了模型输出的长度和深度,同时保持了正确性和推理质量——而无需更改底层提示。 在此示例中:
- 低详细程度会生成最小化的、功能性的脚本,没有额外的注释或结构。
- 中详细程度添加了说明性注释、函数结构和可重复性控件。
- 高详细程度生成了全面的、生产就绪的脚本,包含参数解析、多种排序方法、计时/验证、用法说明和最佳实践提示。
2. 自由格式函数调用
2.1 概述
GPT‑5 现在可以通过新的工具 "type": "custom"
将原始文本负载——从 Python 脚本到 SQL 查询——发送到您的自定义工具,而无需将数据包装在 JSON 中。这与经典的结构化函数调用不同,为与外部运行时交互提供了更大的灵活性,例如:
code_exec
沙箱(Python、C++、Java 等)- SQL 数据库
- Shell 环境
- 配置生成器
请注意,自定义工具类型不支持并行工具调用。
2.2 快速入门示例 - 计算圆的面积
下面的代码生成了一个简单的 Python 代码来计算圆的面积,并指示模型使用自由格式工具调用来输出结果。
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-5-mini",
input="请使用 code_exec 工具计算半径等于 strawberry 中 'r' 数量的圆的面积",
text={"format": {"type": "text"}},
tools=[
{
"type": "custom",
"name": "code_exec",
"description": "执行任意 Python 代码",
}
]
)
print(response.output)
[ResponseReasoningItem(id='rs_6894e31b1f8081999d18325e5aeffcfe0861a2e1728d1664', summary=[], type='reasoning', content=[], encrypted_content=None, status=None), ResponseCustomToolCall(call_id='call_Gnqod2MwPvayp2JdNyA0z0Ah', input='# Counting \'r\'s in the word "strawberry" and computing circle area with that radius\nimport math\nr = "strawberry".count(\'r\')\narea = math.pi * r**2\n{"radius": r, "area": area, "area_exact": f"{r}*pi"}', name='code_exec', type='custom_tool_call', id='ctc_6894e31c66f08199abd622bb5ac3c4260861a2e1728d1664', status='completed')]
模型发出一个包含原始 Python 的 tool call
。您在服务器端执行该代码,捕获打印的结果,并在后续的 responses.create
调用中将其发送回来。
2.3 微基准测试 – 三种语言的数组排序
为了说明自由格式工具调用的用法,我们将要求 GPT-5:
- 生成排序固定数组的 Python、C++ 和 Java 代码(各 10 次)。
- 仅打印每次迭代所需的时间(以毫秒为单位)。
- 调用所有三个函数,然后停止。
from openai import OpenAI
from typing import List, Optional
MODEL_NAME = "gpt-5"
# 将传递给所有模型调用的工具。它们被定义一次,
# 以便配置保存在一个地方。
TOOLS = [
{
"type": "custom",
"name": "code_exec_python",
"description": "执行 Python 代码",
},
{
"type": "custom",
"name": "code_exec_cpp",
"description": "执行 C++ 代码",
},
{
"type": "custom",
"name": "code_exec_java",
"description": "执行 Java 代码",
},
]
client = OpenAI()
def create_response(
input_messages: List[dict],
previous_response_id: Optional[str] = None,
):
"""包装 ``client.responses.create``。
参数
----------
input_messages: List[dict]
要馈送给模型的运行对话历史。
previous_response_id: str | None
传递*上一个*调用的 ``response.id``,以便模型可以保持对话线程。首次请求时省略。
"""
kwargs = {
"model": MODEL_NAME,
"input": input_messages,
"text": {"format": {"type": "text"}},
"tools": TOOLS,
}
if previous_response_id:
kwargs["previous_response_id"] = previous_response_id
return client.responses.create(**kwargs)
# 递归调用
def run_conversation(
input_messages: List[dict],
previous_response_id: Optional[str] = None,
):
response = create_response(input_messages, previous_response_id)
# ``response.output`` 预计是一个列表,其中元素 0 是模型消息。
# 元素 1(如果存在)表示工具调用。当模型完成工具调用时,该元素将被省略。
tool_call = response.output[1] if len(response.output) > 1 else None
if tool_call and tool_call.type == "custom_tool_call":
print("--- tool name ---")
print(tool_call.name)
print("--- tool call argument (generated code) ---")
print(tool_call.input)
# 添加一个合成的*工具结果*,以便模型可以继续线程。
input_messages.append(
{
"type": "function_call_output",
"call_id": tool_call.call_id,
"output": "done", # <-- replace with the result of the tool call
}
)
# 使用更新后的对话进行递归调用,并跟踪响应 ID,以便模型了解之前的回合。
return run_conversation(input_messages, previous_response_id=response.id)
else:
# 基本情况:没有进一步的工具调用 - 返回。
return
prompt = """
编写代码,使用 code_exec 函数以三种语言(C++、Python 和 Java)对数组进行排序(各 10 次)。
始终只调用这三个函数一次:code_exec_python、code_exec_cpp 和 code_exec_java 工具来对每种语言的数组进行排序。调用完每种语言的这三个函数后停止。
仅打印排序数组所需的时间(毫秒)。
[448, 986, 255, 884, 632, 623, 246, 439, 936, 925, 644, 159, 777, 986, 706, 723, 534, 862, 195, 686, 846, 880, 970, 276, 613, 736, 329, 622, 870, 284, 945, 708, 267, 327, 678, 807, 687, 890, 907, 645, 364, 333, 385, 262, 730, 603, 945, 358, 923, 930, 761, 504, 870, 561, 517, 928, 994, 949, 233, 137, 670, 555, 149, 870, 997, 809, 180, 498, 914, 508, 411, 378, 394, 368, 766, 486, 757, 319, 338, 159, 585, 934, 654, 194, 542, 188, 934, 163, 889, 736, 792, 737, 667, 772, 198, 971, 459, 402, 989, 949]
"""
# 初始开发者消息。
messages = [
{
"role": "developer",
"content": prompt,
}
]
run_conversation(messages)
--- tool name ---
code_exec_python
--- tool call argument (generated code) ---
import time
arr = [448, 986, 255, 884, 632, 623, 246, 439, 936, 925, 644, 159, 777, 986, 706, 723, 534, 862, 195, 686, 846, 880, 970, 276, 613, 736, 329, 622, 870, 284, 945, 708, 267, 327, 678, 807, 687, 890, 907, 645, 364, 333, 385, 262, 730, 603, 945, 358, 923, 930, 761, 504, 870, 561, 517, 928, 994, 949, 233, 137, 670, 555, 149, 870, 997, 809, 180, 498, 914, 508, 411, 378, 394, 368, 766, 486, 757, 319, 338, 159, 585, 934, 654, 194, 542, 188, 934, 163, 889, 736, 792, 737, 667, 772, 198, 971, 459, 402, 989, 949]
start = time.perf_counter()
for _ in range(10):
b = arr[:] # copy
b.sort()
elapsed_ms = int((time.perf_counter() - start) * 1000)
print(elapsed_ms, end="")
--- tool name ---
code_exec_cpp
--- tool call argument (generated code) ---
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
using namespace std;
int main() {
vector<int> a = {448, 986, 255, 884, 632, 623, 246, 439, 936, 925, 644, 159, 777, 986, 706, 723, 534, 862, 195, 686, 846, 880, 970, 276, 613, 736, 329, 622, 870, 284, 945, 708, 267, 327, 678, 807, 687, 890, 907, 645, 364, 333, 385, 262, 730, 603, 945, 358, 923, 930, 761, 504, 870, 561, 517, 928, 994, 949, 233, 137, 670, 555, 149, 870, 997, 809, 180, 498, 914, 508, 411, 378, 394, 368, 766, 486, 757, 319, 338, 159, 585, 934, 654, 194, 542, 188, 934, 163, 889, 736, 792, 737, 667, 772, 198, 971, 459, 402, 989, 949};
auto start = chrono::high_resolution_clock::now();
for (int i = 0; i < 10; ++i) {
auto b = a;
sort(b.begin(), b.end());
}
auto end = chrono::high_resolution_clock::now();
auto ms = chrono::duration_cast<chrono::milliseconds>(end - start).count();
cout << ms;
return 0;
}
--- tool name ---
code_exec_java
--- tool call argument (generated code) ---
import java.util.*;
public class Main {
public static void main(String[] args) {
int[] a = new int[] {448, 986, 255, 884, 632, 623, 246, 439, 936, 925, 644, 159, 777, 986, 706, 723, 534, 862, 195, 686, 846, 880, 970, 276, 613, 736, 329, 622, 870, 284, 945, 708, 267, 327, 678, 807, 687, 890, 907, 645, 364, 333, 385, 262, 730, 603, 945, 358, 923, 930, 761, 504, 870, 561, 517, 928, 994, 949, 233, 137, 670, 555, 149, 870, 997, 809, 180, 498, 914, 508, 411, 378, 394, 368, 766, 486, 757, 319, 338, 159, 585, 934, 654, 194, 542, 188, 934, 163, 889, 736, 792, 737, 667, 772, 198, 971, 459, 402, 989, 949};
long start = System.nanoTime();
for (int i = 0; i < 10; i++) {
int[] b = Arrays.copyOf(a, a.length);
Arrays.sort(b);
}
long elapsedMs = (System.nanoTime() - start) / 1_000_000L;
System.out.print(elapsedMs);
}
}
模型输出了 Python、C++ 和 Java 的三个代码块,用于相同的算法。函数调用的输出被链接回模型作为输入,以允许模型继续进行,直到所有函数都被调用一次。
2.4 经验总结
GPT-5 中的自由格式工具调用允许您将原始文本负载——例如 Python 脚本、SQL 查询或配置文件——直接发送到自定义工具,而无需 JSON 包装。这为与外部运行时交互提供了更大的灵活性,并允许模型以您的工具期望的确切格式生成代码或文本。当不需要结构化 JSON 且自然文本输出可提高可用性时,它是理想选择。
3. 上下文无关文法 (CFG)
3.1 概述
上下文无关文法是定义语言中有效字符串的生成规则集合。每条规则将一个非终结符重写为一系列终结符(字面标记)和/或其他非终结符,独立于周围的上下文——因此称为上下文无关。CFG 可以捕获大多数编程语言的语法,并且在 OpenAI 自定义工具中,它们可以作为合同,强制模型仅生成语法可接受的字符串。
3.2 文法基础
支持的文法语法
- Lark - https://lark-parser.readthedocs.io/en/stable/
- Regex - https://docs.rs/regex/latest/regex/#syntax
我们在底层使用 LLGuidance 来约束模型采样:https://github.com/guidance-ai/llguidance。
不支持的 Lark 功能
- 正则表达式中的环视(
(?=...)
、(?!...)
等) - 正则表达式中的惰性修饰符(
*?
、+?
、??
)。 - 终端优先级、模板、
%declares
、%import
(除了%import common
)。
终结符与规则 & 贪婪词法分析
| 概念 | 要点 |
概念 | 要点 |
---|---|
终结符 (大写) | 首先由词法分析器匹配——最长匹配获胜。 |
规则 (小写) | 组合终结符;不能影响文本的标记方式。 |
贪婪词法分析器 | 永远不要尝试跨多个终结符“塑造”自由文本——您会失去控制。 |
** 正确 vs 错误模式设计
✅ 一个有界终结符处理锚点之间的自由文本 start: SENTENCE SENTENCE: /[A-Za-z, ](the hero|a dragon)[A-Za-z, ](fought|saved)[A-Za-z, ](a treasure|the kingdom)[A-Za-z, ]./
❌ 不要将自由文本拆分到多个终结符/规则中 start: sentence sentence: /[A-Za-z, ]+/ subject /[A-Za-z, ]+/ verb /[A-Za-z, ]+/ object /[A-Za-z, ]+/
3.3 示例 - SQL 方言 — MS SQL vs PostgreSQL
以下代码示例是构建多方言 SQL 工具与 CFG 的规范参考。它演示了:
- 两个独立的文法定义(
mssql_grammar_definition
、postgres_grammar_definition
),编码了 TOP 与 LIMIT 的语义。 - 如何在单个脚本中提示、调用和检查工具调用。
- 并排检查助手的响应。
为不同的 SQL 方言定义 LARK 文法
import textwrap
# ----------------- MS SQL 方言的文法 -----------------
mssql_grammar = textwrap.dedent(r"""
// ---------- 标点符号和运算符 ----------
SP: " "
COMMA: ","
GT: ">"
EQ: "="
SEMI: ";"
// ---------- 开始 ----------
start: "SELECT" SP "TOP" SP NUMBER SP select_list SP "FROM" SP table SP "WHERE" SP amount_filter SP "AND" SP date_filter SP "ORDER" SP "BY" SP sort_cols SEMI
// ---------- 投影 ----------
select_list: column (COMMA SP column)*
column: IDENTIFIER
// ---------- 表 ----------
table: IDENTIFIER
// ---------- 过滤器 ----------
amount_filter: "total_amount" SP GT SP NUMBER
date_filter: "order_date" SP GT SP DATE
// ---------- 排序 ----------
sort_cols: "order_date" SP "DESC"
// ---------- 终结符 ----------
IDENTIFIER: /[A-Za-z_][A-Za-z0-9_]*/
NUMBER: /[0-9]+/
DATE: /'[0-9]{4}-[0-9]{2}-[0-9]{2}'/
""")
# ----------------- PostgreSQL 方言的文法 -----------------
postgres_grammar = textwrap.dedent(r"""
// ---------- 标点符号和运算符 ----------
SP: " "
COMMA: ","
GT: ">"
EQ: "="
SEMI: ";"
// ---------- 开始 ----------
start: "SELECT" SP select_list SP "FROM" SP table SP "WHERE" SP amount_filter SP "AND" SP date_filter SP "ORDER" SP "BY" SP sort_cols SP "LIMIT" SP NUMBER SEMI
// ---------- 投影 ----------
select_list: column (COMMA SP column)*
column: IDENTIFIER
// ---------- 表 ----------
table: IDENTIFIER
// ---------- 过滤器 ----------
amount_filter: "total_amount" SP GT SP NUMBER
date_filter: "order_date" SP GT SP DATE
// ---------- 排序 ----------
sort_cols: "order_date" SP "DESC"
// ---------- 终结符 ----------
IDENTIFIER: /[A-Za-z_][A-Za-z0-9_]*/
NUMBER: /[0-9]+/
DATE: /'[0-9]{4}-[0-9]{2}-[0-9]{2}'/
""")
3.4 生成特定的 SQL 方言
让我们定义提示,并调用函数来生成 MS SQL 方言
from openai import OpenAI
client = OpenAI()
sql_prompt_mssql = (
"调用 mssql_grammar 为 Microsoft SQL Server 生成一个查询,该查询检索每个客户最近的五个订单,显示 customer_id、order_id、order_date 和 total_amount,"
"其中 total_amount > 500 且 order_date 在 '2025-01-01' 之后。"
)
response_mssql = client.responses.create(
model="gpt-5",
input=sql_prompt_mssql,
text={"format": {"type": "text"}},
tools=[
{
"type": "custom",
"name": "mssql_grammar",
"description": "执行只读的 Microsoft SQL Server 查询,仅限于 SELECT 语句,包含 TOP 和基本的 WHERE/ORDER BY。您必须仔细推理查询并确保它遵守文法。",
"format": {
"type": "grammar",
"syntax": "lark",
"definition": mssql_grammar
}
},
],
parallel_tool_calls=False
)
print("--- MS SQL Query ---")
print(response_mssql.output[1].input)
--- MS SQL Query ---
SELECT TOP 5 customer_id, order_id, order_date, total_amount FROM orders WHERE total_amount > 500 AND order_date > '2025-01-01' ORDER BY order_date DESC;
输出的 SQL 准确地使用了“SELECT TOP”结构。
sql_prompt_pg = (
"调用 postgres_grammar 为 PostgreSQL 生成一个查询,该查询检索每个客户最近的五个订单,显示 customer_id、order_id、order_date 和 total_amount,"
"其中 total_amount > 500 且 order_date 在 '2025-01-01' 之后。"
)
response_pg = client.responses.create(
model="gpt-5",
input=sql_prompt_pg,
text={"format": {"type": "text"}},
tools=[
{
"type": "custom",
"name": "postgres_grammar",
"description": "执行只读的 PostgreSQL 查询,仅限于 SELECT 语句,包含 LIMIT 和基本的 WHERE/ORDER BY。您必须仔细推理查询并确保它遵守文法。",
"format": {
"type": "grammar",
"syntax": "lark",
"definition": postgres_grammar
}
},
],
parallel_tool_calls=False,
)
print("--- PG SQL Query ---")
print(response_pg.output[1].input)
--- PG SQL Query ---
SELECT customer_id, order_id, order_date, total_amount FROM orders WHERE total_amount > 500 AND order_date > '2025-01-01' ORDER BY order_date DESC LIMIT 5;
输出突出了相同的逻辑查询——不同的物理语法。提供不同的文法,以便模型只能为所选方言生成有效的语句。
| 方言 | 生成的查询 | 关键区别 |
方言 | 生成的查询 | 关键区别 |
---|---|---|
MS SQL Server | SELECT TOP 5 customer_id, … ORDER BY order_date DESC; | 在列列表之前使用 TOP N 子句。 |
PostgreSQL | SELECT customer_id, … ORDER BY order_date DESC LIMIT 5; | 在 ORDER BY 之后使用 LIMIT N 。 |
3.5 示例 - Regex CFG 语法
以下代码示例演示了如何使用 Regex CFG 语法将自由格式工具调用限制为特定的时间戳模式。
from openai import OpenAI
client = OpenAI()
timestamp_grammar_definition = r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) (?:[01]\d|2[0-3]):[0-5]\d$"
timestamp_prompt = (
"调用 timestamp_grammar 为 2025 年 8 月 7 日上午 10 点保存一个时间戳。"
)
response_mssql = client.responses.create(
model="gpt-5",
input=timestamp_prompt,
text={"format": {"type": "text"}},
tools=[
{
"type": "custom",
"name": "timestamp_grammar",
"description": "以 24 小时格式保存日期 + 时间戳。",
"format": {
"type": "grammar",
"syntax": "regex",
"definition": timestamp_grammar_definition
}
},
],
parallel_tool_calls=False
)
print("--- Timestamp ---")
print(response_mssql.output[1].input)
--- Timestamp ---
2025-08-07 10:00
3.5 最佳实践
Lark 文法可能很难完善。虽然简单的文法最可靠,但复杂的文法通常需要对文法定义本身、提示和工具描述进行迭代,以确保模型不会超出分布。
- 保持终结符有界——使用
/[^.\n]{0,10}*\./
而不是/.*\./
。通过内容(否定字符类)和长度({M,N}
量词)限制匹配。 - 优先使用显式字符类而不是
.
通配符。 - 显式地处理空格,例如使用
SP = " "
,而不是全局的%ignore
。 - 描述您的工具:准确告知模型 CFG 接受的内容,并指示它进行大量推理以确保合规性。
故障排除
- API 因文法过于复杂而拒绝 —— 简化规则和终结符,删除
%ignore.*
。 - 意外的标记 ―― 确认终结符没有重叠;检查贪婪词法分析器。
- 当模型偏离“分布外”(表现为模型产生过长或重复的输出,虽然语法有效但语义错误)时:
- 收紧文法。
- 迭代提示(添加少量示例)和工具描述(解释文法并指示模型进行推理以符合它)。
- 尝试更高的推理努力(例如,从“中”提高到“高”)。
资源:
- Lark 文档 – https://lark-parser.readthedocs.io/en/stable/
- Lark IDE – https://www.lark-parser.org/ide/
- LLGuidance 语法 – https://github.com/guidance-ai/llguidance/blob/main/docs/syntax.md
- Regex (Rust crate) – https://docs.rs/regex/latest/regex/#syntax
3.6 经验总结
GPT-5 中的上下文无关文法 (CFG) 支持允许您将模型输出严格限制为匹配预定义的语法,确保只生成有效字符串。这对于强制执行编程语言规则或自定义格式特别有用,可以减少后期处理和错误。通过提供精确的文法和清晰的工具描述,您可以使模型可靠地保持在目标输出结构内。
4. 最小推理
4.1 概述
GPT-5 现在支持新的最小推理功能。在使用最小推理时,模型将输出很少或不输出推理令牌。这适用于开发者希望获得非常快的首次用户可见令牌时间的用例。注意:如果未提供推理努力程度,则默认值为中等。
from openai import OpenAI
client = OpenAI()
prompt = "将评论的情感分类为积极|中性|消极。只返回一个词。"
response = client.responses.create(
model="gpt-5",
input= [{ 'role': 'developer', 'content': prompt },
{ 'role': 'user', 'content': '这家餐厅的食物很棒!我推荐给所有人。' }],
reasoning = {
"effort": "minimal"
},
)
# 提取模型的文本输出
output_text = ""
for item in response.output:
if hasattr(item, "content"):
for content in item.content:
if hasattr(content, "text"):
output_text += content.text
# 令牌使用详情
usage = response.usage
print("--------------------------------")
print("输出:")
print(output_text)
--------------------------------
输出:
positive
4.2 经验总结
最小推理使用很少或没有推理令牌运行 GPT-5,以最小化延迟并加快首次令牌时间。将其用于确定性、轻量级任务(提取、格式化、简短重写、简单分类),而无需解释。如果您不指定努力程度,它默认为中等——当您希望速度胜过深思熟虑时,请明确设置最小。