Day 53 / 共 68 天 · 阶段 10 可信可观测

防幻觉 Critic:给答案配一个审稿人

阶段 9 你学会了"事后评测"发现错误。但有一类错误——模型一本正经地编造——最好在输出给用户之前就拦住。今天进入阶段 10 可信可观测的第一讲:防幻觉 Critic。你会学会用一个专门的"审稿人"角色做双层校验、逐条核对证据 ID,把编造挡在门外。明天(Day 54)学更广的失败闸门与护栏。

📍 你在阶段 10(可信可观测 D53-56)的位置
D53 防幻觉 Critic D54 失败闸门&护栏 D55 可观测 D56 成本治理&缓存
💡 用一个类比兜住今天(今天全程沿用「新闻编辑部」的世界观) 把 Agent 当一家新闻编辑部。写答案的模型是记者——文笔好、下笔快,但偶尔会为了把稿子写圆而"脑补"事实。Critic=编辑部的事实核查员(fact-checker):稿子发出前,他逐句问"这句有出处吗?出处对得上吗?"。证据 ID=每句话后面标注的"资料来源编号",核查员照着编号翻原文核对;拦编造=查无实据的句子,一律打回重写或删掉,绝不见报。今天你给 Agent 请了一位铁面核查员。
L01

幻觉为什么是最危险的错

🤔 痛点模型答"不知道"其实不可怕——用户会换个问法或找人工。真正可怕的是它自信满满地编:语气笃定、格式漂亮、还煞有介事地给个"依据",用户根本看不出是假的。
💡 本质回忆 Day 10:大模型的本质是"预测下一个最像样的词",它天生倾向于把话说圆,哪怕手里没有事实。这就是幻觉(hallucination)。它危险,是因为错得毫无破绽——像一个从不说"我不知道"、总能编出个说法的记者,读者反而更容易信。
📝 举个例子:漂亮的假答案 用户问"你们退货运费谁承担?"知识库其实没写。
· 老实的答案:"抱歉这点资料里没有,帮您转人工。"(安全)
· 幻觉的答案:"根据我们的售后政策第 4 条,7 天内退货运费由平台承担。"——条理、政策编号、语气都很专业,但整段是编的。用户信了、退了货、运费没人报,投诉爆发。这就是为什么幻觉必须在输出前拦住。
👶 RAG(阶段 4)不是已经外挂知识了吗,怎么还幻觉?RAG 大大减少幻觉,但不能根除。原因:检索可能没召回相关资料、或召回了但模型"读着读着又自己发挥了"。所以 RAG 负责"给资料",Critic 负责"查它有没有照着资料说"——两道防线叠加,才靠谱。
L02

Critic 是什么:一个专职挑刺的角色

🤔 痛点让写答案的模型"自己检查自己有没有编"——它既当运动员又当裁判,往往查不出自己的问题(就像作者很难发现自己文章的错别字)。
💡 本质Critic(批评者/审查者) = 一个独立的角色,任务只有一个:拿到"答案 + 依据资料",逐条判断"这句话在资料里有支撑吗?"。它不负责写得好不好,只负责挑刺。像编辑部里那位专职核查员,不写稿,只查真假。分工独立,才查得出问题。
记者写稿 → 核查员审 → 定稿 记者(生成模型) 写出答案+标注出处 核查员(Critic) 逐句核对资料 ✅ 通过→发出 ❌ 打回重写
图注:Critic 是"生成"之后、"发给用户"之前的一道独立关卡。呼应 Day 47 的 supervisor+specialist+critic 分工——critic 就是这里的核查员。
L03

双层校验:先生成、后审查

🤔 痛点只让模型一步到位"又写又保证不编",负担太重、也不可靠。怎么把"写好"和"查真"这两件事分开、都做扎实?
💡 本质双层校验=两遍过:第一层生成(记者尽力写好),第二层审查(核查员专心挑刺)。两个角色、两次调用、各司其职。就像报社流程"记者写稿 → 编辑核查"是两个岗位、两道工序,而不是一个人边写边查。
# 双层校验:generate 写答案,critic 审查,不通过就打回
def generate(question, docs):
    # 第一层:记者写稿。要求每句话标注它依据的资料编号 [D1]/[D2]...
    prompt = f"根据资料回答问题,每句话末尾标注依据编号如[D1]。\n资料:{docs}\n问题:{question}"
    return client.chat(prompt)

def critic(answer, docs):
    # 第二层:核查员。只做一件事——判断答案里每句是否被资料支撑
    prompt = f"""你是严格的事实核查员。逐句检查【答案】是否被【资料】支撑。
只要有一句在资料里找不到依据,就判定 pass=false,并列出可疑句子。
只输出 JSON:{{"pass": true/false, "suspicious": ["...可疑句..."]}}
【资料】{docs}
【答案】{answer}"""
    return json.loads(client.chat(prompt, temperature=0))   # 核查要稳,temperature=0

def answer_safely(question, docs, max_retry=1):
    ans = generate(question, docs)
    check = critic(ans, docs)
    if check["pass"]:
        return ans                       # 审查通过,放行
    # 没通过:把可疑处反馈给记者重写一次(也可直接兜底,见 L05)
    return "抱歉,这个问题我无法从现有资料中确认,已为您转接人工。"
关键点:critic 用的是独立的一次调用、独立的 prompt,它看不到"记者的自我辩护",只对着资料冷冰冰核对。这种"分工独立"正是它比"自己查自己"强的原因。代价是多一次模型调用(更慢更贵)——所以通常只对高风险场景开启。
L04

