1. RAG基础认知:什么是RAG、RAG与微调的区别、RAG的核心流程
大家好,我是这门课的主讲人。今天咱们来聊聊RAG——检索增强生成。说实话,我第一次接触这个概念时,也觉得它不过是给大模型配了个“搜索引擎”。但真正用起来才发现,这里面的门道比想象中深得多。
1.1 什么是RAG?
RAG,全称Retrieval-Augmented Generation。翻译过来就是“检索增强生成”。说白了,就是让大模型在回答问题之前,先去外部知识库“查资料”,然后再基于查到的资料来生成答案。
为什么要这么做?你想想看,大模型虽然知识渊博,但它有个硬伤——知识是静态的。训练完那一刻,它的知识就定格了。你问它“今天天气怎么样”,它只能瞎猜。但RAG不一样,它先去查今天的天气预报,再回答你。
核心思想:不依赖模型内部参数存储的知识,而是从外部知识源实时检索相关信息,作为生成答案的上下文。
我在项目中遇到过这样一个场景:客户想做一个企业内部知识库问答系统。如果只用微调,每次文档更新都得重新训练模型,成本太高。用RAG就简单了——文档更新后,重新索引一遍就行,模型本身不用动。
1.2 RAG与微调的区别
很多新手会问:“RAG和微调,到底选哪个?”嗯,这个问题我当年也纠结过。咱们直接看对比:
| 维度 | RAG | 微调(Fine-tuning) |
|---|---|---|
| 知识更新 | 实时更新,改文档即可 | 需要重新训练,周期长 |
| 训练成本 | 几乎为零(只需索引) | 高,需要GPU和大量数据 |
| 幻觉控制 | 强,答案有据可查 | 弱,模型可能胡编乱造 |
| 适用场景 | 知识问答、文档检索、实时信息 | 风格模仿、指令遵循、特定任务 |
| 模型能力 | 依赖基座模型本身 | 可以提升特定领域能力 |
我个人的习惯是:能用RAG解决的问题,绝不微调。为什么?因为微调就像给模型“动手术”,搞不好会破坏它原有的能力。我曾经见过一个团队,微调后模型在目标任务上表现不错,但写诗的能力却退化了——这就是“灾难性遗忘”。
我的建议:如果你需要模型学习新的知识(比如公司内部文档),用RAG。如果你需要模型改变回答风格或遵循特定指令(比如变成客服语气),用微调。两者也可以结合使用。
但注意,RAG不是万能的。它依赖检索质量,如果检索不到相关信息,模型还是会瞎编。我曾经踩过这个坑——索引没做好,用户问“公司年假政策”,结果检索到的是去年的版本,答案自然就错了。
1.3 RAG的核心流程:索引-检索-生成
RAG的流程,说白了就三步:先存好资料,再找到相关的内容,最后让模型回答。咱们一步步拆解。
1.3.1 索引(Indexing)
索引就是把你的文档“切碎”并“编码”,方便后续快速查找。具体来说:
- 文档切分:把PDF、Word、网页等原始文档,切成一个个小片段(chunk)。我一般切成256-512个token一段,太长了检索不准,太短了上下文不够。
- 向量化:用嵌入模型(比如text-embedding-ada-002)把每个片段转成向量。说白了,就是把文字变成一串数字。
- 存储:把向量和原始文本存到向量数据库里(比如FAISS、Pinecone、Milvus)。
# 伪代码示例:索引流程
documents = load_documents("company_handbook.pdf")
chunks = split_text(documents, chunk_size=512)
vectors = embedding_model.encode(chunks)
vector_store.add(vectors, chunks)
注意:切分策略很关键。我曾经试过按固定长度切分,结果把一句话从中间切断了,检索时语义不完整。后来改用按段落切分,效果好很多。
1.3.2 检索(Retrieval)
用户提问后,系统会做两件事:
- 问题向量化:用同样的嵌入模型,把用户的问题也转成向量。
- 相似度搜索:在向量数据库里找最相似的几个片段。通常用余弦相似度或欧氏距离。
我习惯检索top-3到top-5个片段。太少可能信息不全,太多又会把无关内容塞进上下文,干扰模型判断。
# 伪代码示例:检索流程
query_vector = embedding_model.encode("公司年假政策")
results = vector_store.similarity_search(query_vector, k=3)
# 返回最相似的3个文档片段
1.3.3 生成(Generation)
最后一步,把检索到的片段和用户问题一起,拼成一个完整的提示词(prompt),交给大模型生成答案。
# 伪代码示例:生成流程
context = "\n".join(results)
prompt = f"""
基于以下资料回答问题:
资料:
{context}
问题:{user_query}
请给出准确、简洁的回答。
"""
answer = llm.generate(prompt)
这里有个细节:提示词的设计。我刚开始做的时候,直接把资料和问题丢给模型,结果模型经常忽略资料,自己瞎编。后来我在提示词里加了“请严格基于资料回答,如果资料中没有相关信息,请说不知道”,效果立竿见影。
避坑指南:我曾经遇到过检索到的资料和问题不相关,模型还是强行回答的情况。后来我加了一个“相关性过滤”步骤——如果检索结果的相似度低于某个阈值(比如0.7),就直接告诉用户“没有找到相关信息”。
小结
好了,咱们总结一下:
- RAG是什么:让模型先查资料再回答,解决知识静态和幻觉问题。
- RAG vs 微调:RAG适合知识更新频繁的场景,微调适合改变模型行为。我建议优先用RAG。
- 核心流程:索引(切分+向量化+存储)→ 检索(向量搜索)→ 生成(拼接提示词+模型回答)。
下一章,咱们会深入讲索引阶段的细节——文档切分到底怎么切才最合理?向量化模型怎么选?到时候我会分享一些我踩过的坑和总结的经验。
嗯,今天就到这儿。有问题欢迎在评论区交流。