RAG与大模型核心关系:从幻觉到检索增强生成的全链路拆解

如果你丢给一个大模型一句"我们公司的请假流程是什么",它大概率会非常流畅地给你编出一套"请假先填表、领导审批、HR备案"的流程。看上去无懈可击,可问题是:你们公司根本没有这套流程。这种"一本正经地胡说八道"在圈子里有个专门的名字,叫幻觉。我第一次在企业内部问答项目里撞上它时,第一反应是"模型坏了",后来才慢慢明白,大模型从来就不是一台会"记忆"的机器,它更像一个语感极强、但不太分得清事实和想象的高级续写器。而那一年开始频繁出现在各种架构图里的 RAG,解决的就是这个问题。

RAG,全称 Retrieval-Augmented Generation,检索增强生成。这可能是大模型圈被提到最多、也最容易讲偏的概念之一:有人把它当成一个新模型,有人以为它是某种插件,还有人觉得有了 RAG 就不用做大模型微调了,全是误解。我打算基于这几年做 RAG 项目的实际体会,把 RAG 和大模型之间那层核心关系彻底拆开讲清楚。这篇东西适合谁看?不管是准备做知识库问答、企业内部助手、私域数据检索的人,还是已经跑通了一个 RAG Demo 但效果不理想、想弄明白下一步到底该优化哪里的人,应该都能在里面找到点能直接拿去用的东西。

1. 为什么大模型看起来很聪明,却总是"记不住任何东西"

1.1 大模型的工作方式是续写,不是查询

先把一个底层认知摆正:大模型本质上是一个根据上文预测下文的概率模型。你问它问题,它的"思考"过程不是去数据库里查一条记录,而是根据你的问题,结合它训练时见过的大量文本,逐个 token 去预测最合理的下一个字。这种机制让它在语言组织、逻辑铺陈上非常强,但也意味着:所有"知识"都被压缩进了模型参数里,而不是以可检索条目的形式存在。

我常跟人说,大模型像是把整个互联网"煮"成了一口浓缩汤。你喝汤的时候确实能尝出牛肉味,但你没法从汤里精准捞出一块原封不动的牛排。这个类比的意思是:模型可以表现得很懂某个领域,可当你需要它给出一个必须精确、必须可溯源、必须和当前语境完全一致的答案时,它的续写机制反而成了短板。它不知道答案时,不会说"我不知道",它只会用看起来合理的句子把空缺填上——这就是幻觉的根源。

1.2 三重困境:幻觉、知识截止、私有数据进不来

在实际项目里,大模型裸奔状态有三个绕不过去的问题。

第一是幻觉。当模型不确定答案,又没有外部信息可以依赖时,它会用最流畅的方式填补空缺。它不是故意骗你,而是它的训练目标里根本没有"不知道就承认不知道"这一项。我接手过的客服机器人,早期没接检索时,连"我们支持货到付款吗"这种简单问题都能编出三种互相矛盾的答案。

第二是知识截止。训练集有一个时间点,之后发生的事情模型一概不知道。比如训练数据截止到 2023 年的模型,你跟它聊 2025 年新发布的技术框架,它只能靠猜。企业内部文档大多不是公开语料,更不可能在训练集里。

第三是私有数据进不来。企业内部制度、项目代码、业务数据,这些内容不会出现在任何公开训练集里,模型天然不可能知道。你不可能要求一个通用大模型知道你们公司某个审批流的页面长什么样,除非你把上下文给它带过去。

这三个问题叠加起来,就是 RAG 存在的根本原因:用外部检索到的信息,去弥补模型内部参数的不足。记住这句话,后面所有设计都在为它服务。

1.3 为什么不把所有知识直接塞进模型参数

在做"RAG 还是微调"的讨论时,团队里第一反应往往是:既然模型不知道,那我把知识喂给它不就行了?理论上确实有两条路:一是把文本塞进提示词,对应 RAG 的思路;二是对大模型做微调,把知识写进参数。微调确实能改模型行为,但想通过微调让模型"记住"成百上千份持续更新的企业文档,成本极高:需要高质量标注数据、需要 GPU 算力、训练一次可能几十个小时,还可能灾难性遗忘——学会了新知识,忘了老能力。更头疼的是,业务文档今天改了 3 份、明天新增 5 份,模型就得重新训一遍。

