先给结论:新建 Responses API 应用时,如果规则由应用在每次请求中集中注入,优先使用顶层instructions;如果规则需要作为显式消息进入对话序列、便于保存和重放,使用developerItem。system主要是迁移既有 transcript 时的兼容问题,不应再被当成新应用的默认入口。
推荐顺序可以概括为:按请求集中注入规则,用instructions;把规则作为消息序列的一部分保存或重放,用developer;遇到历史system记录,在请求边界做转换,或仅在目标模型和链路已经验证兼容时保留为 Item。
三者并不是三个同级选项
| 写法 | 位于哪里 | 当前公开资料中的主要用途 | 最容易踩的坑 |
|---|---|---|---|
system | 历史消息角色,具体语义取决于模型和协议 | 迁移既有 transcript,或用于已经验证支持它的链路 | 把某个网关或格式的行为当成 Responses API 的统一规则 |
developer | input中的消息 Item | 应用开发者提供的规则和业务逻辑,优先于用户输入 | 客户端界面写着“系统提示词”,实际却未序列化成developer |
instructions | Responses 请求顶层 | 为当前响应设置语气、目标、约束和示例 | 误以为它会随previous_response_id自动延续 |
OpenAI 当前迁移指南把 system 或 developer guidance 映射为顶层instructions,也允许在需要保留既有 transcript 时使用消息 Items。这里的兼容性仍以目标模型、官方 API 或接入链路的实际支持为准。文本生成指南则把instructions示例描述为与一条developer消息大致等价,并明确developer指令优先于user消息。
这里的“大致等价”不能理解成字段完全相同。它只说明两种写法都能向模型提供高层指令;它们在请求结构、状态管理和兼容链路中的行为仍要分别检查。
developer 和 instructions 怎么选
如果应用直接控制 Responses 请求体,先看规则要不要作为 Item 管理。
需要把规则放进输入 Item
使用developer比较直观:
{"model":"<已验证的模型ID>","input":[{"role":"developer","content":"回答前先核对用户提供的字段,不要补造缺失值。"},{"role":"user","content":"帮我检查这份请求。"}]}这种结构便于查看消息顺序,也适合应用自行保存和重放输入 Items。OpenAI 当前指南明确说明,developer指令的优先级高于user消息。
只想给当前请求设置高层指令
使用顶层instructions更简洁:
{"model":"<已验证的模型ID>","instructions":"回答前先核对用户提供的字段,不要补造缺失值。","input":"帮我检查这份请求。"}OpenAI 当前指南说明,instructions会优先于input参数中的提示。不过它只作用于当前这次响应生成。使用previous_response_id续接下一轮时,上一轮的顶层instructions不会自动出现在新一轮上下文中;需要持续生效的规则应再次提供。
system 还要不要用,不能只看字段名称
很多迁移问题来自“同名不同层”。旧应用可能把业务规则叫作 system prompt;客户端配置项也可能沿用“系统提示词”这个名称;真正发出的请求却可能是system消息、developer消息或顶层instructions。
因此,看到System messages are not allowed时,只能确认当前链路拒绝了这次请求中的某种结构。它不能单独证明:
- Responses API 普遍禁止
system; - 错误一定来自模型,而不是 SDK、客户端或兼容网关;
- 把字段名改成
developer就已经解决; - 其他模型和其他接入入口也遵循同一规则。
不要拿底层格式说明替代目标 API 的请求文档;迁移依据应是目标端点的当前规范、模型支持范围和最终出站请求。
为什么配置改对了,端到端仍可能失败
一条实际调用链通常不止一层:
应用配置 -> 客户端或 SDK 序列化 -> 适配器转换 -> 兼容网关校验或再次转换 -> 目标模型端点界面中的配置项只控制第一层或第二层。后面的适配器可能改写角色,网关也可能只兼容 Responses 的部分字段。判断是否修好,需要看最终出站结构和端到端结果,不能只看“配置已保存”。
一套不容易误判的迁移验证法
1. 固定模型快照和其他变量
生产应用应尽量固定模型快照,并建立 eval。测试时同时固定客户端与 SDK 版本、完整接口入口和同一句用户输入,一次只改变指令承载方式,避免把模型版本变化误判为字段差异。
2. 建立两份最小请求
在官方原生入口或已确认兼容的测试入口,分别发送:
- 一条
developer消息加一条user消息; - 顶层
instructions加普通input。
目标不是评选“更高级”的写法,而是确认目标链路对两种结构的实际支持。
3. 检查最终出站请求
如果应用使用第三方客户端或兼容网关,应在受控环境检查序列化后的脱敏结构:API 路径、模型 ID、字段位置和角色是否与预期一致。看不到最终请求时,只能把角色转换列为待验证方向。
4. 验证指令效果,而不只看 HTTP 状态
使用一个可以客观检查的规则,例如“缺失字段必须明确指出,不得猜测”。至少验证:
- 首轮响应是否遵守规则;
- 使用
previous_response_id后,重新提供与不重新提供instructions的结果是否符合预期; - 重启客户端或网关后,请求结构和行为是否一致;
- 同时提供两条相互冲突的高层指令时,eval 是否能暴露不稳定行为;
- 官方原生端点与兼容网关在相同请求下的结构、错误和指令效果是否一致;
- 不支持的写法是否由预期层级返回明确错误。
模型输出存在非确定性,验收不能依赖一句固定文案。eval 应检查规则是否执行、请求结构是否正确,以及错误是否来自预期层级,并覆盖首轮、previous_response_id多轮、客户端或网关重启、两条高层指令冲突、原生端点与兼容网关五类场景。
按这五个问题选择承载方式
| 决策问题 | 更适合instructions | 更适合developer | 历史system怎么办 |
|---|---|---|---|
| 是否需要 transcript 审计 | 规则可在请求日志中单独审计 | 规则需和消息序列一起保存、重放 | 保留原始记录,在请求边界明确转换 |
| 是否由应用集中注入 | 适合,每次请求显式提供 | 可以,但要构造消息 Item | 不建议作为新应用默认写法 |
| 是否要求跨轮持续生效 | 每轮重新提供;不会随previous_response_id自动继承 | 由应用保存并在后续输入中重放 | 不能假定兼容层会自动保留 |
| 是否使用 prompt 缓存或版本发布 | 对规则文本单独版本化,并按目标平台的缓存机制验证 | 可随 transcript 或提示模板版本化 | 先转换为明确、稳定的目标结构再验证缓存 |
| 兼容层是否完整支持 | 核对顶层字段是否被保留 | 核对角色是否被改写 | 只有经过端到端验证才保留,否则在边界转换 |
如果团队维护的是既有对话记录,还要考虑历史数据怎样映射成 Responses Items。保留原始 transcript、在请求边界做明确转换,通常比直接批量改写历史字段更容易审计。生产迁移未通过 eval 时,可以暂时回滚到已验证的接口格式,但这不等于完成了 Responses 兼容改造。
只有客户端权限时,该提供什么
普通使用者通常看不到网关转换后的请求。提交技术支持时,公开信息与私密协查材料要分开:
| 信息 | 公开讨论可提供 | 仅限受控私密渠道 |
|---|---|---|
| 环境 | 客户端、SDK 版本和操作系统 | 必要的脱敏配置片段 |
| 接口 | API 类型、脱敏路径结构和模型 ID | 实际完整 Base URL;API Key 不提交 |
| 请求 | 指令使用developer还是instructions | 脱敏后的最终结构(若可取得) |
| 错误 | 时间与时区、HTTP 状态和脱敏错误 | 平台关联标识或 trace ID(若有) |
| 复测 | 首轮、多轮、重启后的结果 | 接入方内部日志对照 |
不要公开 API Key、完整请求体、真实业务提示词、内部地址或真实关联标识。接入方能看到哪一层日志,取决于实际链路和日志保留策略,不能预先承诺。
发布或上线前检查清单
- 已按目标 API 的当前文档确认可用字段,而不是沿用旧接口记忆
- 已知道客户端中的“系统提示词”最终被序列化成什么
- 已固定模型快照、入口和版本,对比
developer与instructions - 已验证
instructions在previous_response_id链路中的生命周期 - 已完成重启后的端到端复测,不只检查配置文件
- 已用 eval 覆盖两条高层指令冲突的情况
- 已把原生 API 行为与兼容网关行为分开记录
- 对外材料已删除凭证、业务提示词、内部地址和真实关联标识
FAQ
instructions是第三种消息角色吗?
不是。它是 Responses 请求的顶层参数。OpenAI 当前指南把它描述为向模型提供高层指令,并给出了与developer消息大致等价的示例。
developer就是把旧 system prompt 改个名字吗?
不能这样机械理解。它适合承载应用规则,但旧系统中的system可能还包含平台元信息、历史协议约定或客户端专用语义。迁移时要先分类,再决定映射方式。
收到System messages are not allowed,直接改成developer可以吗?
可以作为单变量对照,但不能跳过复测。先确认错误由哪一层返回,再检查客户端是否真的发出了developerItem,并完成首轮和多轮验收。
instructions和developer能同时使用吗?
请求结构可以同时携带顶层instructions和developerItem,但不要让两者承担重叠或相互冲突的规则。OpenAI 当前公开指南没有给出一条适用于所有模型和版本的通用冲突排序;即使某次测试观察到了固定结果,也不能据此推断其他模型快照或兼容网关相同。确需同时使用时,应明确职责边界、固定模型快照,并把冲突用例纳入 eval。
参考资料
- OpenAI 文本生成指南:Message roles and instruction following
- OpenAI 从 Chat Completions 迁移到 Responses 指南:Map messages to Items
- OpenAI Responses create API 参考
以上资料查阅于 2026-08-03。接口和模型行为可能更新,生产环境应固定模型快照,并以当前官方文档和本地 eval 结果为准。
迁移的难点不在三个名词本身,而在客户端、协议和兼容层是否把同一条业务规则传成了预期结构。把最终请求、多轮生命周期和端到端兼容性查清楚,才能决定使用instructions、developer,还是先在边界转换历史system。