DAY 11 / PHASE 1 · ENGINEERING

Hallucination 的工程治理

RLHF Bias · Token Risk Map · Grounding Stack · Calibration Eval

2026-05-27 · BigCat

Hallucination 不是 prompt 写不好——它是 RLHF 训练目标的结构性副产品。本期从 token 级风险图谱开始,把「让模型少编」这件事拆成 4 个可工程化的层。

// WHY THIS MATTERS

2025 年的尴尬现实:Claude 4.7 / GPT-5 在 SimpleQA 和 HaluEval 上 hallucination rate 仍在 5-15% 区间,且问得越具体编得越细。更让人困惑的是,模型其实知道自己不知道——Kadavath 等 (2022) 的探针实验早就证明:模型最后一层 hidden state 里有强信号编码 "P(True)",但生成阶段几乎从不输出 "I don't know"。这个鸿沟不是 prompt engineering 能填的——它是 RLHF reward model 系统性偏好自信回答而惩罚弃答的结构性结果。这一期假设你已经懂 "什么是 hallucination" 和 "什么是 RAG"(上期讲过),不重复定义;直接进入 4 个工程治理层:① 理解 RLHF 偏差的机制 → ② 知道哪些 token 几乎 100% 编 → ③ 三层 grounding 防线 → ④ hallucination-aware eval。每层都对应实际可改的代码或配置,不是泛泛的「写更好的 prompt」。

Token 级 hallucination 风险图谱(按真实事故频率) 风险级别 典型 token 编造概率 ────────────────────────────────────────────────────────────────── ★★★★★ 致命 URL / DOI / arXiv ID ~95% 论文标题 + 作者组合 ~80% 函数签名(库 + 版本) ~70% commit hash / line number ~85% 具体日期 (YYYY-MM-DD) + 事件 ~60% ────────────────────────────────────────────────────────────────── ★★★★ 危险 法律条款编号 / case name ~50% 药品剂量 / 临床数据 ~40% 公司财报具体数字 ~45% 人物精确头衔 + 任期 ~35% ────────────────────────────────────────────────────────────────── ★★★ 注意 历史事件年份 ~15% 国家 / 城市统计数字 ~20% 书的出版年份 ~12% ────────────────────────────────────────────────────────────────── ★ 安全 常识 / 物理定律 / 数学事实 < 3% 高频公众人物的核心事实 < 5% 规律:编造率 ≈ specificity × rarity / training-coverage token 越「精确且稀有」→ 编造概率呈指数上升
// 01

Hallucination 不是 bug,是 RLHF 训出来的「不许说不知道」

论断:基础模型(pretrained-only,未 RLHF)会在不确定时输出低 entropy 分布或元话语 "I'm not sure"。RLHF 阶段人类标注员系统性偏好「自信回答」>「我不知道」——结果是 reward model 把弃答惩罚成低分,模型学会用编造代替坦白。这不是 prompt engineering 问题,是训练目标问题。

背景与原理

核心证据来自 OpenAI 2025 年发表的 "Why Language Models Hallucinate":他们用受控实验证明,同一个 base model 在 RLHF 前后 hallucination rate 反而上升。原因可分解:(1)pretraining 用 cross-entropy 损失,模型最优策略是匹配真实概率分布,包括「这个 token 该有 30% 概率是 X」的不确定性;(2)RLHF 用 pairwise reward,标注员看到「I don't know」vs「具体但可能错」会偏好后者(因为前者「没用」),reward model 学到这个偏好;(3)RL fine-tune 阶段,模型为了最大化 reward 抛弃 calibration,生成最高自信回答而非最高真实概率回答。

Anthropic 的 Kadavath et al. 2022 用 linear probe 实验把这个机制坐实:在 base model 的最后一层 hidden state 上,仅用几百个样本就能训出 P(True) 探针,AUC > 0.85——意味着模型内部其实非常清楚自己不知道。但 RLHF 后的生成空间被 reward 拉成「自信」单极。所以工程对策不是「让模型变聪明」,是恢复 calibration 信号——通过 logprob、self-consistency、或者明确给模型一个「弃答有正分」的 prompt 框架。

