LLM间接现实:从幻觉到可信系统的工程实践
2026/8/27 7:04:03 网站建设 项目流程

Indirected Reality 这个短语描述的是大型语言模型认识世界的方式:模型并不直接接触物理世界,而是通过人类写下的文字间接地重建世界。苹果的甜、数据库的高延迟、一场台风造成的损失,对模型来说都只是训练语料里的共现关系。对做 LLM 工程的人来说,这不是玄学,而是幻觉、知识过时、答案无法验证这些生产问题的共同根源。下面先讲清楚 LLM 与现实之间的两层间接关系,再解释为什么 truth 对模型来说天然困难,然后给出检索增强、结构化输出、引用验证、真实性评测等可落地的工程手段,最后整理一份生产环境检查清单。读完你会明白:追求 LLM 绝对正确不现实,设计一套能验证、能回退、能追溯的系统才是现实目标。

1. Indirected Reality 到底指什么:模型通过语言接触世界,而不是直接接触世界

1.1 从词到向量的过程,已经决定了模型与现实的距离

大模型处理文本的第一步是分词和向量化。一段输入先被 tokenizer 切分成 token,每个 token 再映射成一个高维向量。这个向量的位置并不由物体的物理属性决定,而是由“这个词在语料里经常和哪些词一起出现”决定。模型从训练开始到推理结束,接触到的都是向量和概率分布,没有视觉、触觉、味觉,也没有对物理世界的实时反馈。

用认知科学里的“符号接地问题”来解释更清楚:模型能熟练处理“苹果”这个符号,却不曾咬过一口苹果。如果训练语料里“苹果”总是和“手机”“发布会”“生态系统”共现,模型会偏向把苹果理解为科技品牌;如果训练语料里“苹果”总是和“水果”“甜”“种植”共现,模型则偏向农产品。同一个词,两种分布,模型无法靠自己的经验分辨哪个是真实世界的哪个是隐喻。它只能跟随语料统计。

这就是 Indirected Reality 的第一层含义:模型与现实的接触,必须经过“符号系统”这一层中介。分布式表示虽然擅长捕捉语义相似性,但它捕捉到的是语言使用的规律,不是世界的规律。

1.2 训练语料是人类的文字记录,不是世界的原始图像

第二层间接性来自训练语料的形成过程。语料并不是世界的镜像,而是人类对世界的叙述。作者写下一句话,需要经过感知、筛选、归纳、语言编码这一整套流程。现实世界的事件,先被作者理解成经验,再被打磨成文字。模型拿到的已经是二手甚至三手信息。

这个过程中必然产生四类问题:

  • 滞后:文字记录总是晚于事件发生,训练完成的模型更是固定在一个知识截止时间。
  • 偏差:写作动机各不相同,营销文案、媒体报道、用户评价对同一件事的描述可能完全相反。
  • 错误:人类写下的文字本身包含大量事实错误和过时信息。
  • 缺失:大量隐性知识不会被写成文字,比如操作系统的运行手感、线下服务的判断标准。

于是模型学到的不是一个完整的世界模型,而是“人类用文字描述世界时留下的分布痕迹”。分布高频的内容容易被模型当成常规事实,分布低频但真实存在的内容容易被忽略。这也是为什么后续接上的对齐训练只能改善表达风格和偏好,并不能把事实数据库实时接进模型。

1.3 工程前提:接受间接性,把目标改成“可验证”

如果接受模型不可能直接接触现实,工程上就应该调整目标。不要问“如何让模型知道所有真相”,而要问“如何设计一个系统,能在模型说出一个论断时,快速判断它有没有依据”。

落到架构上,是三个转变:

  • 从“让模型记住更多事实”转向“让模型在需要时查询事实”。
  • 从“输出一段通顺回答”转向“输出带证据链的回答”。
  • 从“模型对自己的回答负责”转向“系统对证据和结论的一致性负责”。

这样做不是否定大模型的能力,而是把能力放在正确的位置:语言生成、语义理解、逻辑组织由模型负责;事实的获取、更新、核验由外部系统和工程流程负责。Indirected Reality 不是模型的缺陷,而是所有语言系统的共同处境。真正可落地的方向,是把这种间接性变成可控的系统设计。

