先聊一个常被问起的问题:大模型知识更新慢、还容易一本正经地胡编,这种情况怎么破?业界给出的主流答案就是RAG(Retrieval-Augmented Generation,检索增强生成)。RAG的核心思路并不玄乎——给大模型外挂一个可实时查询的资料库,回答问题时先检索相关资料,再让模型基于资料组织回答。这也是目前落地成本最低、迭代最快的知识增强方案之一。
这篇文章面向刚接触大模型应用开发的朋友,从原理到代码、从选型到调优,把RAG这条链路完整走一遍。你可以把它当作一份可以反复查阅的手册,跟着实操一遍后,基本能理解“知识库问答”这类应用是怎么从零搭起来的。
1. 为什么大模型需要RAG:知识过期、幻觉与私有数据
先说一个反直觉的事实:很多大模型并不知道自己“不知道”。你问它训练截止日期之后发生的事,它可能靠预测下一个字的能力编出一个看似合理的答案,这就是行业里常说的“幻觉”。模型的底层能力是语言生成,而不是真实世界的知识检索,因此当问题超出它的记忆范围时,它只能“猜”。
除了知识过期和幻觉,还有一类更现实的需求——私有数据。几乎每家公司、每个团队手里都有大量内部文档、产品手册、历史工单,这些内容不会出现在任何公开训练的语料里。想让模型懂这些内容,主要有三条路:
| 方案 | 成本 | 更新速度 | 可解释性 | 适用场景 |
|---|---|---|---|---|
| 全量重训 | 很高,需要大量算力和数据 | 很慢,每次更新都要重来 | 一般 | 大厂做基础模型 |
| 微调(Fine-tuning) | 中等,需要标注数据和训练环境 | 比重训快,但每次改动仍要训练 | 一般 | 固定格式或输出风格调整 |
| RAG | 低,只需要整理文档和搭建检索 | 快,换文档或加文档立刻生效 | 高,回答可溯源到资料片段 | 私有知识库问答、实时信息问答 |
我自己在接需求时有一个判断标准:如果目标是让模型掌握某种固定的输出风格或特定能力,考虑微调;如果目标是让模型“知道某些事”,优先RAG。原因很简单——RAG的更新成本太低,改一份文档比重新训练一次划算太多,而且回答问题时能把对应资料片段展示出来,用户信任度也高。
用生活化的方式来理解:大模型像一位知识面很广但记性不好的新同事,RAG就是配给他的一部不断更新的工作手册。每次被问到业务问题,先翻手册找到相关章节,再看章节内容作答。手册更新了,他的知识就跟着更新了,不需要重新“培训”这个人。
2. RAG跑起来的四个环节:索引、检索、增强、生成
理解RAG最好的方式,是把整条链路拆成四个阶段。这四个阶段一旦分开看,每一步的逻辑都很简单。
2.1 索引阶段:把资料变成可搜索的向量库
原始文档没法直接让模型“翻”,所以第一步要先把文档处理成系统能快速查找的形式。具体做三件事:加载文档、按规则切片、对每个切片做向量化(embedding)。
切片这个步骤经常被忽略,但它直接决定后续检索质量。切片太大,一个片段里塞太多主题,检索时会出现“查到了文档但说不清具体在哪”的尴尬;切片太小,语义信息被切碎,关键的上下文可能丢掉。我常用的起始参数是每片500字左右,相邻切片之间留50字的重叠,这个组合在大多数通用场景下表现比较稳。
向量化是整个RAG的地基。嵌入模型会把一段文字转换成一串浮点数(也就是向量),语义相近的文字在向量空间里的距离更近。比如“退货流程”和“七天无理由退款”虽然用词不同,向量距离却很近,这样用户在问“怎么退款”的时候,系统能找到写“退货流程”的片段。
2.2 检索阶段:把问题也变成向量,找最相关的片段
用户的提问进来之后,要做和文档一样的事情——用同一个嵌入模型把问题向量化。然后拿这个向量去向量库里做相似度搜索,找出最接近的Top-K个文档片段。
这里有一个关键点:问题向量和文档向量必须由同一个嵌入模型生成,否则向量空间不一致,相似度计算就没有意义。这也是新手比较容易踩的坑,换模型之后忘记重新处理入库数据的问题。
2.3 增强阶段:把检索结果拼进Prompt
找到相关资料后,把它们拼接成一段上下文,塞进发给大模型的Prompt里。Prompt的长相大致如下:
请根据以下资料回答问题。 如果资料中没有足够的信息,请明确回答“资料中未找到相关信息”,不要编造。 相关资料: [片段1] [片段2] [片段3] 问题:用户的提问 回答:这一步千万不能省略“没有足够信息就直说”这个约束。加一句话,幻觉出现的概率会显著下降;不加这句话,模型哪怕资料里没有相关内容,也倾向于硬编一个答案出来。
2.4 生成阶段:模型基于资料作答
最后一步反而是最轻松的——大模型根据带上下文的Prompt生成回答。生成的质量上限由两个因素决定:检索到的片段是否真的相关,以及Prompt的约束是否清晰。
检索不准,后面生成再强也白搭,这就是为什么“调优RAG,优先调检索”几乎是所有实操者的共识。
3. 环境准备与工具选型:小白最容易耗时间的地方
与其一上来就堆工具清单,不如先明确一条主线:你要把文档变成向量,存进向量库,然后查出来喂给模型。围绕这条主线,每个环节只需要选一个趁手的工具就能跑通。
3.1 运行环境
Python 3.10以上版本比较省心。强烈建议用虚拟环境隔离依赖,不要直接装在系统环境里——RAG涉及的库很多,依赖冲突是新手最常遇到的启动障碍。我见过太多人卡在“某个包装不上”这一步,最后发现是全局环境里已经有旧版本在打架。
3.2 嵌入模型怎么选
嵌入模型负责把文字变成向量,选择上主要看两条路:
- 在线API方式:调用云端嵌入接口,效果好、免维护,适合快速验证想法。
- 本地开源模型:完全免费、数据不出内网,适合有隐私要求或长期稳定运行的场景。很多开源嵌入模型在中文场景下效果也足够好,我自己的不少项目就是纯本地模型搭建。
起步阶段建议先用在线API把流程跑通,验证业务效果;确认可行之后,再评估是否迁移到本地模型以降低成本。
3.3 向量数据库怎么选
向量数据库的作用是存储向量并支持快速检索。按部署形态大致分三类:
| 类型 | 特点 | 适合场景 |
|---|---|---|
| 轻量内存型 | 安装简单、数据量不大时速度快 | 个人项目、原型验证 |
| 服务型数据库 | 支持持久化、并发访问、数据管理 | 团队协作、准生产环境 |
| 云托管服务 | 免运维、弹性扩展 | 大规模生产、多端接入 |
个人跑Demo,最轻量的方案是内存型库,整个系统跑起来只需要几个pip命令。等文档量大到一定程度,再换正式的服务型数据库。这个升级路径很平滑,业务代码不用大改。
3.4 编排框架:用还是不用
市面上有不少大模型应用编排框架,自动把加载、切分、向量化、检索、Prompt拼接串起来。我个人的看法是:要用,但要先搞懂框架背后做了什么。
框架能显著缩短搭建时间,但也会屏蔽掉底层细节。如果出了问题,你不知道是切分的问题还是检索的问题。我的建议是先手动跑通一遍核心逻辑,理解每个环节,再用框架提高效率。这样框架对你就是工具,而不是黑盒。
4. 动手搭一个最小可用的本地问答系统:核心代码拆解
现在进入实操环节。我们做一个简单的“员工手册问答机器人”:把一份虚构公司的员工手册文档做成知识库,让用户用自然语言提问,系统从手册中检索答案并生成回答。
4.1 准备示例文档
先用一份Markdown格式的员工手册作为测试资料。内容可以包含考勤制度、休假规则、报销流程等几个主题。注意:文档内容尽量分节清晰,这样切分后的片段质量更高。
4.2 核心流程示意代码
下面用结构化伪代码展示RAG主流程。每一步都对应前面讲的索引、检索、增强、生成四个环节。
# 步骤1:读取文档 # 把本地文档加载进来,保持文本完整性 doc_text = load_file("employee_manual.md") # 步骤2:切片 # 按固定大小切分,并保留重叠部分,避免语义被切断 chunks = split_text( doc_text, chunk_size=500, # 每片约500字 chunk_overlap=50 # 相邻两片重叠50字 ) # 步骤3:向量化 # 对每个切片调用嵌入模型,得到一组向量 chunk_vectors = [embed(chunk) for chunk in chunks] # 步骤4:写入向量库 # 把切片原文和对应向量一起存入向量库 vector_store.add( ids=[f"chunk_{i}" for i in range(len(chunks))], vectors=chunk_vectors, texts=chunks ) # 步骤5:检索 # 用户提问后,把问题向量化,再查最相近的Top-3片段 query = "新员工年假怎么计算?" query_vec = embed(query) results = vector_store.search(query_vec, top_k=3) # 步骤6:构造增强Prompt prompt = build_prompt( system="请严格根据资料内容回答,资料中没有的信息请直接说不知道", context="\n".join(results), question=query ) # 步骤7:调用大模型生成回答 answer = call_llm(prompt) print(answer)# 上面的build_prompt内部逻辑示意 def build_prompt(system, context, question): return f""" {system} 参考资料: {context} 用户问题: {question} 请根据参考资料回答,若资料中无相关信息,请明确说明。 """这段代码虽然用的是伪函数名,但每一步都是真实RAG系统会执行的动作。你完全可以用自己熟悉的三方库把load_file、embed、vector_store.search替换成对应实现,逻辑完全一致。
4.3 跑通后的第一轮测试
搭好之后,我习惯先问三个类型的问题:
- 资料内直接有答案的:“员工每年有几天年假?”——验证基本检索能力。
- 需要跨片段整合的:“休假之前需要提前几天申请?”——如果切片把申请流程拆开了,要看检索能否把相关片段都捞出来。
- 资料里没有的:“期权怎么分配?”——验证系统能否克制地承认“不知道”。
第一轮测试只要第1类问题答得不错,就算及格。第2、3类问题的表现,是后面调优的空间。如果第1类都答不好,先别急着调Prompt,优先检查切分和检索——大概率是相关片段根本没被搜出来。
5. 检索质量调优:切片怎么切、嵌入模型怎么选、重排序要不要加
很多RAG系统“能用但不好用”,瓶颈几乎都出在检索环节。下面这些调优手段是我实测下来性价比最高的几项。
5.1 切片策略:不要迷信固定值
切片大小是检索质量的第一个杠杆。试一下这组对比就明白了:
| 切片大小 | 表现 | 问题 |
|---|---|---|
| 200字 | 检索精确度高,但上下文不完整 | 片段语义碎片化,模型可能缺乏背景 |
| 500字 | 平衡较好 | 多数场景下的稳妥起点 |
| 1000字以上 | 上下文完整,但噪声多 | 一个片段涵盖多个主题,相关性被稀释 |
更好的做法是按文档结构切,而不是只按字数切。比如Markdown里的每个二级标题或三级标题之下作为一个语义块,再结合最大长度限制做二次切分。这样每个片段本身就是一个完整主题,检索命中率明显更高。
5.2 嵌入模型选型:先用通用的,再按领域验证
对大多数起步项目,用主流的通用嵌入模型就够了,它们对中文的支持已经很成熟。真正需要对比测试的,是你所在领域的专业术语是否被正确编码。我的做法是准备一组领域内的高频问题,分别用不同嵌入模型建库、检索,人工看Top-3命中率。不必看复杂的指标,肉眼看结果就能分出高下。
5.3 混合检索与重排序
向量检索擅长语义相似,但对精确关键词未必敏感。比如用户问“MAC地址怎么查”,向量检索可能返回一堆关于网络配置的片段,反而不如直接关键词匹配精准。
比较成熟的方案是混合检索:同时做向量检索和关键词检索(BM25这类经典算法),把两路结果合并去重后,再用重排序模型(Reranker)对候选片段做精细打分。重排序模型会把“用户问题”和“候选片段”一起输入,综合判断相关性,效果通常比单纯向量相似度要好一个档次。
代价是增加了一次模型推理的时间和成本。我的建议是:候选集不大时加上重排序,候选集几百条以上时先靠Top-K粗筛,再对Top-20做重排序取Top-3,质量和性能兼顾。
5.4 Top-K和相似度阈值
Top-K决定模型最多看几片资料。K太小容易漏信息,K太大容易塞进无关内容、反而干扰回答。我一般先从3开始,回答质量不满意就上调到5,上限很少超过7。
相似度阈值用来过滤明显不相关的片段。建议阈值不要设太高,否则有些相关但表达方式差异较大的片段会被误杀。先观察一批正常问题的相似度分布,再在分布的低位附近设阈值,比拍脑袋设一个数值靠谱得多。
6. 评估与上线前的检查:用二十个问题测出系统真实水平
RAG系统最容易出现的情况是“演示时很完美,一换场景就翻车”。因为演示问题通常是精心挑过的,覆盖不到真实用户的刁钻提问。我习惯在建完系统后,专门花半小时做一套简单但有效的评估。
6.1 准备一套测试问题集
从业务方那里收集过去真实被问过的问题,整理成20条左右。分成四类:
- 简单直接题:资料里明确写了答案的
- 综合归纳题:需要综合多个片段才能回答的
- 模糊提问题:用户表达不精确,但资料里有对应内容的
- 超纲题:资料里根本没有答案的
每类5条左右。这套问题集的价值在于可复用——每次改动切分参数、换模型、改Prompt之后,重跑一遍,立刻知道改动是变好还是变坏。
6.2 三个核心评估维度
在人工评估时,主要看三件事:
- 答案相关性:回答是否匹配问题,有没有答非所问。
- 忠实度:回答内容是否能从检索到的资料片段中找到依据。找不到依据的表述越多,说明幻觉越严重。
- 检索召回率:针对每个问题,看Top-3检索结果里是否包含了真正有用的片段。这一步能帮你区分问题是出在“没检索到”还是“没用好”。
6.3 一个简单可复现的检查流程
我每次评估的动作是:记录每个问题检索返回的Top-3片段标题(或片段首句),再记录模型最终回答,然后对照人工标注的预期答案来源片段。整个过程不用任何复杂工具,一张表格就够。
6.4 上线前容易踩的实战坑
最后列几个我踩过、也见别人踩过的高频问题:
- 文档解析乱码:PDF转文本后经常出现排版混乱或乱码,强烈建议优先用Markdown、TXT这类纯文本格式,解析可靠性最高。
- 编码问题:处理中文文档时,统一用UTF-8,避免出现读取半个汉字导致检索质量下降。
- 增量更新没清旧数据:往向量库追加新文档时,旧的过期文档如果没有删除,会被重复检索到。每次更新前先做好版本管理。
- Prompt缺少约束:没有明确告诉模型“资料中没有就说不知道”,幻觉概率会显著上升。这一句话的约束力远比你想象的大。
- 只测一个文档:单个文档建出来的系统不能代表真实场景,至少用三个不同风格的文档测试,才能暴露切分策略的问题。
根据我个人的实际体会,RAG项目最值得投入时间的环节,永远是数据整理和检索调优。模型生成能力目前已经很强,真正拉开系统体验差距的,是你能不能把正确的内容送到模型眼前。
这套流程看起来环节多,但核心主线就一条:把资料变成向量,检索出来,交给模型。第一次完整跑通之后,后续加文档、换模型、调参数都会顺畅很多。收藏这篇文章的同时,更建议你马上拿一份自己的文档,把最小系统搭起来跑一遍。在跑的过程中遇到的具体问题,往往比任何教程都更能让你理解RAG的脾性。