证据 ID 核对:每句话都要有出处编号

🤔 痛点核查员说"这句没依据",但资料一大堆,它到底在拿哪段比对?如果没有明确的对应关系,核查本身也可能"凭感觉",又不可靠了。
💡 本质给每段资料编号(D1、D2……),要求记者每句话都标注它引用了哪条(证据 ID)。核查员就照编号翻原文,一句一句硬核对:标了 [D2] 的句子,D2 里到底有没有这个意思? 像论文的参考文献编号——每个论断都能顺着编号查到原文,赖不掉。
📝 举个例子:编号让编造无处遁形 资料:[D1] 退款 3-5 个工作日到账。 [D2] 满 99 元包邮。
记者答:"退款 3-5 个工作日到账[D1],退货运费由平台承担[D2]。"
核查员核对:第一句 [D1] 属实 ✅;第二句标了 [D2],但 D2 只讲包邮、根本没提退货运费——编号对不上,判定编造 ❌。有了证据 ID,"哪句编的、编在哪"一清二楚,比笼统说"感觉有点假"强太多。

👶 小白:要是记者干脆不标编号,或者乱标一个 [D1] 蒙混呢?

👨‍🏫 老师:这正是核查员要抓的。规则可以定死:没标编号的句子 = 无依据,直接判不通过;标了编号但原文核对不上 = 编造,判不通过。乱标反而更容易被逮——因为核查员会真的去翻那条编号的原文比对。所以"强制标注 + 逐条硬核对"两件套一起上,蒙混的空间就很小了。这套"证据可追溯"的思路,是可信 Agent 的核心,gov-agents 这类框架把它做得很系统。

L05

拦住编造之后:宁可不答,不可乱答

🤔 痛点核查员打回了,那然后呢?总不能让用户干等,或者反复重写陷入死循环吧?
💡 本质被拦下后有几条退路,核心原则是宁可诚实地"答不了",也不能自信地"乱答"——这跟新闻业"核不实就不发,绝不带病见报"一个道理。
处理方式怎么做适合
反馈重写把可疑句告诉记者,让它删/改后再审(限次数)大部分内容基本可靠,个别句超纲
只保留可信部分删掉没依据的句子,答能证实的那部分答案部分对、部分编
诚实兜底"这点资料没有,帮您转人工/建议核实"整段无依据,或重写仍不过
降低确定性措辞把"一定是"改成"资料未明确,建议确认"不确定但不宜完全拒答
务必给重写设次数上限(比如最多重写 1-2 次),呼应 Day 32 的"循环上限"。否则记者和核查员可能来回拉锯、卡死、烧钱。到达上限还不通过,就走"诚实兜底"这条最安全的路。
L06

想看工业级的 Critic 与证据核对?去深潜

🤔 痛点今天讲的是防幻觉的"思路骨架"。真实的可信框架里,Critic、证据 ID、双层校验是怎么和取证、审查、闸门系统地拼在一起的?
💡 本质gov-agents 是一套主打"可信/工程化"的多 Agent 框架,把"specialist 取证 + critic 审查 + 证据可追溯"做成了实打实的代码,正是今天思路的工业级实现。
🔗 想深入?去看《gov-agents 21 天精讲》→
建议重点看它的"Critic 审查 / 证据核对 / 防幻觉"相关章节,对照今天学的双层校验和证据 ID,看它多考虑了哪些真实场景(比如证据冲突、部分支撑怎么判)。看完回来继续 Day 54——把视野从"防编造"扩大到"整个系统怎么在出错时不崩、不闯祸"。
L07

今日小结 + 动手 10 分钟

🧠 今天你应该能回答

  • 为什么幻觉比"答不知道"更危险?RAG 为什么不能根除幻觉?
  • Critic 是什么角色?为什么"独立挑刺"比"自己查自己"强?
  • 双层校验是哪两层?各自负责什么?代价是什么?
  • 证据 ID 核对怎么让"编造无处遁形"?没标/乱标编号怎么判?
  • 被 Critic 拦下后有哪几条退路?核心原则是什么?为什么要设重写上限?

✋ 动手 10 分钟:手动当一回核查员

不用调 API,先练"核查思维"。给定这份资料和答案,逐句核对证据 ID,判断哪句该被拦:

{
  "资料": {
    "D1": "会员分为普通和黄金两档。",
    "D2": "黄金会员每月赠 3 张优惠券。"
  },
  "答案": [
    "会员分普通和黄金两档[D1]。",
    "黄金会员每月赠 3 张券[D2]。",
    "黄金会员还能免费升级为钻石会员[D2]。"
  ]
}

逐句判:第 1 句 [D1] → ?;第 2 句 [D2] → ?;第 3 句标了 [D2],但 D2 只讲优惠券、没提"钻石会员" → ?。(答案:前两句通过,第三句编造、判不通过。)体会"照编号翻原文硬核对"这个动作有多简单又多有效。
进阶:把 L03 的 generate/critic 接上你 Day 11 的真模型,跑一遍双层校验。

明日预告 · Day 54:Critic 防的是"内容编造"。但一个真实系统还会遇到更广的风险——工具调用失败、模型宕机、Agent 想执行危险操作(删库、大额转账)、token 烧超预算。明天学失败闸门 & 护栏:降级不崩、危险动作先审批、预算闸自动跳闸,让系统"出错但不闯祸"。
← Day 52 · 测试集&回归&CI Day 54 · 失败闸门&护栏 →