共计 1940 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点:为什么需要 RAG?
大语言模型(LLM)虽然表现出色,但在实际应用中仍面临三个核心问题:

- 知识更新滞后 :传统 LLM 的训练数据通常截止于某个时间点(如 GPT- 3 基于 2021 年数据),无法实时获取新知识。
- 事实性幻觉 :模型可能生成看似合理但实际错误的回答(例如虚构历史事件)。
- 领域适应性差 :通用模型在专业领域(如医疗、法律)表现不稳定。
RAG 通过将检索系统与生成模型结合,实现了动态知识注入,成为解决这些问题的银弹。
技术对比:微调 vs RAG
- 微调 (Fine-tuning)
- 优点:模型完全适配特定任务
-
缺点:训练成本高、更新周期长(需重新训练)、存在灾难性遗忘风险
-
RAG
- 优点:零样本适应新领域、知识实时更新、可解释性强(可追溯检索来源)
- 缺点:系统复杂度较高、检索延迟影响响应速度
架构设计
典型 RAG 系统包含三大核心组件:
- 检索器 :将用户查询转换为向量,从知识库中检索相关文档
- 向量数据库 :存储文档的向量化表示,支持高效相似度搜索
- 生成器 :基于检索结果生成自然语言响应
完整 pipeline 流程:
- Query 理解:解析用户意图,可能包括查询扩展 / 重写
- 向量检索:使用 kNN 算法查找相似文档(时间复杂度 O(n))
- 结果重排:按相关性评分排序,可能融合 BM25 等传统 IR 技术
- 上下文构建:将 Top- K 文档拼接为生成器的输入上下文
- 响应生成:LLM 生成最终回答
代码实现(基于 LangChain)
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# 1. 文档加载与分块(建议分块大小 500-1000 字符)loader = TextLoader("knowledge_base.txt")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=200 # 避免上下文断裂
)
texts = text_splitter.split_documents(documents)
# 2. 向量化存储(嵌入维度通常 768-1536)embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(texts, embeddings) # 使用 HNSW 索引加速检索
# 3. 构建检索增强生成链
qa_chain = RetrievalQA.from_chain_type(llm=OpenAI(temperature=0.3), # 降低 temperature 减少幻觉
chain_type="stuff",
retriever=db.as_retriever(search_kwargs={"k": 3}), # 返回 Top3 文档
return_source_documents=True
)
# 4. 提问并获取带来源的回答
result = qa_chain("如何预防网络安全攻击?")
print(result["result"])
print("来源文档:", result["source_documents"])
生产环境优化
检索效率
- 索引选择 :
- FAISS:内存效率高,适合中小规模数据
- HNSW:查询速度快(近似 O(log n)),支持动态更新
- 混合检索 :结合稀疏向量(BM25)和稠密向量提升召回率
上下文管理
- 动态上下文窗口:根据查询复杂度调整输入的 token 数量
- 重要性过滤:使用 BERT 等模型对检索结果进行相关性评分
幻觉缓解
- 一致性校验:比较多个检索结果的关键事实
- 置信度阈值:丢弃低置信度的生成内容(可用 self-check 技术)
避坑指南
- 分块大小 :
- 太小:丢失上下文连贯性
- 太大:噪声干扰生成质量
-
建议通过 A / B 测试确定最佳值
-
冷启动问题 :
- 初始阶段可用通用知识库(如 Wikipedia 数据)作为基础
-
实现渐进式更新机制
-
多轮对话 :
- 维护对话历史向量缓存
- 在检索时融合当前 query 和历史上下文向量
开放性问题
在实践中,我们发现检索范围(search_k 参数)与生成质量存在 trade-off:
– 扩大检索范围:可能引入无关噪声
– 缩小检索范围:可能遗漏关键信息
一个可行的方向是开发智能检索调度器,根据 query 复杂度动态调整检索策略。您在实践中是如何平衡这对矛盾的呢?
正文完
发表至: 未分类
近一天内