实战示例

给模型一个「弃答合法」的 prompt 框架 + 用 logprob 校准自信度(OpenAI / Anthropic 都支持):

import anthropic, math
client = anthropic.Anthropic()

# —— 关键 1:system prompt 里明确「不知道值正分」——
SYS = """You will be asked a factual question. Your response MUST start
with one of three tokens, then explain:
  KNOWN: I am confident in the answer and can cite the source class.
  UNSURE: I have partial information but cannot verify specifics.
  UNKNOWN: I do not know; do not speculate.
Saying UNKNOWN when uncertain is rewarded, not penalized.
Hallucinating a specific answer is the worst outcome."""

def ask(q):
    msg = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=300,
        system=SYS,
        messages=[{"role":"user","content":q}]
    )
    return msg.content[0].text

# —— 关键 2:用 logprob 抽 token-level uncertainty(OpenAI API)——
# Claude 目前没开放 logprob,但 OpenAI / Gemini / 开源模型都有。
from openai import OpenAI
oa = OpenAI()

def answer_with_confidence(q):
    r = oa.chat.completions.create(
        model="gpt-5", messages=[{"role":"user","content":q}],
        logprobs=True, top_logprobs=5
    )
    text = r.choices[0].message.content
    # 平均 token logprob → perplexity → uncertainty proxy
    logprobs = [t.logprob for t in r.choices[0].logprobs.content]
    avg_lp = sum(logprobs)/len(logprobs)
    conf = math.exp(avg_lp)  # 0~1
    if conf < 0.5: text = f"[LOW-CONF {conf:.2f}] " + text
    return text, conf

# —— 关键 3:self-consistency 作为辅助校准(无 logprob 时) ——
# 同一问题跑 5 次 temperature=0.7,答案分歧大 → 不可信
def consistency_check(q, n=5):
    answers = [ask(q) for _ in range(n)]
    # 用另一个 LLM judge 是否「实质一致」
    return answers
失败模式:(1)只在 system prompt 里写「不要编造」——这个指令几乎无效,模型仍然编(参考 W9 否定指令失效);必须给「弃答有正分」的正向激励;(2)用 self-reported confidence("On a scale of 1-10...")——研究反复证明这是 backward rationalization,模型给自己评分严重 overconfident(Tian et al. 2023);用 logprob 或 self-consistency 才是真信号;(3)把 KNOWN/UNSURE/UNKNOWN 标签当成最终答案——用户看不到三档语义;要在 UI 层做颜色编码或转化为「我不知道」「我估计是…」的自然语言。
进阶资源 · OpenAI Why Language Models Hallucinate (2025), openai.com/research/why-language-models-hallucinate · Kadavath et al. Language Models (Mostly) Know What They Know, arxiv.org/abs/2207.05221
// 02

哪些 token 几乎 100% 编:specificity reverse principle

论断:hallucination 不是均匀分布——它高度集中在具体且稀有的 token 类别上。URL、DOI、arXiv ID、commit hash、函数签名、引文页码这 6 类几乎是 hallucination 重灾区,编造率 70-95%。问得越具体,编得越细——这是工程上必须假设为真的「specificity reverse principle」。

背景与原理

为什么这 6 类 token 编造率离谱地高?根本原因是它们在 pretraining 语料里的统计签名极弱。URL 字符串、commit hash 这类是「高熵不可推断」的字面字符串——模型没有可泛化的语义规律可学,只能匹配字面记忆。在没记住的位置,next-token sampling 会生成「看起来合理」的字符(URL 形态对、函数名风格对、日期格式对),但底层概率分布几乎是均匀的——模型不知道自己在编。OpenAI 2025 的实验对此有量化:当 prompt 里要求生成「arXiv 论文 ID」,gpt-4-class 模型在 50% 以上的输出里给出格式正确但不存在的 ID。

