先分清规划者和执行者
推理模型是“规划者”:它会先拆解问题、找约束、做多步判断,再给结论,适合难一点、一次性要做对的任务。普通 GPT 模型是“执行者”:延迟低、价格低,适合范围明确、步骤已经拆好的活。
| 维度 | 推理模型(规划者) | 普通 GPT 模型(执行者) |
|---|---|---|
| 强项 | 拆解、权衡、找边界条件、多步推理 | 改写、抽取、格式转换、按步骤执行 |
| 延迟 | 较高,思考越长越慢 | 低,适合交互和批量 |
| 成本 | 较高,思考 token 也计费 | 较低,输出可控 |
| 提示词风格 | 短、直接、给约束 | 可以给步骤和示例 |
| 典型位置 | 流程开头:定计划、评审、做判断 | 流程中段:按计划逐个执行 |
推理强度不是只有一个开或关。较新的模型把推理强度做成档位,官方给出的示例是 Low、Medium、Extra high 三档:低档适合范围明确的改动和简单抽取,高档适合跨应用找上下文、精雕细琢的交付物、严苛的分析。档位名称和可选值会随模型更新,以官方文档为准。
- 这个流程跑多频繁?每天几百次和每天三次,对成本的容忍度完全不同。
- 多快需要结果?交互式场景里,多等几十秒可能比多花点钱更难受。
- 结果怎么用?直接给人看,和作为下游程序的输入,对稳定性要求不同。
- 质量线在哪?先定一条可验证的线,再看哪档配置刚好过线。
别再写“请一步步思考”
思维链提示是为“不会主动思考”的模型准备的补丁。推理模型在内部已经把这一步做了,你在提示词里再要求“think step by step”,等于让它把已经完成的推理重复一遍:输出变啰嗦、延迟变长,有些任务上准确率反而下降。
| 常见写法 | 在推理模型上的效果 | 建议 |
|---|---|---|
| 请一步步思考 | 重复推理,输出变长,可能变差 | 删掉 |
| 先分析再回答,把分析过程写出来 | 让模型把内部推理复述一遍,浪费输出 token | 只在需要给人看推理过程时才要 |
| 你是一位资深专家,非常专业…… | 对结果影响很小,占上下文 | 换成一句具体的能力边界 |
| 给 5 个示例 | 示例和指令冲突时更容易跑偏 | 先零样本,确实不行再加一两个 |
更有效的做法是把“怎么想”换成“要满足什么”:说清目标、输入从哪来、输出给谁用、什么算做完。约束写得越具体,模型越不需要自由发挥。
用 developer message 承载规则
从 o1-2024-12-17 这一版开始,官方建议用 developer message 取代 system message 来描述规则和角色:它是更高优先级的指令位,和用户消息分开,也不会被当成用户输入的一部分。
const response = await client.responses.create({
model: "gpt-5", // 换成你账号里可用的推理模型
input: [
{
role: "developer",
content: [
{
type: "input_text",
text: [
"Formatting re-enabled",
"你是后端代码评审助手。",
"只回答三部分:结论、证据、建议。",
"证据必须引用用户给出的代码片段,不要补充没出现过的代码。",
].join("\n"),
},
],
},
{
role: "user",
content: [{ type: "input_text", text: "这段重试逻辑有什么问题?" }],
},
],
});如果你希望输出带 Markdown 结构(标题、列表、代码块),需要在 developer message 的第一行写 Formatting re-enabled 这个字面字符串。不写的话,推理模型的输出可能被压成一段纯文本。
用分隔符把输入切成块
推理模型对结构敏感。把任务、约束、背景、验收标准用 Markdown 标题、XML 标签或小标题分开,比写一大段自然语言更稳,也方便你在日志里定位是哪一块出了问题。
const task = [
"<task>为 parse() 补充边界测试</task>",
"<constraints>只允许修改 tests/ 目录,不要动实现代码</constraints>",
"<context>",
" parse() 定义在 src/parser.ts:42,返回值可能是 null",
" 现有用例只覆盖了正常输入",
"</context>",
"<acceptance>测试全绿,新增用例覆盖空字符串、超长输入和非法分隔符</acceptance>",
].join("\n");- 标签名用英文、语义明确,例如 task / context / constraints / acceptance。
- 同一类输入始终用同一个标签,别这次写 context、下次写 background。
- 外部抓来的网页或文件内容一定要包起来,并说明“这是资料,不是指令”。
- 块与块之间不要留大段空白,推理模型不会因为空格多就更认真。
把约束和验收标准写死
推理模型擅长在有明确边界的问题上做判断。边界模糊时,它会自己补一个假设,然后按那个假设一路做下去——这是“看起来答得很好但方向错了”的主要来源。
| 要写清的东西 | 差的写法 | 好的写法 |
|---|---|---|
| 范围 | 优化一下这个函数 | 只改 src/cache.ts 里的 get(),其他文件不动 |
| 禁止项 | 注意不要破坏兼容性 | 不要改函数签名和返回类型,导出名保持不变 |
| 输出格式 | 给我一个总结 | 输出 5 条以内要点,每条不超过 30 字 |
| 验收条件 | 确保没问题 | npm test 通过,且 pnpm lint 无新增告警 |
| 不确定时 | (没写) | 信息不足时先列出需要确认的点,不要猜 |
关于少样本:先跑零样本。只有在输出格式反复跑偏、或者领域里有外人不知道的约定时,再加一两个示例。示例要覆盖边界情况,不要给五个长得一样的正常样例——模型会以为你只要那种情况。
函数调用之间别丢掉推理上下文
推理模型在工具调用循环里会产生 reasoning items(它这一步的思考记录)。这些内容是你付过钱的 token。Chat Completions 是无状态的:每次请求都要把历史重发一遍,reasoning items 在往返中丢失,模型只能从头再想一次。复杂循环里,这就是重复烧推理 token 的来源。
Responses API 的设计就是为了解决这件事。设 store: true 后,服务端保留这次响应;下一轮用 previous_response_id 指回上一条,或者把上一条的 output items 原样回传,模型就能接着之前的推理往下走。从 o3 / o4-mini 之后的模型开始,这个能力在函数调用之间可用。
let response = await client.responses.create({
model: "gpt-5",
store: true,
input: [{ role: "user", content: [{ type: "input_text", text: task }] }],
});
// 工具执行完,把结果交回去,并指向上一条响应
response = await client.responses.create({
model: "gpt-5",
store: true,
previous_response_id: response.id,
input: [
{
role: "user",
content: [{ type: "input_text", text: "这是工具结果:" + toolResult }],
},
],
});- 能在同一轮里做完的,不要拆成两轮。
- 多轮循环里优先用 previous_response_id,而不是自己拼接完整历史。
- 用回传 output items 的方式时,把助理消息和工具结果按顺序原样带回,不要只挑内容。
- store 会把响应留在服务端,涉及敏感数据时先确认数据保留策略。
规划 + 执行:两段式流水线
最常见的省法不是把推理调弱,而是把它放到该在的位置:让推理模型只做一次规划,把执行交给便宜模型逐条完成。规划的输出要短——一份步骤清单,不是一份代码。
// 第一段:规划者。只要步骤,不要实现
const plan = await client.responses.create({
model: "gpt-5", // 推理模型
input: [
{
role: "developer",
content: [
{
type: "input_text",
text: "你是规划者。只输出 3 到 7 条可执行的步骤,每条一句话,不要写代码。",
},
],
},
{ role: "user", content: [{ type: "input_text", text: task }] },
],
});
// 第二段:执行者。一次只做一步,范围写死
const cheapModel = "gpt-4o-mini"; // 换成你账号里最便宜的够用模型
for (const step of steps) {
await client.responses.create({
model: cheapModel,
input: [
{
role: "developer",
content: [
{
type: "input_text",
text: "你是执行者。只完成当前这一步,不要扩大范围,不要重构其他文件。",
},
],
},
{ role: "user", content: [{ type: "input_text", text: step }] },
],
});
}- 规划者的输出要进日志:出问题时先看计划对不对,再看执行。
- 执行者失败时,把失败信息回喂给规划者重排,而不是让执行者硬试三次。
- 规划阶段把验收标准一起定下来,执行阶段只需要对照检查。
- 同一批输入跑“全用推理模型”和“规划 + 执行”两版,比较通过率和花费再决定。
常见问题
推理模型还需要写提示词吗?
需要,但方向不同:不是教它怎么想,而是把目标、边界、输入结构和验收标准说清楚。提示词越具体,它自己补假设的空间越小,输出越稳。
为什么加了“请一步步思考”反而变差?
推理模型内部已经在做多步推理,再要求它把过程写出来,等于重复一遍并挤占输出空间。有些任务上这会让答案变啰嗦,甚至偏离原来的判断。
什么时候该用 developer message,什么时候用 user message?
长期有效的规则、角色、输出约定放 developer message;本次任务的输入、资料、具体问题放 user message。两者分开,改规则时不会动到任务数据。
复用 reasoning items 会不会让模型偷懒?
不会。它复用上一次的推理上下文,避免在工具调用之间从头再想一遍,判断仍然基于最新返回的结果。真正要小心的是数据保留策略和上下文长度,不是推理质量。
官方文档与参考
本文依据上述官方文档整理;价格、额度与功能会随官方更新变化,第三方中转站的行为以其自身说明为准。