首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >如何规避 RAG 系统中大模型的幻觉

如何规避 RAG 系统中大模型的幻觉

作者头像
java金融
发布2026-05-26 19:59:29
发布2026-05-26 19:59:29
1190
举报
文章被收录于专栏:java金融java金融

如何规避 RAG 系统中大模型的幻觉

事情是这样的。

有个团队做了一个内部客服机器人,接了公司的制度库、产品手册和历史工单。

第一轮用户问:

报销打车费需要什么材料?

机器人答得挺准,能说出发票、行程单、审批单,还能引用制度编号。

第二轮用户又问:

那如果是晚上十点之后呢?

机器人也能回答,说需要补充加班证明。

第三轮用户继续问:

那这个是不是主管批一下就行?

这时候系统开始一本正经地胡说了。

它说「主管审批即可,无需额外材料」,还顺手编了一个制度条款。看起来很像真的,语气也很稳,甚至还给了一个不存在的“第 4.2.1 条”。

这就很要命了。

因为用户不是在闲聊,他可能真的会照着做。等到财务驳回,锅不会甩给向量数据库,也不会甩给 prompt,用户只会觉得:这个 AI 不靠谱。

更尴尬的是,团队一开始还很困惑。

明明接了知识库。

明明检索结果也有。

明明模型能力也不差。

那为什么 RAG 系统还是会幻觉?

不是模型突然变坏了,是系统没有管住它

RAG 幻觉最常见的根因,不是大模型完全不知道答案。

而是系统给了它一堆不干净、不完整、没优先级的信息,然后又要求它必须回答得像专家。

模型很擅长补全。

这既是能力,也是风险。

当检索结果缺失、上下文冲突、引用不清、问题指代模糊时,模型不会天然停下来问一句「我不知道」。它更可能根据已有碎片,补出一个听起来合理的答案。

所以规避 RAG 幻觉,不是只在 prompt 里加一句:

不要胡说。

这句话有用,但很有限。

真正要做的是让系统在每一层都减少“模型自由发挥”的空间。

第一层,用户的问题本身可能就不完整

RAG 系统最容易被低估的一层,是 Query。

单轮问答里,用户的问题通常比较完整:

公司差旅报销标准是什么?

但多轮对话里,用户经常这么问:

那这个呢? 刚才那个场景呢? 如果换成上海呢? 主管批一下行不行?

人看得懂,因为人记得前文。

检索系统不一定看得懂。

很多系统的代码是这么写的:

代码语言:javascript
复制
def chat(question, history):
    docs = vector_db.search(question, top_k=5)
    prompt = build_prompt(history, docs, question)
    return llm.generate(prompt)

听起来没问题对吧?

历史进了 prompt,文档也检索了。

但问题在这里:

代码语言:javascript
复制
docs = vector_db.search(question, top_k=5)

检索只看了当前这一句。

用户问「这个是不是主管批一下就行」,向量库根本不知道「这个」指的是“晚上十点之后的打车报销”。

于是它可能搜到一堆包含“主管审批”的制度文档,却没搜到“夜间打车”“加班证明”“差旅报销”这些关键约束。

后面模型拿到的证据已经偏了。

它再强,也是在错误材料上写答案。

这类问题的解法不是把 history 全塞给模型,而是做 Query Rewrite。

把用户当前问题改写成一个独立、可检索的问题:

代码语言:javascript
复制
def rewrite_query(question, history_summary):
    prompt = f"""
你是检索查询改写器。
基于对话摘要和用户当前问题,生成一个完整、明确、适合检索知识库的问题。

对话摘要:
{history_summary}

用户当前问题:
{question}

要求:
- 保留用户真实意图
- 补全指代对象
- 不要引入摘要中没有的信息
- 只输出改写后的查询
"""
    return llm.generate(prompt)

改写后的查询应该类似:

晚上十点之后因加班产生的打车费报销,是否只需要主管审批,还是还需要加班证明和其他材料?