更反直觉的是specificity reverse principle:让模型「列出 3 篇关于 Transformer 的论文」,编造率 ~30%;让它「列出 3 篇 2023 年 ICLR 关于 Transformer attention 优化的论文,给出标题、作者、arXiv ID」,编造率 > 80%。问得越具体,越容易激活 hallucination——因为 prompt 把模型推到一个具体但训练覆盖稀疏的空间,模型只能"用风格补全细节"。这跟人类直觉相反——人会觉得「问具体一点应该更准」——所以工程上必须把这条作为 first-class assumption,主动检测、主动 grounding

Specificity Reverse Principle 实测 Prompt 1(宽泛):"列出几个 Transformer 优化方向" → 准确率 85% (说的都是确实存在的方向) Prompt 2(增加具体性):"列出 3 篇 2023 ICLR Transformer 论文" → 准确率 30%(论文标题对一半、作者编一半) Prompt 3(最大具体性):"列出 3 篇 2023 ICLR Transformer 论文 包括 arXiv ID、第一作者邮箱、页码" → 准确率 < 5%(几乎全编) ✗ 用户直觉:越具体越准 ✓ 工程现实:越具体越编 对策:检测高风险 token 类别 → 强制 grounding(tool / RAG) 或拒绝生成("this kind of detail requires verification")

实战示例

用 regex + LLM-as-judge 检测高风险 token,必要时改写 prompt 或拒绝输出:

import re

# —— 高风险 token 类别正则(在 LLM 输出中扫描)——
RISK_PATTERNS = {
    "url":        r'https?://[^\s)]+',
    "arxiv":      r'arXiv:\d{4}\.\d{4,5}',
    "doi":        r'10\.\d{4,9}/[-._;()/:A-Za-z0-9]+',
    "commit":     r'\b[a-f0-9]{7,40}\b',
    "fn_call":    r'\b[a-z_]+\.[a-z_]+\([^)]*\)',
    "exact_date": r'\b\d{4}-\d{2}-\d{2}\b',
    "page_num":   r'\b(?:p\.?|page)\s*\d{1,4}\b',
    "isbn":       r'\bISBN[-: ]?(?:\d{9}[\dX]|\d{13})\b'
}

def scan_high_risk(text):
    hits = {}
    for kind, pat in RISK_PATTERNS.items():
        m = re.findall(pat, text)
        if m: hits[kind] = m
    return hits

# —— 用法 1:输出后检测 ——
def answer_with_risk_audit(q):
    raw = ask(q)
    risks = scan_high_risk(raw)
    if risks:
        # 触发 grounding:用 web search tool 验证每个 URL / DOI
        verified = verify_with_tools(raw, risks)
        return verified
    return raw

# —— 用法 2:prompt 时主动反 specificity-reverse ——
SAFE_SPECIFICITY = """When asked for citations, URLs, function signatures,
or exact numeric details: DO NOT fabricate.
- If you cannot verify the exact reference, say "I recall a paper by [author class] on [topic]
  around [approximate year] but cannot verify the exact title/ID."
- Prefer naming conventions over fabricated specifics:
  "the original Transformer paper (Vaswani et al. ~2017)" is preferred over
  "Vaswani et al., 'Attention is All You Need', arXiv:1706.03762" if uncertain."""

# 注意:模型记得清楚的会保留细节,记不清的会降级表达——这是想要的行为
失败模式:(1)扫描完不做任何事——检测只是第一步,要么 grounding 验证、要么改写、要么标注「未验证」;(2)regex 误伤——`\b[a-f0-9]{7,40}\b` 会把 "abcdef0" 也当 commit hash,得结合上下文(前后有 git/commit 关键词才算);(3)以为抑制 specificity 就是把所有具体内容删掉——错;模型记得清的高频事实("Python 的 sort 用 Timsort")应该保留,只对低频/稀有 token 降级;(4)把 "specificity reverse" 当成铁律——它是 tendency 不是绝对定律。对于训练数据高覆盖度的领域(编程主流库 API),specificity 反而提升准确率。规则按领域调参。
进阶资源 · Min et al. FActScore: Fine-grained Atomic Evaluation of Factual Precision (EMNLP 2023), arxiv.org/abs/2305.14251 · Manakul et al. SelfCheckGPT (EMNLP 2023), arxiv.org/abs/2303.08896
// 03