这也是我在实际项目里几乎总是先考虑 RAG 的原因:知识更新只需要重新切块、重新索引,模型参数一行不动,成本低、可回滚、可审计。微调更适合解决的是另一类问题,后面专门用一节说清楚。

2. RAG 的本质:把大模型从"闭卷考场"挪到"开卷考场"

2.1 RAG 不是模型,是一套系统架构

很多人第一次听到"RAG 模型"这种说法时,会误以为 RAG 是一种新的预训练模型。真不是。RAG 是一套"检索 + 生成"的系统架构,由三个核心部分组成:检索器、生成器、把两者连接起来的编排逻辑。生成器通常就是某个现成的大模型,检索器负责从外部知识库中找到与当前问题相关的文档片段,编排逻辑决定"怎么找、找多少、怎么拼进提示词"。

理解这一点,才能理解标题里"RAG 和大模型的核心关系"。RAG 不会替代大模型,也不会和大模型并列竞争。它是在大模型外围构建了一条知识供给链路,让大模型在回答问题时能临时查阅资料,而不是凭印象瞎编。换句话说,大模型依然是那个最终说话的人,RAG 只是替它把参考资料提前摆到桌面上。

2.2 检索、增强、生成,三个词各自承担什么职责

"增强"这个字眼经常被忽略,但它是理解 RAG 的钥匙。所谓增强,指的是模型本身的参数不变、能力不重新训练,只是通过检索到的内容把答案的"事实依据"补全,再让大模型基于这些依据重新组织语言。我们经常说的"把资料喂给大模型",本质上就是把检索结果作为上下文拼进 Prompt。

所以 RAG 的完整流程可以压缩成一句话:先按问题去知识库里检索相关资料,再把资料和问题一起交给大模型,由大模型生成最终答案。

这个"检索在前、生成在后"的顺序特别重要。如果顺序反了,或者检索到的内容跟问题不相关,那一轮 RAG 几乎必然失败——不是大模型不行,而是它拿到的"参考资料"有问题。我在带新人时经常让他们做一个实验:故意把无关文档塞进知识库,然后问一个明确的问题,看模型怎么回答。十次有八次,模型会一本正经地结合无关文档编一个新答案。这就是"垃圾进,垃圾出"的放大版。

2.3 大模型是大脑,RAG 是外接记忆

我一般跟团队同学这样解释:大模型像一个文笔极好但记忆力很差的研究员,你直接问它问题,它只能用印象乱讲;RAG 就是给这位研究员配了一个图书管理员,在回答前先把相关书籍的页码翻好、递到他手里。研究员还是在靠自己的本事写答案,但有了管理员递来的素材,他写出来的东西就靠谱得多。

这也是很多人把 RAG 称为"大模型的第二大脑"或"外接记忆"的原因。明确了这层定位以后,后续调优的方向就清晰了:你要优化的不只是模型,更是那个负责递材料的检索链路。材料不对,模型再聪明也救不回来。

3. 从文档到答案:亲手拆开一条 RAG 管线

3.1 离线阶段:切块、向量化、建索引

RAG 一般分两个阶段。离线阶段负责"把知识准备好",在线阶段负责"问答时实时检索"。离线阶段最先做的是文档加载和清洗。文档格式五花八门,PDF、Word、Markdown、HTML、Confluence 导出的页面,统统要转成纯文本,然后去掉页眉页脚、多余空白、表格错乱之类的问题。

清洗完的文本通常太长,不能整篇塞进上下文窗口,所以要切块。我常用的默认策略是固定长度加重叠窗口,比如每块 500 字符,重叠 100 字符。重叠的目的是避免一句话刚好被拦腰截断,导致语义碎片化。接下来做向量化,也就是 Embedding:把每一块文本转换成一组浮点数向量,让意思相近的文本在向量空间里距离更近。最后把这些向量连同原文、元数据一起写入向量数据库,形成索引。这个阶段完成后,知识库就算建好了。

3.2 在线阶段:问题向量化、相似度召回、拼装提示词

