风格要求与输出格式冲突:挑一个、限到 prose 字段、或拆两遍

要 AI 同时输出温暖口语和严格 JSON,是两个方向相反的要求。挑一个、把温暖限到 prose 字段、或拆成两遍,并在 API 层用 strict 真正强制 schema。

你想要”温暖、口语化、有人情味”的输出,同时又要”按这些精确 key 的严格 JSON”。JSON 有固定结构,温暖只能寄生在 prose 里。两边都要,等于让模型同时满足两个相互矛盾的规范;你拿到什么,取决于这一次哪个约束赢:要么是平板无趣但通过 schema 的 JSON,要么是带类 JSON 标签的温暖 prose,把你的 parser 弄挂。模型不是找不到中间地带,而是”严格结构”和”无约束 prose”之间根本没有中间地带。

最快修复: 先确定谁第一个读这份输出。如果先经过 parser,就从 prompt 里删掉”温暖”,并在 API 层打开真正的 schema 强制(OpenAI 用 response_format: json_schemastrict: true,Claude 用 output_format/output_config.formattype: "json_schema",Gemini 用 responseSchema)。如果先给人读,就放弃严格 JSON,改成一个 markdown 表加一段短评论。如果两边都得读,就拆两遍:先出 prose,再把这段 prose 抽进 schema。

常见原因

1. 固定 key 的 schema 里要温暖

JSON 的 key 没温度,enum 值也没温度。唯一能装温度的是 string 型的 prose 字段,而且还受长度和周围结构约束。

如何判断:同一条指令里既要 JSON 又要语气温暖。

2. 创意语气 + 机器可读目标

营销文案要创意语气,ETL 流水线要机器可读结构。一次输出同时服务两边,就是牺牲两边各自的强约束。

如何判断:输出既给人看又给脚本消费。

3. 冲突时没声明赢家

你不说”格式赢”或”风格赢”,模型就在两者之间取平均,结果两边消费者都失望。

如何判断:prompt 里风格和格式之间没有优先级。

4. 价值在 nuance 的任务却非要严格 JSON

让 JSON 把 sentiment 分成 positive/negative/neutral,就丢掉了”沮丧但理解”这种细微。瓶颈是 schema,不是模型。

如何判断:JSON 的 shape 表达不了你真正需要的东西。

5. 同一个 prompt 里有矛盾线索

同一个 prompt 既说”口语化”(温暖)又说”只返回合法 JSON”(结构),模型没法调和,于是每次随机挑一边——这也是输出在多次运行间反复横跳的原因。

如何判断:两个指令同时存在,彼此之间没有取舍。

你属于哪一类

你看到的症状最可能的原因跳到
JSON 能解析,但读起来冷、像机器人要了温暖,但 schema 没有放温暖的 prose 字段Step 2
parser 因为前缀 Here is the JSON: 或末尾评论报错API 层没强制,温暖泄漏到了大括号外Step 4、Step 6
每次运行形态都变(有时 JSON,有时 prose)线索矛盾,没声明赢家Step 1
JSON 合法,但丢了你需要的含义enum/schema 对这个 nuance 太粗Step 3(转人读)或放宽该字段
JSON 里冒出凭空多出来的字段模型在”改进”你的 schemaStep 4、Step 6

动手前先确认

  • 标清真实消费者:parser、人,还是两者都要。
  • 如果都要,定哪个是主要(输出错了哪个会崩)。
  • 标出你绝不能丢的那个值:温暖还是结构。
  • 决定一遍能不能两边都满足,还是需要流水线。
  • 做好必要时彻底放弃弱约束的准备。

需要收集的信息

  • 当前 prompt,且风格和格式两个要求都看得见。
  • 一份满足不了一边或两边的样例输出。
  • 下游消费者(具体的 parser、具体的读者)。
  • 哪个约束不可让步。
  • 用的模型,以及在跑的 system prompt。

最短修复路径

Step 1:定主要消费者

消费者 = parser(JSON 下游):
  格式赢。从 prompt 里彻底删掉"温暖"。

消费者 = 人读:
  风格赢。用 markdown(表 + 评论)代替严格 JSON。

消费者 = 两者(UI 显示 + 走分析管道):
  拆两遍。见 Step 5。

Step 2:schema 里有 prose 字段时,把温度限在那里

{
  "category": "billing",
  "priority": "high",
  "summary": "<prose, warm tone, max 50 words>",
  "escalation_needed": true
}

categorypriority 是没温度的严格 enum,escalation_needed 是 boolean,summary 是唯一允许温度的 prose 字段。显式标清哪些字段是 prose、哪些是机械字段,模型才不会把语气扩散到结构化字段里。

Step 3:纯人读时用混合格式

