你让它做一个客户账户的季度活动总结。你给了登录日期和工单数。模型回了一篇齐整总结,里面有”Q3 的续约日期”、“该公司的主联系人”、“营收影响约 4.2 万美元”。这些你都没给。听起来都对,全是编的。模型把”写一份季度总结”读成了”产出一份看上去完整的季度总结”,而按训练分布的惯例,一份”完整”的总结就该有续约日期和联系人,于是它补上了。
最快修复:在 prompt 里加一条规则——If a detail is not in the input, output "UNKNOWN". Do not infer or estimate.——并把任何结构化输出换成每个字段都能取 null 的 schema。仅此一步就能止住大部分填空。本文剩下的部分,是给那些你没法用肉眼逐条核对的输出准备的验证层。
有一点贯穿全文:结构化输出模式只保证答案的”形状”,不保证它的”真假”。 截至 2026 年 6 月,OpenAI、Anthropic、Google 三家都会把你的 JSON Schema 编译成 token 语法,让模型在物理上无法吐出不符合形状的字段——但一个 schema 合法的 "renewal_date": "2026-09-01" 照样可以是编的。形状约束堵的是”格式错乱”那类失败,堵不住”凭空编造”那类失败。两道都得上。
你属于哪一类
| 症状 | 可能原因 | 跳到 |
|---|---|---|
| 编出来的细节很像真的(日期、姓名、整数金额) | 从先验里做常识填空 | Step 1、6 |
| Prompt 写了”完整""全面""comprehensive” | 这个形容词就是要”不留空” | Step 1 |
| 某个 JSON 必填字段永远有值、从不为 null | Schema 没有缺失数据路径 | Step 2 |
| 输出是表格或 bullet 列表、没有空格 | 格式把”完整”读成”不许有空” | Step 1、2 |
| 出现”大约""估计""通常”这类词 | 表示在猜的语言对冲 | Step 5 |
| 某条论断你回溯不到任何一行输入 | 缺核对步骤 | Step 6 |
常见原因
1. Prompt 要”完整""全面""comprehensive”
这些词的意思就是”不留空”。模型按字面执行,把空都填上。
如何判断:你的动词或形容词暗示了完整性。
2. Schema 有必填字段但无缺失数据路径
如果 schema 是 { "renewal_date": string }、没有 null 选项,模型只能往里塞字符串,真假无所谓。用严格结构化输出时这点更刺眼:截至 2026 年 6 月,OpenAI 的 strict 模式要求每个属性都出现在 required 里,所以一个没有可空路径的字段必然会被填满。
如何判断:schema 没有显式的 null / unknown 处理。
3. 没有”未知怎么办”的规则
你没告诉模型不知道时该怎么办。默认行为就是”产出一个听着合理的东西”。
如何判断:prompt 里没有 if unknown: 规则。
4. 常识填空
对非常常见的模式(员工名、日期、地址),模型会从训练时”这类记录该长什么样”的先验里填。这不是随机——是统计先验。
如何判断:编出来的细节合理且符合常见模式(续约填 “Q3”、联系人填 “张伟”、营收填一个整数)。
5. 输出格式鼓励完整
尤其是表格:空格看着不对,模型就填上。bullet 列表也一样——半截 bullet 读起来就是不完整。
如何判断:格式是表格、结构化列表或 schema 驱动的对象。
动手前先确认
- 标出输出里你从未提供过的细节。
- 把被编造的输出存一份做对比。
- 定政策:缺失数据应该是
"UNKNOWN"、null、空白,还是直接报错? - 检查 schema 里是否有必填字段没有缺失数据路径。
- 想清楚工作流到底需不需要这些数据,还是只标一个”缺失”就够了。
需要收集的信息
- 原始输入数据(你真给了什么)。
- 模型产出的输出。
- 具体被编造的细节,标注出来。
- 你的 schema(如果有)和必填字段。
- 模型和 temperature。
最短修复路径
Step 1:显式”未知规则”
Rules for missing data:
- If a required detail was not provided in the input, output "UNKNOWN".
- Do not infer. Do not estimate.
- Do not write "approximately", "around", or "roughly" unless
the input contains a number.
- If more than 3 fields would be UNKNOWN, stop and ask for the missing data.
对纯文本输出,光这一块就能解决大多数填空。把它放在 prompt 靠后的位置、紧跟在任务之后,让它是模型读到的最后一条指令。
Step 2:Schema 显式处理 null
Schema:
{
"renewal_date": "<ISO date OR null>",
"primary_contact": "<name OR null>",
"revenue_impact_usd": "<number OR null>",
"data_gaps": ["<field name that was null and why>"]
}
null 选项加上 data_gaps 数组,让”缺失”成为一类合法输出。如果你是在调带严格结构化输出的 API,就把同样的思路写进真正的 JSON Schema,让语法允许 null:
{
"type": "object",
"additionalProperties": false,
"required": ["renewal_date", "primary_contact", "revenue_impact_usd", "data_gaps"],
"properties": {
"renewal_date": { "type": ["string", "null"] },
"primary_contact": { "type": ["string", "null"] },
"revenue_impact_usd":{ "type": ["number", "null"] },
"data_gaps": { "type": "array", "items": { "type": "string" } }
}
}
截至 2026 年 6 月,["string", "null"] 这种类型数组是 OpenAI strict 模式和 Gemini response_json_schema 下让字段可空的标准写法;Anthropic 的结构化输出也吃同一份 JSON Schema。没有可空类型,strict 模式会强行往字段里塞一个非 null 值——而这恰恰就是你想干掉的编造。
Step 3:让模型先列假设再作答
Step 1: List every assumption you would need to make to produce
a complete answer. Number them.
Step 2: For each, mark whether the input data supports it (YES) or
whether you would be inventing it (NO).
Step 3: Produce the answer using only YES assumptions. For NO
assumptions, write UNKNOWN in the output.
这把隐式编造翻成了一个显式、可审计的步骤。在带推理模式的模型上(GPT-5.5 Thinking、Claude Opus 4.7、Gemini 3.1 Pro),模型内部本来就会做一部分这种事,但把它写出来,你就有了一份能逐条查的底稿。
Step 4:few-shot 示例里带上 UNKNOWN
让模型看见”我不知道”长什么样才算合格:
Example 1:
Input: Login dates only
Output:
{
"renewal_date": null,
"primary_contact": null,
"revenue_impact_usd": null,
"data_gaps": ["renewal_date (not in input)", "primary_contact (not in input)", "revenue_impact_usd (not in input)"]
}
Now produce for: <real input>
一个填满 null 的示范,比一整段说明都管用,因为它告诉模型:大量留空的输出是预期内的,不是失败。
Step 5:禁用填充词
Forbidden phrases (do not use unless the input contains evidence):
- "approximately", "around", "roughly", "likely", "estimated"
- "based on industry norms"
- "typical", "average", "standard"
这些短语是填空的语言指纹。禁掉它们,模型要么给出证据,要么说 UNKNOWN。它们也是你做审计时最好 grep 的目标(见”怎么确认”)。
Step 6:让模型拿输出核对输入
末尾追加:
After producing, list each non-UNKNOWN claim with the exact input line
that supports it. If you cannot point to a supporting input line,
the claim must become UNKNOWN.
核对步骤兜住规则漏掉的东西。对高吞吐流水线,把它拆成单独一次调用:prompt 1 负责产出,一个全新的 prompt 2 只拿到输入和草稿,被要求标出任何找不到出处行的论断。一次”没记着想显得完整”的二次核对,更难被糊弄过去。
怎么确认已经修好
- 抽查 5 个输出:每条具体论断在输入里都有支持原文行。
- 输入有缺时
data_gaps数组非空。 - 该出现
"UNKNOWN"(或null)的地方都出现了。 - 用 grep 在输出里搜那串禁用填充词——次数应该是 0。
- 同事审输出时,找不到任何一处”等等这是哪来的”的细节。
如果还是没修好
- 降 temperature。 严格事实性一般要 temperature 0 到 0.2;temperature 越高,模型越敢猜。
- 切到结构化输出模式(在 OpenAI、Anthropic 或 Gemini 上用带 strict / 语法约束的 JSON Schema)。记住它修的是形状、不是真假——要和 Step 2 的可空 schema 搭配用,不是用它替代。
- 把产出和核对拆成两次调用,让核对方不带”想显得完整”的私心。
- 高风险工作上检索(RAG),让模型有显式来源,并要求每条论断附引文。找不到可检索来源的论断,就改成 UNKNOWN。
常见问题
为什么模型偏要编那么具体的东西(一个日期、一个金额),而不是干脆留空? 因为留空在统计上是更罕见的续写结果。在训练数据里,“季度总结”几乎总带着续约日期,所以概率最高的下文就是一个像样的日期,而不是 “UNKNOWN” 这个词。你得把 UNKNOWN 做成被明确允许、且被示范过的选项(Step 1 和 Step 4),否则先验会赢。
打开 Structured Outputs / JSON mode 是不是就能止住编造? 不能,而且这恰恰是这里最常见的误判。截至 2026 年 6 月,OpenAI、Anthropic、Gemini 都用 token 语法强制形状——模型吐不出违反形状的字段。但一个 schema 合法的值照样可以是编的。结构化输出止的是格式错乱的 JSON;止住编造的值,靠的是可空类型加上那条 UNKNOWN 规则。
我的 JSON Schema 开了 strict,但模型一直往一个我想设成可选的字段里填值。
在 OpenAI strict 模式下,每个属性都必须列进 required,没有”干脆不写它”这个选项。修法不是删字段,而是把它的类型设成可空:"type": ["string", "null"]。这样 null 就是合法值,语法会允许模型选择不填。
缺失数据到底该用 null、"UNKNOWN"、空白,还是报错?
挑一种,处处统一。JSON 流水线用 null 加一个 data_gaps 数组,下游代码好按它分支。纯文本用字面的 "UNKNOWN",方便 grep。空白格是最差的选择——无论对模型还是对人工审核,它都读作”已完成”。
多一次核对调用就多一次请求,值吗? 凡是要喂给某个决策、某次 join 或某条落库记录的,值。核对那一遍通常只花产出那遍的一小部分成本(它只重读、不重新生成),却是抓编造里收益最高的一道检查。一次性的草稿可以略过它,靠 Step 1、2、5 兜着。
预防建议
- 每个 prompt 的默认规则:“不知道就输出 UNKNOWN。不要推断。”
- 在 schema 里标清必填 vs 选填;选填字段做成可空、而不是删掉。
- 在事实性要紧的 prompt 里全面禁用填充词。
- 每月审一次生产输出里”被编的具体”(grep 那串填充词,抽查 5 条记录)。
- 重复工作流把核对步骤作为单独一次调用嵌进流水线。
- 把”完整性”和”正确性”当两件事看——有时不完整的输出才是对的输出。
相关阅读
外部参考:
标签: #排查 #Prompt #Prompt 质量 #幻觉