用户发起一个查询时,系统先把问题用同一个 Embedding 模型转换成向量,再到向量数据库里做相似度检索,取回和问题最相关的 Top-K 条文档片段。这里的 K 通常取 3 到 5。然后把这些片段按顺序拼进一个提示词模板,模板大概是:"你是严谨的助手,请仅根据以下资料回答问题……"再附上资料原文,最后把整个 Prompt 交给大模型生成。

这里有一个极容易忽略、但杀伤力极强的细节:在线查询时必须复用离线索引用的同一个 Embedding 模型。如果离线用 A 模型,在线用 B 模型,两套向量空间根本不兼容,检索结果会直接废掉。我见过不止一次,有人把离线用的 Embedding 和在线查询的 Embedding 配置成了不同版本,结果检索相似度一塌糊涂,还查了半天找不到原因。

3.3 一套最小可用 RAG 的代码骨架

下面用一个极简示例把整个流程串起来。为了降低依赖,我直接用 Chroma 作为本地向量库,嵌入和生成都用 Ollama 拉起的本地模型。这段代码不是生产级的,但足够让零基础的人看到 RAG 的全貌。

import chromadb from chromadb.utils import embedding_functions # 1. 用 Ollama 上的嵌入模型生成向量 ef = embedding_functions.OllamaEmbeddingFunction( url="http://localhost:11434/api/embeddings", model_name="bge-m3" ) client = chromadb.PersistentClient(path="./demo_rag_db") collection = client.get_or_create_collection("docs", embedding_function=ef) # 2. 把切好的文本块写入向量库,带上来源作为 metadata chunks = ["请假需提前一天在OA系统提交申请。", "报销单须附发票原件,金额超过500元需部门总监审批。"] collection.add( ids=["chunk_001", "chunk_002"], documents=chunks, metadatas=[{"source": "company_manual.pdf"}, {"source": "company_manual.pdf"}] ) # 3. 问答时检索 Top-K query = "我想请假,具体应该怎么做?" results = collection.query(query_texts=[query], n_results=3) # 4. 把检索结果拼进 Prompt,交给 Ollama 上的大模型生成 from openai import OpenAI llm_client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama") context = "\n".join(results["documents"][0]) prompt = f"请仅根据以下资料回答用户问题:\n{context}\n\n问题:{query}" resp = llm_client.chat.completions.create( model="qwen2.5:7b", messages=[{"role": "user", "content": prompt}] ) print(resp.choices[0].message.content)

这段代码里,我故意把检索器和生成器的边界画得很清楚:Chroma 负责检索,是大模型的外接记忆;Ollama 上的本地模型负责生成,参数全程不变。以后想换大模型,只需要改 model_name 这一行;想换知识内容,只需要改 collection 里的 documents。先跑通这个最小闭环,再谈优化。

4. 检索质量才是 RAG 的上限:切块、Embedding、重排、上下文压缩的取舍

4.1 RAG 效果差,八成是检索环节的锅而不是大模型的锅

这句话我想先说在前面:当 RAG 给出的答案不理想时,绝大多数情况下问题不在生成阶段,而在检索阶段。要么该找到的资料没找回来,要么找回来的资料根本不对。很多人一上来就调大模型的 Prompt,调了半天发现没用,原因就是没意识到大模型只是照着材料写作文,材料不对,作文不可能对。

所以我做 RAG 项目时的第一步,永远是单独检查召回结果:把同一个问题拿到检索环节单独跑一遍,看 Top-K 返回的片段到底相不相关。这一步能定位掉八成以上的"RAG 效果不好"问题。如果召回结果全是无关内容,那就别去折腾提示词了,去调整切块策略、Embedding 模型和重排序方案。

4.2 切块策略直接影响答案完整性

切块大小是最容易见效的调优点。块太小,比如 200 个字符,上下文碎片化严重,可能把关键流程描述拦腰切断;块太大,比如 2000 个字符,大量无关信息会混进 Prompt,向量表示也会被平均化,检索精度反而下降。除了大小,切块方式也有讲究:完全按固定长度切最简单,但会截断语义;按段落切更自然,但段落长度差异很大;按语义切最理想,但需要额外的模型参与。

我的上线建议是:先用 500 字符固定长度、100 字符重叠起步,配合一批真实问题做评测,再逐步换成按标题和段落结构切。注意,不要在小数据集上凭感觉反复试参,拿真实问答对去测,用指标说话,才是唯一的评判标准。