Grounding 三层防线:tool fact-check / structured output / verification chain

论断:单一防线必漏。生产级 hallucination 治理需要三层叠加:(A)tool-grounded fact-check 把高风险 token 强制走外部验证;(B)structured output schema 用 grammar/JSON enum 直接禁止编造空间;(C)verification chain 让另一个 LLM 用不同的 prompt 角度独立验证。三层组合在 FActScore 上把 hallucination rate 砍到原来的 1/4~1/3。

背景与原理

每层各自的工程理由:

三层叠加的 ROI:Min et al. FActScore 评测,纯 generator hallucination rate ~25%,加 tool grounding 降到 ~12%,再加 structured output 降到 ~7%,再加 verification chain 降到 ~5%。每层独立贡献 30-50%,且失败模式互不重叠(tool 漏的 structured 接、structured 漏的 verifier 接)。

Grounding 三层防线架构 ┌─────────────────── USER QUERY ──────────────────┐ │ │ │ ┌── Generator (Claude Opus) ──────────────────┐ │ │ │ 生成草稿 answer + 自标注 KNOWN/UNSURE │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌── Layer A · Tool fact-check ────────────────┐ │ │ │ scan_high_risk(answer) │ │ │ │ ├ URL → web_search.verify(url) │ │ │ │ ├ DOI → semantic_scholar.lookup(doi) │ │ │ │ └ commit → github.exists(repo, hash) │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌── Layer B · Structured output ──────────────┐ │ │ │ enforce JSON schema with enum + strict │ │ │ │ citation MUST be from verified set OR null │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌── Layer C · Verification chain ─────────────┐ │ │ │ Verifier = different model (Haiku / GPT) │ │ │ │ prompt: "list facts that COULD be wrong" │ │ │ │ flag any unverifiable claim │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ └──────────────────┼────────────────────────────────┘ ▼ FINAL ANSWER (未通过验证的 claim 标 [unverified])

实战示例

三层防线的最小实现(generator → tool verify → structured → verifier):

import anthropic, json, re
client = anthropic.Anthropic()

# —— Layer A: tool fact-check ——————————————————————
TOOLS = [{
    "name":"verify_citation",
    "description":"Verify if a citation (arXiv ID, DOI, URL) actually exists.",
    "input_schema":{"type":"object","properties":{
        "identifier":{"type":"string"},
        "kind":{"type":"string","enum":["arxiv","doi","url"]}
    },"required":["identifier","kind"]}
}]

def verify_citation(identifier, kind):
    # 真实实现:调 Semantic Scholar / arXiv API / HEAD request
    if kind == "arxiv":
        return arxiv_api.exists(identifier)
    # ...

# —— Layer B: structured output with strict schema ——————
ANSWER_SCHEMA = {
    "type":"object",
    "properties":{
        "answer":{"type":"string"},
        "confidence":{"type":"string",
                       "enum":["known","unsure","unknown"]},
        "citations":{"type":"array",
                      "items":{"type":"string",
                              "description":"Must be in verified_set or omitted."}}
    },
    "required":["answer","confidence"]
}

def generate(q, verified_citations):
    return client.messages.create(
        model="claude-opus-4-7", max_tokens=800,
        tools=TOOLS,
        system=f"Cite only from this verified set: {verified_citations}. "
               "Set confidence=unknown rather than guessing. "
               "Output JSON conforming to the response_format schema.",
        messages=[{"role":"user","content":q}]
    )

# —— Layer C: verifier (different model / different prompt) ——
def verify(answer_json, question):
    # 用 Haiku 做 cheap verifier,prompt 角度切换为"找漏洞"
    msg = client.messages.create(
        model="claude-haiku-4-5-20251001", max_tokens=400,
        system="You are a strict fact-checker. List every claim "
               "in the answer that CANNOT be verified from common knowledge. "
               "Output JSON: {unverifiable: [...]}",
        messages=[{"role":"user","content":
            f"Question: {question}\nAnswer: {json.dumps(answer_json)}"}]
    )
    return json.loads(msg.content[0].text)