2. LLM 的 truth 问题:预测下一个词和说出真相是两回事

2.1 自回归训练目标与事实性无关

主流的生成式大模型采用自回归训练方式:给定前文,预测下一个 token。训练损失衡量的是“模型预测的词是否和语料里的词一致”,而不是“这句话在现实世界是否成立”。模型优化的目标,是让生成文本在统计上接近人类写法,要求是通顺、连贯、符合上下文。

这带来一个很实际的问题:一句语法完全正确、语气非常笃定的话,可能完全错误。比如“公司已停止维护旧版接口”这种句子,模型完全可以在一秒内生成得和真新闻一模一样。它不是故意撒谎,而是它的训练目标里根本没有“去外部世界验证一下”这个环节。

因此,truth 对 LLM 来说是外部工程问题,不是模型内部能力问题。判断一句话是否符合事实,需要额外的事实来源和校验步骤。

2.2 知识放在哪里,决定知识的时效性和可信度

从系统角度看,模型可用的知识有三类存放位置,它们的更新成本、时效性和可解释性完全不同。

知识位置来源更新成本时效性可解释性幻觉风险
参数记忆预训练语料需要重新训练或微调低,存在知识截止时间低,无法定位出处中高,容易过度自信
上下文窗口用户提示、历史对话、临时资料每次请求修改高,但受窗口大小限制中,取决于提示质量
外部检索(RAG)数据库、知识库、文档、网页更新索引即可高,可列出来源文档低,但依赖检索质量

这张表解释了为什么纯靠模型“记忆”做客服问答、技术文档问答并不可靠。知识截止之后的新版本、新接口、新政策,模型完全不知道;而 RAG 方案把知识搬离参数区,放在可更新的外部索引里,模型只负责阅读和改写。对可解释性要求高的业务,RAG 几乎是必选项。

2.3 幻觉不是偶然 bug,而是统计行为的必然结果

幻觉也不是模型偶尔犯的错,而是自回归语言模型在特定条件下的必然倾向。常见成因包括:

  • 统计惯性:训练语料里高频出现的说法,更容易被模型输出,即使它已经过时。
  • 知识截止:训练结束后出现的新事实,模型无法知道,却仍可能强行组织回答。
  • 提示误导:用户问题里嵌入了错误前提,模型顺着前提继续编造。
  • 解码扰动:temperature 大于 0 时,采样引入随机性,同一问题可能得到不同答案。
  • 对齐目标偏差:RLHF 等对齐技术优化的是人类偏好和安全规范,并不等价于实时事实验证。

理解这几点,就不应该期待“换一个更大的模型就能消灭幻觉”。幻觉只能被压低、被拦截、被暴露,不能被彻底根除。工程上要做的是让幻觉发生时,系统能及时标识“这条信息没有依据”,或者直接拒绝回答。

3. 把可信做成工程能力:检索、约束与验证

3.1 RAG 是典型的“间接现实”接口

检索增强生成(Retrieval-Augmented Generation,RAG)是目前最成熟的可信化方案。它的基本链路是:用户问题先转为查询向量,在向量数据库中检索相关文档片段,把 top-k 结果拼进提示词,再让模型基于这些资料生成回答,最后把来源文档编号一并返回。

RAG 之所以有效,是因为它把“回忆事实”改成了“阅读理解”。模型不再需要从参数里搜索事实,只需要从给定的上下文中找出答案。这样有几个直接好处:

  • 事实可以随时更新,更新知识库即可,不需要重新训练模型。
  • 回答可以溯源,每个论断都能对应到具体文档。
  • 幻觉更容易拦截,因为模型只被允许基于上下文回答。

当然,RAG 不是万能药。查询改写、文档切分、向量召回、重排序、引用一致性,任何一个环节出错都会影响最终质量。

3.2 最小可运行的检索增强问答管线

下面这个示例展示 RAG 的最小闭环。为了便于阅读,检索部分用关键词命中代替向量检索;实际项目请换成 embedding 模型加向量库。

