最近两年,大语言模型(LLM)在各类场景中展现出惊人的能力,但一个明显的短板是:当遇到专业领域问题时,模型往往会给出"一本正经胡说八道"的答案。这正是RAG(Retrieval-Augmented Generation)技术大显身手的地方——通过将外部知识库与LLM结合,让AI的回答既有通用语言理解能力,又具备专业准确性。
我去年为一家医疗科技公司实施的RAG系统,将临床指南文档库接入GPT-4后,医生咨询的准确率从63%提升到92%。这个案例让我深刻认识到:掌握RAG技术正在成为AI工程师的核心竞争力。今天我就用Python带大家从零构建一个完整的RAG系统,包含以下关键模块:
提示:完整代码已打包在GitHub仓库(文末获取),建议边阅读边运行代码。需要提前安装Python 3.9+和至少8GB内存。
为什么选择这样的技术栈?让我们对比几个主流方案:
| 组件 | 备选方案 | 选择理由 |
|---|---|---|
| 向量数据库 | Pinecone, Weaviate | Chroma轻量且开源,适合本地调试;Pinecone更适合生产环境 |
| 嵌入模型 | OpenAI, Cohere | Sentence-Transformer的all-MiniLM-L6-v2模型在精度和速度间取得最佳平衡 |
| 框架封装 | LlamaIndex | LangChain提供更灵活的管道组合方式,便于添加自定义预处理逻辑 |
| LLM接入 | 本地LLM, Anthropic | OpenAI API性价比最高,gpt-3.5-turbo在$0.002/1k tokens的成本下表现足够好 |
典型的RAG查询会经历以下关键步骤:
文档摄取:
向量化处理:
python复制from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer('all-MiniLM-L6-v2')
chunks = ["文本段落1", "段落2"...]
embeddings = embedder.encode(chunks, show_progress_bar=True)
检索增强:
生成应答:
python复制from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(temperature=0)
augmented_prompt = f"基于以下上下文:\n{context}\n\n问题:{query}"
response = llm.predict(augmented_prompt)
文本分块是影响效果的关键因素之一。经过多次实验,我发现这些策略最有效:
python复制from langchain.text_splitter import RecursiveCharacterTextSplitter
class SmartTextSplitter:
def __init__(self):
self.code_splitter = RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=30,
separators=["\n\n", "\n", " ", ""]
)
self.doc_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=80,
separators=["\n\n", "\n", "。", " ", ""]
)
def split(self, text, doc_type="doc"):
if doc_type == "code":
return self.code_splitter.split_text(text)
return self.doc_splitter.split_text(text)
单纯的余弦相似度可能返回无关结果,我通过以下方法提升准确率:
查询扩展:使用SPLADE技术生成相关术语
python复制def expand_query(query):
expanded = query + " " + " ".join(generate_related_terms(query))
return expanded[:512] # 防止过长
混合检索:结合传统关键词搜索
python复制from rank_bm25 import BM25Okapi
class HybridRetriever:
def __init__(self, texts):
self.bm25 = BM25Okapi([t.split() for t in texts])
def search(self, query, k=5):
bm25_scores = self.bm25.get_scores(query.split())
# 与向量分数加权融合...
元数据过滤:比如限定文档时间范围或作者
python复制import chromadb
from tqdm import tqdm
class KnowledgeBaseBuilder:
def __init__(self, persist_path="./chroma_db"):
self.client = chromadb.PersistentClient(path=persist_path)
self.collection = self.client.get_or_create_collection("knowledge_base")
self.embedder = SentenceTransformer('all-MiniLM-L6-v2')
def ingest_documents(self, file_paths):
for path in tqdm(file_paths):
text = self._load_file(path)
chunks = SmartTextSplitter().split(text)
embeddings = self.embedder.encode(chunks)
ids = [f"{path.stem}_{i}" for i in range(len(chunks))]
metadatas = [{"source": path.name}] * len(chunks)
self.collection.add(
ids=ids,
embeddings=embeddings.tolist(),
documents=chunks,
metadatas=metadatas
)
python复制from fastapi import FastAPI
app = FastAPI()
@app.post("/query")
async def handle_query(query: str):
# 1. 检索相关段落
expanded_query = expand_query(query)
query_embedding = embedder.encode(expanded_query)
results = collection.query(
query_embeddings=[query_embedding.tolist()],
n_results=3,
include=["documents", "metadatas"]
)
# 2. 构造增强提示
context = "\n\n".join(results["documents"][0])
prompt = f"""基于以下上下文:
{context}
问题:{query}
请给出专业、准确的回答,如果无法确定请说"根据现有信息无法确定"。"""
# 3. 生成回答
response = llm.predict(prompt)
return {"answer": response}
在真实业务场景中,我总结出这些提升效率的方法:
python复制from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_embed(text):
return embedder.encode(text)
建立这些监控指标确保系统健康运行:
检索质量:
生成质量:
python复制def evaluate_response(true_answer, generated):
# 使用ROUGE或BERTScore等指标
return similarity_score
性能指标:
Q1:中文文档处理效果差?
Q2:检索到无关内容?
print(chunks)调试)Q3:API响应慢?
Q4:如何处理超长文档?
python复制def process_large_doc(text):
summary = llm.predict(f"请用3句话总结以下内容:\n{text[:3000]}")
return summary + "\n\n[详细内容请查阅原始文档]"
当基础系统跑通后,可以尝试这些增强功能:
多模态检索:支持图片、表格等内容
python复制# 使用CLIP模型处理图像
image_embeddings = clip_model.encode_images(images)
动态知识更新:设置定时爬虫更新知识库
个性化适配:基于用户历史查询优化结果排序
我在金融领域的实践中发现,添加这些优化后系统准确率能再提升15-20%:
python复制class PersonalizedRetriever:
def __init__(self, user_profile):
self.user_weights = self._calculate_weights(user_profile)
def rerank(self, results):
# 结合用户兴趣重新排序
return sorted_results
整个项目的完整代码已打包在GitHub(搜索"python-rag-kit"),包含测试数据集和Docker部署脚本。在实际部署时,记得用uvicorn运行服务:
bash复制uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
这个系统我已经在三个不同行业(医疗、法律、金融)成功落地,最大的体会是:RAG系统的效果20%靠算法,80%靠对业务场景的理解。建议大家在跑通基础流程后,花时间深入研究领域特有的文档结构和术语体系,这才是打造高可用知识库的真正关键。