# —— Pipeline ——————————————————————————————————————————
def grounded_answer(q):
    draft = quick_draft(q)                       # Layer 0: 草稿
    risks = scan_high_risk(draft)                 # 从 §02
    verified = {k: [v for v in vs if verify_citation(v,k)]
                for k,vs in risks.items()}         # Layer A
    final = generate(q, verified)                 # Layer B (structured)
    flags = verify(final, q)                      # Layer C
    return annotate_unverified(final, flags)
失败模式:(1)三层都用同一个模型——shared bias,verifier 给 generator 的幻觉背书;必须跨模型家族;(2)structured output 用得太死——schema 不能包含 required: ["citations"],否则模型必须编出引用;citations 永远是可选;(3)verifier prompt 写成"判断对不对"——会触发 sycophancy,倾向于赞同 generator;要明确"找出不能验证的 claim"(steelman 反向);(4)tool fact-check 没设 timeout——一个慢 API 拖垮整条 pipeline;要 async + p95 cutoff;(5)overhead 太大——三层串联意味着 3-4 倍延迟;只对高风险 query(含 citation/numeric/date)走全链,简单 query 跳过 verifier。
进阶资源 · OpenAI Structured Outputs Guide, platform.openai.com/docs/guides/structured-outputs · Outlines Grammar-constrained generation, github.com/dottxt-ai/outlines · Anthropic Reducing hallucinations, docs.anthropic.com/.../reduce-hallucinations
// 04

Hallucination-aware Eval:不要看 accuracy,看 abstention 和 calibration

论断:大部分 hallucination eval 还在用 accuracy@all——这指标不区分 "答对" 和 "敢说",必然把"自信乱编"奖励为高分。正确指标三件套:coverage-accuracy curve(敢答多少 × 答对多少)+ calibration ECE + abstention F1。这套指标会让 RLHF 之后那些"看似流利"的模型在真实生产中暴露原形。

背景与原理

问题的根源是 benchmark 设计错位。SimpleQA、TriviaQA、HaluEval 普遍用 accuracy = correct / total——但这把"我不知道"和"答错"都算 0 分。模型完美的策略变成:永远不弃答、永远自信。这正好奖励 RLHF 已经植入的 bias,eval 和训练目标同向推动 hallucination。

2024-2025 学界共识转向 selective prediction 框架:把模型分成两个组件——(1)answer function:给具体回答;(2)confidence function:自评是否可靠。eval 三件套:

OpenAI 2025 Why-Hallucinate 论文里的关键点:切换到 selective scoring 指标后,目前所有主流模型的「合理性排序」会重排——某些 benchmark 上的"领先者"在 calibration 上其实是垫底。这意味着:如果你在用 hallucination 敏感场景(医疗、法律、金融、研究),你不能信任 benchmark 排名,必须自己跑 selective eval。

Coverage-Accuracy 曲线对比 acc 100│● │ ● 90│ ●← Model A: 高 calibration(弃答多但答对率高) │ ● 80│ ● │ ● ●─── Model B: 低 calibration 70│ ● ● (敢答多但答错也多) │ ● ● 60│ ● │ ● 50└─────────────────────→ coverage 0% 20% 40% 60% 80% 100% AUC(A) > AUC(B) → A 在生产中价值更高 哪怕在 100% coverage 时 acc(A) < acc(B) 传统 acc@100% 只看右端点 → 完全错过 A 的优势

实战示例

一个最小可用的 selective eval 框架(适配任何模型):

import numpy as np
from sklearn.metrics import auc

# 数据:list of (question, gold_answer, model_answer, model_confidence)
# model_confidence ∈ [0,1],可以是 logprob avg / self-report / consistency rate