4.3 Embedding 和向量数据库选型的实际标准

Embedding 模型决定了文本语义表达的质量。中文场景下,我实测过几种方案:OpenAI 的 Embedding 接口效果不错,但数据要出本地;本地化场景我更偏爱 BGE、bge-m3 这类开源中文模型,支持长文本,效果也稳定。判断一个 Embedding 好不好,不能只看宣传指标,要拿你自己的领域文本跑相似检索,看 Top-K 能不能把该命中的都命中。

向量数据库方面,小项目用 Chroma 或 FAISS 足够了,起步快、上手简单;要上生产、要支持高并发,再考虑 Milvus、Qdrant;如果公司内部已经在用 PostgreSQL,pgvector 也是零额外运维的稳妥选择。说到底,向量库只是一个基础设施,在数据量没过百万级之前,性能差异没有想象中那么大,真正影响效果的是前面切块和 Embedding 的质量。

4.4 重排序、混合检索和上下文压缩

召回不是终点。向量检索的"相似"是向量距离上的接近,跟"这个问题是这个意思"并不完全等价。比如用户问"年假没用完怎么办",召回的片段可能包含"休假制度适用范围"这样沾边但没答到点上的内容。为了处理这种表面相似、实际不对的情况,现在主流方案是混合检索:BM25 关键词检索和向量检索并行,再用一个重排序模型(Reranker)把两路结果合并排序,挑出真正相关的片段。

重排序这一步在我的项目里几乎是标配,它能明显拉开效果差距。检索内容太多时,还要做上下文压缩:只保留与问题最相关的句子,去掉重复内容,或对长原文做摘要。上下文窗口再大也有限,把最关键的依据喂给大模型,比把十个片段全塞进去更有效。

5. RAG 和微调到底怎么选?"外挂知识"和"条件反射"是两码事

5.1 微调改变的是模型自身,RAG 改变的是模型输入

大模型微调,是在预训练模型基础上用一批格式化数据继续训练,把领域知识、回答风格、输出格式刻进模型参数里。这个做法适合解决"模型的表达习惯不符合业务要求"的问题,比如让模型学会输出 JSON 结构、学会专业术语的表达方式、模仿特定客服语气。但拿它来灌入频繁更新的业务文档,性价比就很低了:训练一批数据动辄需要准备语料、调超参、跑几十小时,而且一旦业务文档有更新,又得重头来一遍。

RAG 则完全不动参数,知识存放在外部向量库里,每次问答时动态注入。两者之间的差别,可以这样记:微调是让大模型形成条件反射,RAG 是给大模型递小抄。条件反射解决的是"反应习惯",小抄解决的是"这次考试到底考什么"。

5.2 选型决策表:什么样的需求选什么方案

这里给一张我自己经常用的决策表。它不是绝对标准,但能帮你在需求评审时快速对齐方案:

需求特征推荐方案原因
知识更新频繁、需要追溯原文RAG改文档只需重建索引,不用重训模型
答案必须给出出处和依据RAG可以返回命中文档 id 和原文片段
回答风格、输出格式需要固化微调直接改变模型输出偏好和结构
领域术语极多,想让模型更理解业务先 RAG,后续再微调先用检索补事实,再让模型习惯领域表达
私有数据不能出内网RAG + 本地模型数据不走第三方 API
想让模型具备某种全新综合能力微调需要调整的是能力本身,不是单次输入

这张表是我在做项目立项时和团队一起定的,实际用下来,比争论"RAG 好还是微调好"省力得多。因为一旦把场景特征摆出来,答案是自动浮出来的,不需要站队。

5.3 最常见的落地路径:RAG 打底,微调做增强

个人观点:对多数企业场景,正确路径是先跑通 RAG,解决"模型不知道"的现状;再根据线上数据判断是否需要微调,解决"模型说话不合口味"的问题。这两者不是二选一的对立面,而是可以叠加的组合拳。