这时候再去检索,命中率会高很多。

但 Query Rewrite 也不是银弹。

它可能改错用户意图。

所以工程上要加两个约束:

风险

处理方式

改写过度

保留原始 query 和改写 query,一起参与检索

指代不清

让模型返回 need_clarification=true,不要强行改写

引入新事实

prompt 明确禁止补充历史里没有的信息

多轮漂移

用结构化摘要,而不是无限 append history

核心判断很简单:

检索前的问题越含糊,回答后的幻觉越像真的。

第二层,向量相似不等于答案相关

很多 RAG 系统上线初期,最爱盯一个指标:

向量相似度。

相似度高,就以为文档靠谱。

但线上系统最怕的就是这个。

因为向量相似度解决的是“语义上像不像”,不是“能不能回答这个问题”。

比如用户问:

晚上十点之后打车报销,是不是主管批一下就行?

向量检索可能返回这些文档:

文档

为什么被召回

实际问题

主管审批流程说明

有“主管”“审批”

没有夜间打车规则

差旅报销总则

有“报销”

条款太泛

加班管理制度

有“晚上十点”

不一定提打车

夜间交通补贴规则

最相关

可能排在后面

如果系统直接把 top 5 塞进 prompt,模型会在这些材料里自己拼答案。

这就是幻觉的温床。

更稳的做法是 Hybrid Search + Rerank。

先用关键词和向量一起召回,再用重排模型判断“这份文档是否能回答当前问题”。

一个简化流程大概是这样:

代码语言:javascript
复制
用户问题
  -> Query Rewrite
  -> Vector Search 召回语义相关文档
  -> Keyword Search 召回关键词命中文档
  -> Merge 去重
  -> Rerank 判断答案相关性
  -> 只保留高相关证据进入 prompt

代码可以写得很直白:

代码语言:javascript
复制
def retrieve(query):
    vector_docs = vector_db.search(query, top_k=20)
    keyword_docs = keyword_search.search(query, top_k=20)

    candidates = deduplicate(vector_docs + keyword_docs)

    ranked = reranker.rank(
        query=query,
        documents=candidates,
        top_k=6
    )

    return [doc for doc in ranked if doc.score >= 0.72]

这里有两个关键点。

一个是召回要宽一点。

不要指望第一次检索就精准命中全部答案。

另一个是进入 prompt 要窄一点。

不要把低相关文档也塞进去,指望模型自己分辨。

坦率地讲,很多 RAG 幻觉不是因为文档太少,而是因为文档太杂。

模型看到 6 段互相打架的材料,又被要求“给出明确答案”,它就会开始编一个看似统一的结论。

第三层,Prompt 里必须标清证据边界

很多系统的 prompt 看起来像这样:

代码语言:javascript
复制
请根据以下参考资料回答用户问题。

参考资料:
{context}

用户问题:
{question}

这太粗了。

模型不知道哪段资料更新,哪段资料过期,哪段来自正式制度,哪段来自历史工单,哪段只是用户聊天记录。

对人来说,来源不同,可信度完全不同。

对模型来说,如果你不标,它就可能混着用。

更可靠的 prompt 应该把证据边界写清楚:

代码语言:javascript
复制
你是企业知识库问答助手。

回答规则:
1. 只能依据【正式制度】和【产品文档】回答。
2. 【历史工单】只能作为理解问题的辅助,不能作为制度依据。
3. 如果资料不足以回答,必须说“当前资料不足,无法确认”。
4. 每个关键结论后必须引用资料编号。
5. 不允许编造制度编号、条款号、流程名称。

参考资料:
[doc_id=HR-2025-018]
[type=正式制度]
[updated_at=2025-03-12]
[trust=high]
内容:晚上十点后因加班产生的打车费,需提供加班审批记录、发票和行程单。

[doc_id=TICKET-8831]
[type=历史工单]
[updated_at=2024-11-02]
[trust=low]
内容:有员工反馈主管口头同意后仍被财务驳回。