结构化部分用 markdown 表:
| 类别 | 优先级 | 状态 |
|---|---|---|
| 账单 | 高 | 需升级 |

表下面写评论(温度在这里):
"客户沮丧但理解——3 年老用户。
值得一通私人电话回访,而不是模板回复。"

两半模型都能自然产出,你既保住能扫读的结构,也保住人想要的语气。

Step 4:在 prompt 里禁止 schema 膨胀

JSON 约束:
- 不要加 schema 没列的字段。
- JSON 内不要写解释性注释。
- JSON 外不要包 prose 前缀("以下是 JSON:")。
- 只返回 JSON 对象,前后什么都不要。

模型爱给你的 schema “加点解释字段”,也爱在前面加一句友好的话——这两样都会弄挂严格 parser。在 prompt 里禁掉,再在 Step 6 真正强制执行。

Step 5:两边都要时用两遍工作流

Pass 1(内容):用 prose 产出温暖、细微的分析。
Pass 2(结构):把 Pass 1 的 prose 抽进这个 JSON schema。

Pass 1 抓 nuance,Pass 2 强结构,每一遍只干一件事。这比让一个 prompt 同时做两件事可靠得多,而且 Pass 2 可以换更便宜的模型、并开着结构化输出模式跑。

Step 6:在 API 层打开结构化输出模式

prompt 里的指令只是”礼貌请求”,而 API 层的强制能让无效结构在物理上不可能出现:constrained decoding 会把你的 JSON Schema 编译成语法,挡掉任何会违反它的 token。截至 2026 年 6 月,三大厂商都支持真正的 schema 强制,所以能用 API 时就别再在 prompt 里写”请只返回 JSON”:

  • OpenAI(GPT-5.5):把 response_format 设成 { "type": "json_schema", "json_schema": { "name": "...", "schema": {...}, "strict": true } }。开 strict: true 时,schema 里每个对象都必须设 "additionalProperties": false,并把每个属性都列进 required。旧的 {"type": "json_object"} JSON mode 只保证语法合法、不保证符合你的 schema,按 legacy 对待即可。记得检查响应里的 refusal 字段——模型拒答时内容会落在这里,而不是符合 schema 的结果。
  • Anthropic(Claude Opus 4.7 / Sonnet 4.6):Structured Outputs 于 2025 年 11 月进入 public beta。带上 beta header anthropic-beta: structured-outputs-2025-11-13,传 output_format(或 output_config.format),type 设为 "json_schema";每个对象都要有 "additionalProperties": false。Claude Code 只跑 Anthropic 模型,所以在那个生态里走的就是这条路。
  • Google(Gemini 3.1 Pro):把 responseMimeType 设为 "application/json",并在 responseSchema(OpenAPI 子集)或 responseJsonSchema(完整 JSON Schema)里传 schema。两者用其一,别同时用。

打开强制后,温暖就被安全地关在 prose 字段里,弄不挂 parser。如果你是从聊天界面而不是 API 调模型、拿不到 schema 控制权,就退回 Step 4 加两遍工作流。

怎么确认已经修好

  • 输出在下游一次过解析,不用手动清洗。
  • prose 字段(如果有)有你想要的温度。
  • 没有 schema 膨胀,没有凭空多出来的字段。
  • 同一个 prompt 跑 3 次,3 份输出形态一致。
  • OpenAI strict 模式下,正常运行时 refusal 字段为 null。
  • 适用的话,人读觉得能用。

如果还是没修好

  1. 两个约束可能根本不兼容——砍掉一个。
  2. 凡是两边都必须成立的,用两遍流水线(Step 5)。
  3. 还没切就切到 API 层结构化输出(Step 6);只靠 prompt 写”返回 JSON”在任何 temperature 下都不可靠。
  4. 降 temperature。即便不开 strict 模式,格式稳定性和字段名一致性也会随 temperature 降低而提升。
  5. 如果 strict 模式拒收你的 schema,检查是不是每个对象都设了 additionalProperties: false、每个属性都进了 required(OpenAI strict 模式把可选字段当成与 null 的类型联合处理)。

预防建议

  • 把风格和格式当成两件事,写 prompt 前先排序。
  • 默认:机器消费者用干净 schema、不加风格;人读用 markdown。
  • 多消费者流水线拆成两遍。
  • “温暖 JSON” 只用在显式的 prose 字段,key 和 enum 一律不行。
  • 只要你控制得了调用,就在 API 层强制 schema,而不是在 prose 里求 JSON。
  • 审一遍生产 prompt 里的”风格 + 格式”冲突,多数靠删掉其中一个就能解决。
  • 拿不准消费者时反问一句:“这份输出先给谁?“

相关阅读

标签: #排查 #Prompt #Prompt 质量 #风格漂移