# 最小验证管线示例:文档检索 + LLM 生成 # 实际项目请根据向量库和模型服务商调整 import 与参数 from typing import List, Dict DOCUMENTS = [ { "id": "doc-001", "title": "产品发布记录", "content": "2025 年 4 月,示例公司发布新版本,支持离线缓存。", "source": "/docs/release/2025-04.md", }, { "id": "doc-002", "title": "数据库连接池说明", "content": "生产环境建议最大连接数不高于 200,避免数据库负载过高。", "source": "/docs/ops/database-pool.md", }, ] def retrieve(query: str, top_k: int = 2) -> List[Dict]: # 演示用关键词命中,实际项目应使用向量检索 hits = [] for doc in DOCUMENTS: if any(k in doc["content"] for k in query.split()): hits.append(doc) return hits[:top_k] def build_prompt(query: str, contexts: List[Dict]) -> str: context_text = "\n\n".join( f"[文档 {doc['id']}] {doc['content']}" for doc in contexts ) prompt = f"""请根据提供的资料回答用户问题。 资料: {context_text} 要求: 1. 如果资料中找不到答案,直接回复“资料中未找到相关信息”。 2. 不要编造资料中不存在的内容。 3. 回答后列出依据的文档编号。 用户问题:{query} """ return prompt def generate(prompt: str) -> str: # 这里替换为实际模型调用,例如本地模型服务或云端 SDK # response = client.chat.completions.create( # model="your-model", # messages=[{"role": "user", "content": prompt}] # ) # return response.choices[0].message.content return "示例回答:资料中未找到相关信息。" def answer(query: str) -> str: docs = retrieve(query) prompt = build_prompt(query, docs) return generate(prompt) if __name__ == "__main__": print(answer("新版本支持什么功能?"))

这个示例有三个关键点。第一,提示词里明确给出资料边界,并且强制要求“找不到就拒绝”。第二,回答要求附文档编号,为后续校验提供结构。第三,generate 函数被隔离成独立模块,实际项目可以平滑替换模型服务,不影响上游逻辑。真正的生产版本还需要把 document 切分成合适粒度的 chunk,建立向量索引,并在检索后增加重排。

3.3 结构化输出:让回答进入程序可校验的轨道

自由文本难以校验。更稳妥的做法是要求模型输出 JSON 格式的结构化结果,把答案、置信度、引用、是否资料不足都变成程序可读的字段。