用户问题:
晚上十点之后打车报销,是不是主管批一下就行?

这样做不是为了让 prompt 更长。

而是为了让模型知道:

哪些能当证据。

哪些只能当背景。

哪些信息优先级更高。

如果没有这个边界,模型很可能把历史工单里的“主管口头同意”当成正式规则。

这就是很多企业 RAG 最危险的地方:

模型不是凭空胡说,它是在错误地平权所有上下文。

所以我自己更建议,RAG prompt 里至少给每段材料加 4 个元信息:

元信息

作用

source

资料来源,制度、手册、工单、网页、用户上传

updated_at

判断时效性

trust_level

判断可信度

doc_id

方便引用和追责

当答案不能被高可信资料支撑时,系统必须允许拒答。

拒答不是体验差。

在企业场景里,乱答才是体验差。

第四层,不要让模型替你做事实校验

很多团队会把所有压力都压给生成模型:

你要准确。 你要基于知识库。 你不要幻觉。 你要引用来源。

这当然要写。

但不能只写。

因为生成模型的主要任务是“生成答案”,不是“审计答案”。

更稳的架构应该在生成后加一层校验。

比如让系统检查三个问题:

  1. 答案里的关键结论,是否都能在资料中找到依据?
  2. 引用的 doc_id 是否真实存在?
  3. 答案有没有出现资料中没有的条款、数字、时间、流程名?

可以做一个轻量级 verifier:

代码语言:javascript
复制
def verify_answer(answer, evidence_docs):
    prompt = f"""
你是答案校验器。

请检查回答是否完全被证据支持。

证据:
{format_docs(evidence_docs)}

回答:
{answer}

输出 JSON:
{{
  "supported": true/false,
  "unsupported_claims": [],
  "fake_citations": [],
  "should_refuse": true/false
}}
"""
    return llm.generate_json(prompt)

如果校验失败,就不要直接把原答案吐给用户。

可以降级成:

代码语言:javascript
复制
当前知识库资料不足以确认“只需要主管审批”。
已检索到的正式制度只说明:晚上十点后因加班产生的打车费,需要提供加班审批记录、发票和行程单。

这一步会增加延迟和成本。

但在制度、医疗、金融、法务、运维这些场景里,很值。

因为这些场景里,幻觉不是“答错一道题”。

它可能会变成错误操作、错误承诺、错误决策。

第五层,没有评估,幻觉只能靠感觉排查

RAG 系统最怕一种状态:

线上有人反馈不准,团队就开始调 prompt。

调完之后,感觉好像好了一点。

过几天又有人反馈不准,再继续调。

这不是工程优化。

这是玄学调参。

要规避幻觉,必须能量化看到它在哪一层发生。

至少要记录这些指标:

指标

看什么问题

retrieval_hit_rate

正确文档有没有被召回

rerank_top1_accuracy

最相关文档有没有排到前面

citation_coverage

答案关键结论有没有引用

unsupported_claim_rate

有多少结论没有证据支持

refusal_rate

不确定时是否敢拒答

multi_turn_decay

第几轮开始质量下降

尤其是多轮 RAG,一定要看质量衰减曲线。

比如你可能会看到这样的日志:

代码语言:javascript
复制
session_id=9821 turn=1 hit_rate=1.00 citation_coverage=0.92 unsupported_claim=0
session_id=9821 turn=2 hit_rate=0.83 citation_coverage=0.86 unsupported_claim=0
session_id=9821 turn=3 hit_rate=0.41 citation_coverage=0.38 unsupported_claim=3
session_id=9821 turn=4 hit_rate=0.35 citation_coverage=0.22 unsupported_claim=5

这时候问题就很清楚了。

不是模型从第三轮开始突然不会说话。

是第三轮开始,检索命中率掉了,引用覆盖率掉了,无证据结论上来了。

排查方向就不该是“换个更大的模型试试”。

