在构建基于检索增强生成(RAG)的系统时,我们需要深入理解其核心架构和工作原理。RAG系统本质上是通过结合信息检索与生成模型优势的混合架构,能够有效解决纯生成模型容易产生幻觉(hallucination)的问题。下面我将从系统层面拆解RAG的核心组件。
知识更新环节是RAG系统的基石,其质量直接决定了后续检索和生成的效果。这个环节包含以下关键步骤:
数据预处理实战要点
python复制from PyPDF2 import PdfReader
def extract_text_from_pdf(pdf_path):
text = ""
with open(pdf_path, "rb") as f:
reader = PdfReader(f)
for page in reader.pages:
text += page.extract_text() + "\n"
return text
python复制import chardet
def detect_encoding(file_path):
with open(file_path, 'rb') as f:
result = chardet.detect(f.read())
return result['encoding']
文本分块最佳实践
关键提示:分块大小需与后续使用的embedding模型上下文窗口匹配。例如使用BERT系列模型时,建议不超过512token
选择适合的embedding模型是RAG系统的关键决策点。以下是主流方案对比:
| 模型类型 | 代表模型 | 维度 | 适合场景 | 计算成本 |
|---|---|---|---|---|
| 通用模型 | BERT-base | 768 | 通用文本 | 中等 |
| 专用模型 | bge-small | 384 | 检索优化 | 较低 |
| 多语言模型 | paraphrase-multilingual | 768 | 多语言场景 | 较高 |
| 领域适配 | scibert | 768 | 学术文献 | 中等 |
实际项目中,我推荐使用HuggingFace的SentenceTransformer库:
python复制from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-small-en-v1.5')
embeddings = model.encode(texts, normalize_embeddings=True)
向量存储方案对比
小型项目可以使用FAISS实现快速原型:
python复制import faiss
import numpy as np
dimension = 384 # 匹配embedding维度
index = faiss.IndexFlatIP(dimension)
faiss.normalize_L2(embeddings)
index.add(embeddings)
原始查询往往信息不足,需要进行智能处理:
查询重写技术
示例实现:
python复制from nltk.corpus import wordnet
def expand_query(query):
synonyms = set()
for word in query.split():
for syn in wordnet.synsets(word):
for lemma in syn.lemmas():
synonyms.add(lemma.name())
return query + " " + " ".join(synonyms)
单一向量检索可能遗漏关键词匹配的重要文档,建议采用混合策略:
BM25实现示例:
python复制from rank_bm25 import BM25Okapi
corpus = ["doc1 text", "doc2 text", ...]
tokenized_corpus = [doc.split() for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)
query = "example query"
tokenized_query = query.split()
doc_scores = bm25.get_scores(tokenized_query)
初步检索结果需要精细排序,常用方法:
跨编码器重排序
python复制from sentence_transformers import CrossEncoder
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
scores = reranker.predict([(query, doc) for doc in candidates])
多样性优化
构建有效的prompt模板需要包含:
优质prompt示例:
code复制你是一位专业的技术顾问。请基于以下参考内容回答问题。
要求:
1. 答案必须来自给定参考内容
2. 如参考内容不足,明确回答"根据现有资料无法确定"
3. 使用中文回答,保持专业但易懂
参考内容:
{context_str}
问题:{query}
答案:
不同场景下的LLM选型建议:
| 模型类型 | 代表模型 | 适合场景 | 成本 |
|---|---|---|---|
| 通用模型 | GPT-4 | 复杂推理 | 高 |
| 领域模型 | Med-PaLM | 专业领域 | 中 |
| 轻量模型 | Phi-3 | 边缘设备 | 低 |
| 开源模型 | LLaMA3 | 私有部署 | 可变 |
生成内容需要验证:
自动化验证脚本示例:
python复制def validate_answer(answer, context):
# 简单的事实一致性检查
overlap = set(answer.split()) & set(context.split())
return len(overlap) / len(answer.split()) > 0.3
缓存策略
异步处理
关键监控指标建议:
| 指标类别 | 具体指标 | 健康阈值 |
|---|---|---|
| 检索质量 | Top-k准确率 | >80% |
| 生成质量 | 事实一致性 | >90% |
| 性能 | 端到端延迟 | <2s |
| 资源 | GPU利用率 | <80% |
检索效果差
生成内容不相关
系统延迟高