我做过一个行业客服机器人,当时的改造经历很典型:先接 RAG,解决了大概八成左右的事实型问题答案准确率;接着对输出格式做了轻量微调,用了八百多条样本,让所有回答统一成"结论 + 依据 + 操作建议"三段式。这次微调用单卡训练了不到两小时,量级跟全量预训练完全不可同日而语,但线上体验提升非常明显。先检索补事实,再微调改表达,两者各管一段,边界很清楚。

6. 本地跑一套能改的 RAG:从 Ollama、Dify 到 LangChain4j、vLLM 的落地路径

6.1 为什么本地 RAG 成了热词

最近很多团队把本地 RAG 挂在嘴边,原因很现实:数据合规、API 调用成本、网络稳定性。企业内部制度、客户资料、源码文档,这些数据往往不允许跑到第三方模型 API 上。本地部署大模型加本地向量库,相当于把整个问答系统关在自家网络里,数据不用出域,模型随时可以换。对个人开发者来说,本地 RAG 还有一层意义:可以低成本反复试错,把 RAG 的原理彻底折腾明白。

6.2 Ollama:把模型的下载和运行变得足够简单

Ollama 现在的热度非常高,因为它把本地跑大模型的门槛降到了很低。你不用手动配置 Python 环境、不用处理 PyTorch 和 CUDA 依赖,一条命令就能把模型拉下来,并暴露一个 OpenAI 兼容接口。我的起步配置通常是:Ollama 跑一个 7B 级别的模型用于生成,再跑一个 Embedding 模型用于向量化。机器配置不用太豪华,16G 内存的 MacBook 或者一张消费级显卡就能跑得动。唯一的注意点是模型量级越大响应越慢,本地体验用 7B 到 14B 级别比较合适,再大就建议上服务器了。

6.3 Dify、LangChain4j、vLLM 各自适合什么场景

Dify 是低代码平台里很受欢迎的一个,自带知识库、工作流、模型管理,可以很直观地看到"检索、拼接、生成"每一步发生了什么。我第一次带非技术背景的同事理解 RAG,就是直接在 Dify 上拖流程看效果。LangChain4j 则是 Java 生态的选择,适合已经在用 Spring Boot 做企业应用、不想为了 RAG 再引入一套 Python 技术栈的团队。它把切块、向量化、检索、Prompt 组装都封装成了 Java API,配置起来很直接。vLLM 则是更偏生产部署的推理引擎,主打高吞吐,适合把 RAG 后面的大模型生成环节做成高并发服务。

这三个工具不是三选一的关系。Dify 适合快速原型和非技术团队,LangChain4j 适合 Java 工程体系,vLLM 负责把跑通的链路推向生产环境。选型的时候别迷信某一款,先看自己团队的生态在哪条语言线上。

6.4 一套零基础可复制的 RAG 起步配置

如果今天有人问我,从零开始做一套本地 RAG 知识库,最省力的组合是什么,我会给这个清单:文档清洗用 Python 脚本,或者直接用 Dify 自带的解析器;切块大小 500、重叠 100;Embedding 用 Ollama 上的 bge-m3;向量库用 Chroma;生成模型用 Ollama 上的 Qwen2.5-7B;编排逻辑先用上面第 3 节的 Python 骨架跑通,如果想更省事,直接换 Dify 可视化搭建。

整套组合只需要一台能跑 Ollama 的机器,成本几乎为零。跑通之后,再去考虑要不要引入 Milvus、要不要加重排序、要不要上 vLLM 并发。先把链条走通,比一步到位选型重要得多。我在好几个项目里都看到过这种情况:团队花了两周讨论技术选型,最后发现数据量根本到不了需要分布式向量库的量级,白白浪费了时间。

最后分享一下自己这几年的操作习惯:每当我决定给一个业务场景做 RAG,都会先准备二十到五十条真实问题作为评测集,然后固定大模型不动,单独调检索链路,记录每个问题能不能在 Top-K 召回里看到有效依据。这个过程很枯燥,但对效果提升的帮助比任何玄学调参都大。RAG 上线之后,一定要把"检索不到正确答案"的失败案例记录下来,它们往往是后续优化切块策略、补充同义词、调整重排序权重的最佳素材。RAG 和大模型的关系,说到底就一句话:大模型负责把话说明白,RAG 负责让它的每句话都有凭有据。想清楚这一层,剩下的事情都是工程上的耐心活。