共计 1450 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点:为什么需要 RAG?
大语言模型(LLM)在知识密集型任务中表现出色,但它们存在两个主要问题:

- 知识时效性差:LLM 的训练数据往往截止于某个时间点,无法获取最新信息。
- 事实准确性不足:LLM 容易产生幻觉(hallucination),生成看似合理但实际错误的内容。
检索增强生成(RAG)通过引入外部知识库,动态检索相关信息作为生成上下文,有效解决了这些问题。
技术选型:主流向量数据库对比
选择适合的向量数据库是 RAG 系统的关键。以下是三种主流向量数据库的对比:
| 特性 | Milvus | Pinecone | Weaviate |
|---|---|---|---|
| 延迟 | 低(毫秒级) | 极低(亚毫秒) | 中等 |
| 吞吐量 | 高 | 中等 | 高 |
| 成本 | 自托管成本低 | SaaS 模式较贵 | 混合模式 |
| 适合场景 | 大规模部署 | 快速原型 | 多模态应用 |
核心实现
分块策略对 embedding 质量的影响
- 固定分块:简单但可能切断语义连贯性。
- 滑动窗口:通过重叠分块保留上下文,但会增加存储开销。
- 语义分割:基于内容结构(如段落)分块,效果最好但实现复杂。
多模态 embedding 融合
对于包含文本和结构化数据的场景:
- 分别生成文本和结构化数据的 embedding。
- 使用加权平均或拼接(concatenation)融合两种 embedding。
- 通过实验确定最佳融合策略。
Python 实现示例
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
# 加载文档
loader = TextLoader("data.txt")
documents = loader.load()
# 分块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
splits = text_splitter.split_documents(documents)
# 生成 embedding 并存储
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
db = FAISS.from_documents(splits, embeddings)
# 检索
query = "RAG 系统如何工作?"
docs = db.similarity_search(query)
性能优化
ANN 算法调优
- HNSW 参数 :调整
efConstruction和M参数平衡构建时间和搜索精度。 - IVF 参数 :增加
nlist值提高精度,但会增加内存使用。
缓存策略
- 查询结果缓存:对常见查询结果进行缓存。
- embedding 缓存:缓存频繁访问文档的 embedding。
避坑指南
冷启动数据预热
- 预先加载高频查询的 embedding。
- 实施渐进式索引构建。
处理 OOV 问题
- 使用子词(subword)或字符级 embedding。
- 维护自定义词表。
延伸思考
- 动态分块:根据查询动态调整分块大小。
- 混合检索:结合关键词和向量检索。
- 反馈循环:利用用户反馈优化检索结果。
结语
构建高效的 RAG 系统需要综合考虑多种因素。通过合理的技术选型、优化的实现和避坑经验,可以显著提升系统性能。希望本文能为你的 RAG 实践提供有价值的参考。
正文完
