你想要”温暖、口语化、有人情味”的输出,同时又要”按这些精确 key 的严格 JSON”。JSON 有固定结构,温暖只能寄生在 prose 里。两边都要,等于让模型同时满足两个相互矛盾的规范;你拿到什么,取决于这一次哪个约束赢:要么是平板无趣但通过 schema 的 JSON,要么是带类 JSON 标签的温暖 prose,把你的 parser 弄挂。模型不是找不到中间地带,而是”严格结构”和”无约束 prose”之间根本没有中间地带。
最快修复: 先确定谁第一个读这份输出。如果先经过 parser,就从 prompt 里删掉”温暖”,并在 API 层打开真正的 schema 强制(OpenAI 用 response_format: json_schema 加 strict: true,Claude 用 output_format/output_config.format 配 type: "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 里冒出凭空多出来的字段 | 模型在”改进”你的 schema | Step 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
}
category 和 priority 是没温度的严格 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。 - 适用的话,人读觉得能用。
如果还是没修好
- 两个约束可能根本不兼容——砍掉一个。
- 凡是两边都必须成立的,用两遍流水线(Step 5)。
- 还没切就切到 API 层结构化输出(Step 6);只靠 prompt 写”返回 JSON”在任何 temperature 下都不可靠。
- 降 temperature。即便不开 strict 模式,格式稳定性和字段名一致性也会随 temperature 降低而提升。
- 如果 strict 模式拒收你的 schema,检查是不是每个对象都设了
additionalProperties: false、每个属性都进了required(OpenAI strict 模式把可选字段当成与null的类型联合处理)。
预防建议
- 把风格和格式当成两件事,写 prompt 前先排序。
- 默认:机器消费者用干净 schema、不加风格;人读用 markdown。
- 多消费者流水线拆成两遍。
- “温暖 JSON” 只用在显式的 prose 字段,key 和 enum 一律不行。
- 只要你控制得了调用,就在 API 层强制 schema,而不是在 prose 里求 JSON。
- 审一遍生产 prompt 里的”风格 + 格式”冲突,多数靠删掉其中一个就能解决。
- 拿不准消费者时反问一句:“这份输出先给谁?“
相关阅读
标签: #排查 #Prompt #Prompt 质量 #风格漂移