{ "type": "object", "properties": { "answer": { "type": "string" }, "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, "citations": { "type": "array", "items": { "type": "string" } }, "insufficient": { "type": "boolean" } }, "required": ["answer", "confidence", "citations", "insufficient"] }

拿到这种结构化结果后,可以写一段轻量校验逻辑:

# 校验模型输出:非法引用或字段越界时直接拦截 from typing import List, Dict import json def validate_response(raw: str, allowed_doc_ids: List[str]) -> Dict: data = json.loads(raw) assert 0 <= data["confidence"] <= 1, "confidence 超出范围" assert set(data["citations"]).issubset(set(allowed_doc_ids)), "存在未检索文档的引用" return data

这段逻辑解决两个实际问题:一是模型可能生成根本不存在的文档编号;二是模型可能给出过高的置信度。规则校验通过后,回答才能进入下游展示或工单系统。如果校验失败,系统可以丢弃本次回答并走兜底流程,而不是把错误结果直接暴露给用户。

3.4 让模型学会说“不知道”

对可信系统来说,拒答不是产品缺陷,而是可控行为。通过在系统提示词中明确“不知道”的边界,可以把模型的默认行为从“硬答”改造成“先判断再回答”。

系统指令: 你是资料检索助手。请严格基于提供的资料回答。 约束: 1. 资料中不存在答案时,回复:“资料中未找到相关信息”,并设置 insufficient=true。 2. 不要推测,不要使用资料之外的常识补充。 3. 回答必须附资料文档编号。 4. 如果用户问题中包含未被资料证实的假设,先说明“题干前提未在资料中找到依据”,再回答可证实部分。

配合上一节的 confidence 字段,可以设置阈值逻辑:confidence 低于 0.3 时直接进入人工兜底;低于 0.6 时在界面上增加“该回答可能不完整”的提示;高于 0.6 才作为主答案展示。这套机制让模型在不确定时主动暴露不确定性,而不是用自信的措辞掩盖问题。

4. 如何评估输出的真实性:指标、数据与流程

4.1 真实性评估的核心指标

评估不能只看“回答是否通顺”,要从事实支撑的角度拆开看。

指标含义计算方式关注点
忠实度 Faithfulness回答中的论断是否都能由资料推出逐句判断,或由人工/评估模型标注防止无中生有
覆盖率 Coverage资料中回答问题所需的关键信息是否都被用上与标准答案对照查漏防止遗漏关键事实
引用准确率 Citation Precision每个引用编号是否确实支撑对应句子逐句回查文档防止引用装饰
拒答率 Refusal Rate资料不充分时是否正确拒绝统计 insufficient=true 的样本占比防止硬答
幻觉率 Hallucination Rate无法由资料支撑的句子在全部句子中占比句子级标注后统计整体质量底线

对技术文档问答场景,忠实度比覆盖率更优先。宁可少答一点,也不能答错;宁可拒绝,也不要用幻觉填满。对需要完整解决方案的场景,覆盖率则要和忠实度一起看,避免只答了局部问题。

4.2 构造一条最小评测集

评测集至少要覆盖四类样本:资料可答、资料不可答、资料互相冲突、资料内容过时。下面是一个最小示例:

[ { "query": "新版本支持离线缓存吗?", "source_ids": ["doc-001"], "answerable": true, "expected_key_points": ["支持", "离线缓存"] }, { "query": "产品获得过哪些奖项?", "source_ids": ["doc-001", "doc-002"], "answerable": false, "expected_behavior": "拒答或说明资料未覆盖" }, { "query": "数据库连接数应该设置为多少?", "source_ids": ["doc-002", "doc-003"], "answerable": true, "conflict": true, "expected_behavior": "指出资料存在冲突,不要自行选一个作为唯一答案" } ]

评测集不需要一开始就做得很大,50 条覆盖四类场景即可用于基线建立。重点是保持测试集不变,这样每次换模型、改提示词、换索引后,才能对比指标变化,判断改动是改善还是回退。

4.3 自动评估和人工评估的搭配方式

自动评估可以先跑规则校验和基于 LLM 的打分。规则校验负责引用合法性、JSON 结构、置信度范围;LLM 打分负责判断忠实度和覆盖率,但需要给评估模型明确的评分标准和正反示例,否则评估模型自己也会犯“顺着正确答案编理由”的毛病。

人工评估不能省。上线前的关键样本、自动评估中出现分歧的样本、真实用户反馈较差的样本,都应进入人工复核池。实际流程可以是:生成结构化解 -> 规则校验 -> LLM 粗筛 -> 人工抽样复核 -> 输出指标周报。自动评估保证效率和覆盖,人工评估守住底线。

5. 常见坑与排查路径

5.1 模型把问题里的错误假设当成既定事实

现象:用户问“新版本的接口为什么移除分页参数?”,但资料里根本没有移除分页这件事,模型却顺着问题编造了一堆原因。

原因:提示词没有要求模型先验证题干前提;模型对“为什么”类问题的惯常反应是补全因果关系。

检查方式:把用户问题原样输入检索系统,查看检索返回的资料里是否存在“移除分页”相关描述。

处理建议:在系统提示词中加入“若题干前提未在资料中出现,先指出前提缺乏依据,再回答可证实的部分”。同时把“无法证实前提”纳入拒答或降置信度逻辑。

5.2 检索不到相关内容时模型仍硬答

现象:top-k 文档与问题无关,模型仍然基于自身记忆生成了一段貌似合理的回答。

原因:向量召回质量差;top-k 太小导致有效文档缺失;提示词没有对“资料不足”做硬约束。

检查方式:打印每次请求检索到的 top-k 文档 id、内容片段和相似度分数,人工核对命中是否合理。

处理建议:提高 top-k 并增加重排序;设置最低相似度阈值,低于阈值时直接触发拒答;在提示词中把“不编造资料外内容”从建议改成命令。

5.3 引用了文档编号,但答案内容与编号不对应

现象:回答里写了 [doc-002],但对应句子在 doc-001 中出现。

原因:模型把引用当成文本装饰,生成时没有把每个引用和具体句子绑定,或者上下文里文档顺序混乱。

检查方式:抽取回答中每个 citation,回查对应文档段落,逐句判断支撑关系。

处理建议:在输出 schema 中把 citations 约束为本次检索文档 id 的子集;生成后用规则校验,发现不匹配时丢弃整条回答;更彻底的做法是采用“引用约束解码”或句子级检索后再组装答案。

5.4 温度设置导致事实型问题输出不稳定

现象:同一问题连问 10 次,回答内容不一致,甚至出现一次有引用、一次没引用。

原因:temperature 大于 0 时采样引入随机性;部分服务即使设置 temperature=0,也因批处理或采样器实现存在微小波动。

检查方式:固定 prompt 和参数,重复请求 10 次,统计答案变化率和引用缺失率。

处理建议:事实型任务设置 temperature=0,并固定随机种子;创意型任务可以放宽,但要对事实字段单独走冷却通道;上线前做稳定性回归,防止参数调整带来隐性质量下降。

把上述问题整理成排查表:

问题现象可能原因检查方式处理建议
问题前提错误时仍继续回答提示词未要求验证前提核对检索结果是否包含前提描述增加前提验证指令和拒答逻辑
检索无关但仍生成长答案召回质量差或提示约束不足打印 top-k 文档和相似度提高 top-k、加重排、加相似度阈值
引用编号与内容不匹配引用仅是文本补全逐句回查引用文档约束 citations 子集并做规则校验
事实型回答不稳定温度或采样随机性同一问题重复 10 次temperature=0,固定随机种子

6. 生产落地:在“间接现实”上建立可信系统

6.1 可信 LLM 应用的发布前检查清单

上线前逐项确认,可以作为团队评审模板:

  • 知识源清单:哪些文档被索引,更新时间,负责人是谁。
  • 文档切分策略:chunk 粒度是否适合检索,是否覆盖了关键信息。
  • 检索质量基线:抽样 50 条真实用户问题,检查 top-3 命中率。
  • 拒答率与幻觉率基线:先测出当前模型的数值,再设定上线红线。
  • 引用合法性:所有对外展示的回答必须通过 citation 校验。
  • 兜底流程:低置信度时转人工、展示资料卡片或返回预设话术。
  • 日志全链路:记录 query、检索文档、生成结果、置信度、用户反馈。
  • 监控告警:幻觉率上升、拒答率异常、检索服务超时都要有告警。
  • 版本回滚:代码、索引、模型版本分别支持回退。
  • 权限与脱敏:内部文档是否被越权检索,敏感信息是否被模型带出。

6.2 学习环境与生产环境的显著差异

学习环境跑通 demo 和上线生产是两套完全不同的要求。

维度学习环境生产环境
知识源少量示例文档持续更新的业务库、多来源异构文档
检索关键词或本地向量库高可用检索服务、多路召回、重排序
模型任意公开模型固定版本、安全审核、降级方案
验证人工看几条回答自动化评测加人工抽样,指标可对比
日志基本不需要全链路可追溯,支持问题复盘
安全最低要求权限隔离、数据脱敏、操作审计

最常见的失败模式,是把学习环境里“模型答得不错”直接等同于“生产可以上线”。实际上,随机几条效果好,可能是因为测试问题恰好落在文档覆盖范围内,检索压力、并发、权限边界和异常输入都还没有被检验过。

6.3 更长的路:从语言可信走向系统可信

进一步扩展可以考虑四个方向。第一是接入知识图谱,用实体和关系做二次校验,回答里的“公司名-时间-事件”三元组如果和图谱冲突就降权。第二是多模态接地,把文档、图片、数据库实时数据统一成事实源,让模型不仅能读文字,还能查结构化数据。第三是对抗性评测,持续构造诱导性提问、冲突文档和恶意假设,验证拒答和校验逻辑是否被绕过。第四是持续评测机制,每次换模型、换索引、换提示词都重跑同一套测试集,保证质量可比较。

对刚入门的开发者,最值得做的一件事不是继续研究更大的模型,而是先把手上的管线做成“能说不知道、能出示证据、能被验证”的闭环。Indirected Reality 是所有语言系统的共同处境,工程上真正稳的方向,是接受这种间接性,然后用检索、约束、验证和回退把它变成可控的系统行为。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询