而是回到 Query Rewrite、Memory 压缩、Rerank 和 Prompt 证据边界。

工程上怎么落地

如果要把 RAG 幻觉压下去,我会按一条链路改。

不要一上来就迷信大模型参数。

先把系统管住。

一套相对稳的链路是:

代码语言:javascript
复制
用户输入
  -> 意图识别 / 是否需要检索
  -> Query Rewrite / 指代消解
  -> Hybrid Search
  -> Rerank
  -> 证据过滤
  -> 带元信息的 Prompt
  -> 生成答案
  -> Grounding Verify
  -> 引用检查 / 拒答降级
  -> 日志与评估

每一层解决一个具体问题:

解决什么

Query Rewrite

用户问题不完整、指代不清

Hybrid Search

单纯向量召回漏掉关键词约束

Rerank

相似文档不等于答案文档

Metadata Prompt

来源、时间、可信度混乱

Verify

生成内容没有证据支撑

Eval

不知道幻觉在哪一层出现

这里面有几个边界要提前讲清楚。

Query Rewrite 会改错意图,所以要保留原问题。

Rerank 会增加延迟,所以可以只对高风险问题启用。

Verifier 会增加成本,所以可以按场景分级,比如制度、权限、金额类问题强制校验。

长上下文不能替代信息治理。

你把 100 页资料塞进去,不等于模型就会更可靠。很多时候,只是让模型在更大的噪声里更自信地犯错。

面试怎么答

如果面试官问:

如何规避 RAG 系统中大模型的幻觉?

不要只答“优化 prompt、增加知识库、降低 temperature”。

这个回答太浅。

可以这么说:

我会把 RAG 幻觉拆成四类问题看。

第一类是检索前的问题。用户的问题可能有省略和指代,所以要做 Query Rewrite,把当前问题改写成独立可检索的问题,同时保留原始 query,避免改写过度。

第二类是检索中的问题。向量相似度不代表答案相关,所以会用 Hybrid Search 扩大召回,再用 Rerank 过滤真正能回答问题的文档。

第三类是生成时的问题。Prompt 里要给证据加来源、时间、可信度和 doc_id,要求模型只能基于高可信资料回答,关键结论必须引用来源,资料不足时允许拒答。

第四类是生成后的问题。不能只相信模型自己说“我是基于资料回答的”,还要做 grounding verify,检查答案里的关键结论是否被证据支持,引用是否真实。

如果继续追问怎么评估,我会补一句:

线上要看检索命中率、引用覆盖率、无证据结论率、拒答率和多轮质量衰减。否则 RAG 准不准只能靠感觉,最后会变成反复调 prompt。

这个回答的重点不是术语多。

而是你能说清楚:幻觉不是一个点的问题,是检索、排序、上下文、生成、校验、评估整条链路的问题。

回到开头那个客服机器人

用户问:

那这个是不是主管批一下就行?

系统如果没有做指代消解,就不知道“这个”是什么。

如果检索只看向量相似度,就可能拿到一堆“主管审批”的泛文档。

如果 prompt 不标来源,就可能把历史工单当正式制度。

如果生成后不校验,就会把编出来的条款号直接发给用户。

所以 RAG 系统里的幻觉,很多时候不是模型不知道。

而是系统没有告诉它:

什么能说。

什么不能说。

什么必须有证据才能说。

真正可靠的 RAG,不是让模型看起来更聪明。

是让模型在证据不足的时候,敢停下来。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java金融 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何规避 RAG 系统中大模型的幻觉
    • 不是模型突然变坏了,是系统没有管住它
    • 第一层,用户的问题本身可能就不完整
    • 第二层,向量相似不等于答案相关
    • 第三层,Prompt 里必须标清证据边界
    • 第四层,不要让模型替你做事实校验
    • 第五层,没有评估,幻觉只能靠感觉排查
    • 工程上怎么落地
    • 面试怎么答
    • 回到开头那个客服机器人
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档