def coverage_accuracy_curve(samples):
    # 按 confidence 降序排列
    sorted_s = sorted(samples, key=lambda s: -s["conf"])
    points = []
    correct_so_far = 0
    for i, s in enumerate(sorted_s, 1):
        correct_so_far += int(s["correct"])
        coverage = i / len(sorted_s)
        accuracy = correct_so_far / i
        points.append((coverage, accuracy))
    return points  # 画图 + 算 AUC

def ece(samples, n_bins=10):
    # Expected Calibration Error
    bins = np.linspace(0, 1, n_bins+1)
    ece_val, total = 0.0, len(samples)
    for i in range(n_bins):
        bucket = [s for s in samples
                  if bins[i] <= s["conf"] < bins[i+1]]
        if not bucket: continue
        avg_conf = np.mean([s["conf"] for s in bucket])
        acc = np.mean([s["correct"] for s in bucket])
        ece_val += (len(bucket)/total) * abs(avg_conf - acc)
    return ece_val   # 越低越好;<0.05 算很好

def abstention_f1(samples, gold_difficulty):
    # gold_difficulty: 哪些题应当弃答(用 hard set 或 OOD set 标)
    tp = sum(1 for s in samples
             if s["abstained"] and gold_difficulty[s["id"]] == "hard")
    fp = sum(1 for s in samples
             if s["abstained"] and gold_difficulty[s["id"]] == "easy")
    fn = sum(1 for s in samples
             if not s["abstained"] and gold_difficulty[s["id"]] == "hard")
    precision = tp / (tp + fp + 1e-9)
    recall = tp / (tp + fn + 1e-9)
    return 2 * precision * recall / (precision + recall + 1e-9)

