- 适读人群:工程师、研究者、产品经理,以及正在与模型"分手又复合"的你
- 文风提示:专业 + 底层原理 + 一点幽默 + 可落地方案
- 语言与工具:示例代码为 JavaScript
- 温馨说明:本文避免使用传统数学公式记法,遇到公式概念将改用文字和类比解释
1. 什么是"幻觉"?
"幻觉"(Hallucination)指的是生成模型在缺乏足够依据时,生成看似合理但客观不正确或捏造的内容。典型表现:
- 编造不存在的论文、API、函数、条款
- 错配事实:把 A 公司的产品特性说成 B 公司的
- 逻辑跳跃:前提和结论彼此不认识,但硬拉关系
一句话:"语言像人,但不保证像真。"
小图标氛围组:✨🧠📚🦄
2. 技术成因:从底层原理出发
从"语言建模"的基本机制说起:
生成式模型的核心是"预测下一个词的分布",本质是高维概率场上的采样过程。它擅长"统计上的相似",而非"事实上的正确"。
2.1 训练分布与真实世界分布的错位
- 训练数据是"过去的文本合集",真实世界是"实时变化的事实集合"。
- 当问题脱离训练分布(例如非常新的知识、冷门领域、或结构前所未有的任务),模型利用"相似性补全"来强行解释,结果就是一本正经的"合理化错误"。
类比:你问一个读遍古籍的文人"USB-C 2.1的最大功率是多少",他会优雅地胡诌,因为书里没写过,但他要凑一段像样的答复。
2.2 概率采样与"自信误差"
- 输出是从概率分布中采样而来。
- 在不确定场景中,模型仍会给出高置信度的文本,因为"连贯性"与"真实性"在它眼中并无天然约束。
提示:温度越高、Top-p越宽,探索度越大,幻觉概率上升;温度极低虽减少幻觉,但也会增加"模式坍缩",出现机械复读。
2.3 表征与检索的断层
- 传统语言模型将知识"压缩进参数",像一本烧录在芯片里的百科。
- 这种"参数化知识库"难以更新,也缺乏对出处的引用能力。
- 当被问到长尾事实,模型会在其表示空间里找最近邻"语言片段",拼接成看似合理的答案,却往往离事实差一截。
2.4 训练目标的偏差
- 训练目标通常是"最大化训练文本的似然",不直接优化"真实性"。
- 为提升"对话体验",微调可能会偏向"礼貌、详尽、肯定",这进一步鼓励模型在不确定时"稳稳输出",而不是"承认我不知道"。
2.5 指令歧义与多步推理脆弱性
- 用户指令含糊或多解时,模型可能自定补充设定,产生"虚构上下文"。
- 多步推理如链式思考,如果每步都有小误差,后续步骤会把误差放大,最终偏航。
3. 幻觉的主要类型与识别特征
- 事实型幻觉:日期、数值、出处、API签名编造
- 语义型幻觉:词义错位、概念边界混淆
- 结构型幻觉:表格/代码/格式不符合真实规范
- 逻辑型幻觉:推理链断裂或跳步
- 引用型幻觉:捏造论文、链接、法条、截图
识别小贴士:
- "看起来很像"的内容要特别警惕,比如拼写接近的论文作者、API参数顺序、法条编号。
- 让模型"给出处"和"逐步解释",能更快暴露问题。
4. 工程化解决路线图(从数据到系统)
下面给出自下而上的实战方案栈,每一层都有价值,堆叠效果更好。
4.1 数据层:检索增强生成(RAG)
- 外接检索系统,让模型先"看资料再回答"。
- 核心思想:把"事实"从参数里搬到外部知识库,降低猜测。
- 关键点:高质量切片、向量化召回、重排序、引用片段拼装与上下文窗口管理。
强化策略:
- 查询扩展与重写:改写用户问句,提高召回。
- 多路检索(BM25 + 向量召回 + 结构化数据库)。
- 源文档版本化与时效控制。
- 提供引用片段的标注,便于用户校验。
4.2 推理层:约束生成与程序化验证
-
减少"自由发挥",让生成受控:
- 模板约束:JSON Schema、正则模板、函数调用签名
- 工具调用:把计算、查询、单位换算交给确定性工具
- 程序化校验:对输出进行规则检查与自动回退
4.3 策略层:提示工程与元提示
- 明确约束:若不确定,必须表达不确定或请求澄清。
- 让模型解释思路:隐式链式思考 + 外部验证器。
- 分治提示:将复杂任务拆分为检索、草稿、事实核查、最后成稿。
4.4 反馈层:人类在环与自动评测
- 人类在环(HITL):对关键业务环节做抽检与纠偏。
- 线下评测集:构建包含"陷阱题"的对照集。
- 在线指标:引用命中率、可验证率、事实覆盖度、拒答合规率。
4.5 模型层:微调与拒答策略
- 指令微调:加入"不知道就说不知道"的样本。
- 对抗训练:加入幻觉诱发样本提升鲁棒性。
- 校准输出置信:通过后验估计或阈值策略,控制"敢说"的边界。
5. 一个端到端最小可用范式(JS伪实现)
目标:RAG + 工具调用 + 结构化校验 + 回退策略。
说明:
- 使用伪接口 model.generate 与 search.index/search.query
- 重点演示控制流与校验,而非依赖具体 SDK
javascript
// 基础工具:检索、校验、回退
const search = {
async query(q, k = 5) {
// 同时使用关键词检索与向量检索(伪)
const keywordHits = await kwSearch(q, k);
const vectorHits = await vecSearch(q, k);
return rerank([...keywordHits, ...vectorHits]).slice(0, k);
}
};
function buildContext(docs) {
// 将检索片段拼装,并附上可引用的来源标注
return docs.map((d, i) => `【S${i+1}】${d.snippet}\n(来源: ${d.source})`).join("\n\n");
}
function validateJsonSchema(obj, schema) {
// 极简校验器:只校验字段存在与类型
for (const [k, t] of Object.entries(schema)) {
if (!(k in obj)) return { ok: false, reason: `缺少字段 ${k}` };
if (typeof obj[k] !== t) return { ok: false, reason: `字段 ${k} 类型应为 ${t}` };
}
return { ok: true };
}
async function hallucinationGuard(answer, sources) {
// 简单启发式:检查是否含有强断言但无引用
const strongClaims = [/始终|确定|绝对|官方已确认|唯一/i];
const hasStrong = strongClaims.some(r => r.test(answer));
const hasCite = /[S\d+]/.test(answer) || /【S\d+】/.test(answer);
if (hasStrong && !hasCite) {
return { ok: false, reason: "强断言缺少引用" };
}
// 可扩展:实体对齐、日期数值一致性检查等
return { ok: true };
}
// 主流程
async function answerQuestion(userQuestion) {
// 1) 检索
const docs = await search.query(userQuestion, 6);
const context = buildContext(docs);
// 2) 生成草稿(提示模型:引用来源、标注片段)
const draft = await model.generate({
system: "你是严谨的助手,若不确定请说明并请求澄清。",
prompt: [
"请基于给定资料回答问题,并用【S#】标注引用来源(尽量覆盖关键结论)。",
"若资料不足,请直说不足并提出需要的信息类型。",
"",
`用户问题:${userQuestion}`,
"",
`可用资料:\n${context}`
].join("\n")
});
// 3) 幻觉守门与回退
const guard = await hallucinationGuard(draft.text, docs);
if (!guard.ok) {
// 回退策略:降低温度 + 强制要求引用
const retry = await model.generate({
temperature: 0.2,
system: "你是严谨的助手,必须在关键结论处添加【S#】引用;若资料不足则拒答并说明不足。",
prompt: [
`重新回答,并在关键句后标注来源,问题:${userQuestion}`,
`资料:\n${context}`
].join("\n")
});
return retry.text;
}
// 4) 结构化摘要输出(便于前端或下游系统)
const schema = { finalAnswer: "string", citations: "object" };
const structured = await model.generate({
system: "请将答案压缩为结构化对象",
prompt: [
"生成 JSON:{ finalAnswer: string, citations: { [S#]: sourceUrl } }",
"确保所有引用的S#都在对象里映射到来源链接",
`原答案:\n${draft.text}`,
`资料来源列表(编号->链接):\n${docs.map((d,i)=>`S${i+1}: ${d.source}`).join("\n")}`
].join("\n"),
format: "json"
});
const obj = JSON.parse(structured.text);
const check = validateJsonSchema(obj, schema);
if (!check.ok) {
// 回退为纯文本安全版
return draft.text + "\n\n(提示:结构化失败,已回退为文本版本)";
}
return obj; // 下游可直接渲染
}
要点复盘:
- 外部资料喂给模型,要求显式引用
- 检测强断言是否缺引用,失败则低温重试
- 最终产物结构化,便于监控与 UI 呈现
6. 提示工程示例:减少幻觉的模板片段
可直接纳入你的系统提示或用户提示中:
- 事实优先:
"如果资料不足或不一致,请明确指出不确定性,并列出需要的附加信息类型。不要编造引用或链接。" - 引用规范:
"在每个关键论断之后添加来源标注【S#】。若无可用来源,请写'无来源'并降低语气。" - 拒答策略:
"当问题涉及超出已知资料范围,请回复'无法确定',并建议可能的检索方向或权威渠道。" - 多步推理:
"先列出必要前提与中间结论,再给出最终结论。对每个中间结论尽量附来源或工具计算结果。"
7. 评测与监控:如何量化"少胡说"
建议构建三个维度的指标:
- 可验证率:包含明确引用或可计算验证的比例
- 引用一致性:引用片段与陈述是否语义匹配
- 拒答合规率:不确定时能否正确拒答或请求补充
线上监控手段:
- 抽样对比"有引用 vs 无引用"的正确率
- 域外问题诱饵(比如新发布标准)观察拒答行为
- 自动化规则:链接有效性、日期数值对齐、命名实体一致性
8. 高阶技巧与研究前沿
- 检索-思考交替(ReAct 类)
先检索一点,再思考,再检索,再思考。减少"一口气瞎编到底"。 - 工具编排与程序化推理
把数学计算、单位换算、代码执行交给工具,模型负责"决定调用什么"。 - 自一致性与多样性投票
生成多个推理路径,让它们相互投票,选稳定答案。 - 校准与覆盖估计
用一个"置信评估器"预测"我这句靠不靠谱",高风险时自动降温或拉工具。 - 参数内知识与外部知识的融合
将知识图谱、结构化数据库与文本检索混合;对关键信息用结构化约束。
9. 小结:让模型"敢不会,慎会说"
- 幻觉不是"Bug",更像是"任务定义导致的自然现象"。
- 通过检索增强、约束生成、工具调用、结构化校验与有效拒答,可以把"玄学"变"工程学"。
- 真正稳健的系统,不是让模型无所不知,而是让它知道何时该闭嘴。
小图标收尾:🔍🧭🧩🛡️📎
10. 附:极简前端演示片段(仅为说明交互思路)
下面是一个超简的输入输出组件逻辑,展示如何在前端提示引用和不确定性。无外部依赖,便于移植。
ini
// 假设后端返回 { finalAnswer, citations } 或纯文本
function renderAnswer(payload) {
const root = document.getElementById("answer");
root.innerHTML = "";
if (typeof payload === "string") {
root.textContent = payload; // 回退文本
return;
}
const para = document.createElement("p");
para.textContent = payload.finalAnswer;
root.appendChild(para);
const citeTitle = document.createElement("div");
citeTitle.textContent = "来源:";
citeTitle.style.marginTop = "12px";
root.appendChild(citeTitle);
const ul = document.createElement("ul");
for (const [k, url] of Object.entries(payload.citations || {})) {
const li = document.createElement("li");
li.textContent = `${k} -> ${url}`;
ul.appendChild(li);
}
root.appendChild(ul);
}
愿你与模型的对话,不再是"你演我猜",而是"你证我信"。