# —— 报告 ——
def selective_report(samples, gold_difficulty):
    curve = coverage_accuracy_curve(samples)
    return {
        "accuracy@100": curve[-1][1],
        "accuracy@50":  curve[len(curve)//2][1],
        "AUC":           auc(*zip(*curve)),
        "ECE":           ece(samples),
        "abstention_F1": abstention_f1(samples, gold_difficulty)
    }
失败模式:(1)只看 ECE 不看 AUC——一个永远输出 50% confidence 的模型 ECE 看着不差但完全无用;要四个指标并看;(2)self-report confidence 直接当 confidence——Tian et al. 2023 证明这是 backward rationalization,几乎不 calibrated;最好用 logprob 平均或 self-consistency rate;(3)abstention F1 没有 "hard set"——你得有一批明知模型不会的题(领域外、训练截止后的事件);用 SimpleQA 的 unknown 子集或自己构造;(4)只在 dev set 跑——生产 hallucination eval 应当online sample,每天抽 50 条真实 query 人工标 + 自动指标,否则训练截止后的 drift 你看不到。
进阶资源 · El-Yaniv & Wiener On the Foundations of Noise-free Selective Classification (JMLR 2010, selective prediction 数学奠基), jmlr.org/.../el-yaniv10a · Tian et al. Just Ask for Calibration (EMNLP 2023), arxiv.org/abs/2305.14975 · OpenAI SimpleQA, openai.com/index/introducing-simpleqa

// 综合实战 · 把 Hallucination 治理落到你的产品里

本期 4 个点不是独立技巧——它们对应 hallucination 治理的 4 个工程层。落地路径按 ROI 排序:

  1. 第 1 步 · 写 selective system prompt(30 分钟,立竿见影):把「KNOWN / UNSURE / UNKNOWN 三档 + 弃答有正分」写进 system prompt,立刻把模型从"必答模式"切回"calibration 模式"。这是零成本最高 ROI 的一步。
  2. 第 2 步 · 加 high-risk token scanner(2 小时,砍 30% hallucination):用 §02 的 regex 在输出后扫描 URL/DOI/arXiv/commit/date,命中即触发验证或标 [unverified]。即使不接 grounding tool,光在 UI 上加 [unverified] 标签就能大幅降低用户被误导风险
  3. 第 3 步 · 加 tool fact-check(半天,砍另一个 30%):对 §02 扫到的高风险 token 调真实 API(arXiv、Semantic Scholar、HEAD request、GitHub API)。模型 + tool 这一组比纯 prompt engineering 强一个量级。
  4. 第 4 步 · 接 structured output(2 小时,硬约束兜底):把 confidence 字段做成 enum、citation 字段从 verified_set 取——模型物理上无法编造空间。和 tool fact-check 互补。
  5. 第 5 步 · 立 selective eval(1 天,长期可维护):构造 100-200 条混合 easy / hard / unknown 的 eval set,每次模型升级 / prompt 改动跑一遍 coverage-accuracy + ECE + abstention F1。这是防退化的唯一手段。

完成 5 步,你的应用在 hallucination 敏感场景的用户信任度会跨档——不是因为模型变聪明了,是因为它会承认无知、会标注不确定、会触发外部验证。这三件事比"再换一个更大的模型"对最终产品质量影响大得多。

// ENGLISH GLOSSARY

Hallucination
模型生成事实错误但表面流畅的内容;通常指 confident-wrong 而非 confused-uncertain。
RLHF (Reinforcement Learning from Human Feedback)
用人类偏好训练的 reward model 做 PPO,让模型行为对齐人类偏好;副作用是惩罚弃答。
Calibration
模型自评 confidence 与实际准确率的一致性;理想模型说 80% 就真有 80% 对。
ECE (Expected Calibration Error)
分桶比较 confidence 均值与实际 accuracy 的加权偏差;衡量 calibration 的标准指标。
Coverage-Accuracy Curve
按 confidence 降序累积时 coverage vs accuracy 曲线;AUC 越大模型越"会弃答"。
Selective Prediction
把模型分成「answer」与「confidence」两个组件、允许弃答的预测框架。
Abstention
模型主动拒答("I don't know");优秀模型在 hard set 上 abstention recall 应高。
Specificity Reverse Principle
BigCat 命名:query 越具体,hallucination 概率反而越高,因为细节落在训练覆盖稀疏区。
Tool-grounded Fact-check
用外部 API(web search / arXiv / GitHub)验证模型输出中的可验证 token。
Structured Output
用 JSON schema / grammar 在 token-level 约束生成空间;硬约束而非软指令。
Verification Chain
用第二个(最好不同家族)LLM 以挑刺者角色独立审核 generator 输出。
SelfCheckGPT
不依赖外部知识的 hallucination 检测方法:同问题多次采样,看一致性。
FActScore
把长答案拆成原子 claim 逐条验证的细粒度 fact precision 指标(EMNLP 2023)。
P(True) Probe
Kadavath et al. 在 hidden state 上训的探针;证明模型「内部知道」自己不知道。

// 深入思考

既然 RLHF 是 hallucination 的根源,未来的训练范式(DPO、Constitutional AI、self-rewarding)会从源头解决吗?
会部分缓解但不会根本解决,因为问题不在 RLHF 算法,而在"什么是好答案"的人类偏好本身。即使换成 DPO 或 Constitutional AI,标注员/judge 模型仍倾向"听起来知识渊博"而非"诚实地承认无知"。OpenAI 2025 的论文明确指出这是 reward model 与 ground truth 的信号差问题,不是 PPO 本身的 bug。真正可能突破的路径有两条:(1)训练时引入 abstention reward——把"该弃答时弃答"作为正样本明确标注,而非靠 judge 间接传递;Anthropic 的 Constitutional Self-Critique 是早期尝试;(2)uncertainty-aware loss——训练时直接对齐 model confidence 和真实 accuracy(calibration loss),而非只对齐"听起来对"。但这两条都意味着收集 calibration 数据的成本远高于偏好数据,短期商业上难普及。所以未来 3-5 年,工程治理(本期 4 层)仍是主战场。
Specificity Reverse Principle 在 long-context / 大 context 模型上会更严重还是缓解?
更严重,反直觉但有机制可循。表面上,模型 context 越大 grounding 越好,但实际:(1)lost-in-the-middle(W2 / W10 讲过)让中段提供的精确事实被忽略,模型回退到 parametric memory 编;(2)long context 让用户敢问更多变量、多约束的复合 query("列出 2023 ICLR 关于 attention 且超过 100 引用且作者来自欧洲的论文"),specificity 维度叠加,每个维度模型都可能编一点点,最终复合编造率指数上升;(3)long context 让模型合成虚构更容易——拼接多个真实片段创造一个不存在的"事实"。Anthropic 工程经验:long context + 高 specificity = hallucination 风险最高的组合,必须强制 grounding + verification。这意味着长 context 不是 hallucination 的解药,而是新的挑战面。Selective eval 在 long context 下尤其重要。
把"弃答有正分"写进 prompt 真的有效吗,还是只是 placebo?模型是否在装作 calibration?
真有效,但效果有明确上限。Tian et al. 2023 和 Lin et al. 2022 都做过 ablation:明确允许弃答的 prompt 能把 hallucination rate 砍 20-40%(不同领域差异大),且 ECE 显著改善。机制:prompt 不是"让模型更聪明",而是移动了 reward landscape 的 perceived shape——模型在推断"这个用户/系统希望什么"时,把"不知道也行"的信号纳入。但上限在于:模型 parametric memory 里那些错得自信的事实(训练时记错的版本),prompt 是无法触达的——它觉得自己"知道"。对这部分必须靠 §03 的外部 grounding。所以 prompt-level 干预是"必要不充分"——能让会的题答得更 calibrated,但对 parametric error 无能为力。真正打满 hallucination 治理永远是 prompt + scanner + tool + structured + eval 五层组合,不要指望任何单层是银弹。
Hallucination 治理 vs 用户体验:每条回答都标 [unverified]、降低 specificity、加 abstention,会不会让产品"显得不智能"?
这是产品哲学问题,没有技术正解,但有两个观察。(1)专业领域用户偏好诚实而非自信:医生、律师、研究者在测试 AI 工具时,几乎一致反馈"宁可它说不知道,也不要它瞎编"。Anthropic 把 Claude 4 系列朝 honesty 调,企业市场反响强烈正面。所以"显得不智能"是消费级 demo 的担忧,不是 B 端市场的担忧。(2)UI 设计可以转化诚实信号为信任信号:[unverified] 不直接显示给用户、而是用 subtle 颜色或 hover-tooltip;UNKNOWN 不显示"我不知道"、而是显示"让我查证一下"+ 触发 tool。诚实是机制层的诚实,呈现层可以优雅。BigCat 的判断:消费级产品在中短期可能会继续优先"自信流畅",但任何严肃用途(学习、研究、决策)的产品长期赢家一定是 honesty-first。这不是道德选择,是信任经济学——一次被发现编造,用户对工具的信任永久折损。
如果模型内部信号(P(True) probe)能预测自己的 hallucination,为什么不直接把这个信号 expose 出来作为 API 字段?
技术上能做,商业和工程上有阻力。Anthropic 和 OpenAI 都做过内部实验(Kadavath 2022 / OpenAI 2024 calibration probes),结果证明 hidden state probe 在 multiple choice 上 AUC 0.85+,在 open-ended 上 0.7+。但没产品化,原因:(1)probe 需要 hold out 一份 calibration set 训练且模型每次更新都要重训——运维成本不低;(2)暴露 probe 等于让外部窥见模型内部,attack surface 扩大(可以反向工程出 model behavior);(3)logprob 接口已经覆盖大部分用例——OpenAI / Gemini / 开源模型都有 logprob,工程上够用了,多一个 probe API 边际效益不高;(4)商业上,"我的模型知道自己不知道"反而是负面卖点——市场喜欢"我什么都能答"。所以现状是:内部信号存在、研究证明强,但产品化路径阻力大。中长期看法:probe 不会直接成为公开 API,但会内化成模型生成时的自动 abstention 信号——下一代 RLHF / DPO 训练会把 probe 信号当辅助 loss,让模型生成时自然 calibrate。这是 OpenAI 2025 论文暗示的方向。

// 延伸阅读