RAG系统开发实战
🎯 概述
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了信息检索和文本生成的技术,能够从知识库中检索相关信息来增强大语言模型的生成能力。
📚 核心概念
RAG工作流程
- 文档处理: 将原始文档分割成小块
- 向量化: 使用embedding模型将文档块转换为向量
- 存储: 将向量存储到向量数据库中
- 检索: 根据用户问题检索相关文档块
- 生成: 将检索到的文档和问题一起输入LLM生成答案
🛠️ 技术架构
系统组件
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 文档处理 │───▶│ 向量化 │───▶│ 向量数据库 │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌─────────────┐ ┌─────────────┐ │
│ 用户问题 │───▶│ 检索系统 │───────────┘
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ LLM生成 │
└─────────────┘🚀 实现步骤
1. 文档处理
python
import os
import json
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import DirectoryLoader
class DocumentProcessor:
def __init__(self, chunk_size=1000, chunk_overlap=200):
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
)
def load_documents(self, directory_path):
"""加载目录中的所有文档"""
loader = DirectoryLoader(directory_path, glob="**/*.txt")
documents = loader.load()
return documents
def split_documents(self, documents):
"""将文档分割成小块"""
chunks = self.text_splitter.split_documents(documents)
return chunks
def process_documents(self, directory_path):
"""完整的文档处理流程"""
documents = self.load_documents(directory_path)
chunks = self.split_documents(documents)
# 添加元数据
for i, chunk in enumerate(chunks):
chunk.metadata['chunk_id'] = i
chunk.metadata['source'] = chunk.metadata.get('source', 'unknown')
return chunks2. 向量化存储
python
import chromadb
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
class VectorStore:
def __init__(self, persist_directory="./chroma_db"):
self.embeddings = OpenAIEmbeddings()
self.persist_directory = persist_directory
self.vectorstore = None
def create_vectorstore(self, chunks):
"""创建向量数据库"""
self.vectorstore = Chroma.from_documents(
documents=chunks,
embedding=self.embeddings,
persist_directory=self.persist_directory
)
return self.vectorstore
def load_vectorstore(self):
"""加载已存在的向量数据库"""
self.vectorstore = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings
)
return self.vectorstore
def similarity_search(self, query, k=5):
"""相似性搜索"""
if self.vectorstore is None:
self.load_vectorstore()
docs = self.vectorstore.similarity_search(query, k=k)
return docs3. 检索系统
python
from langchain.retrievers import VectorStoreRetriever
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain.llms import OpenAI
class RetrievalSystem:
def __init__(self, vectorstore, llm):
self.vectorstore = vectorstore
self.llm = llm
self.retriever = self._create_retriever()
def _create_retriever(self):
"""创建检索器"""
base_retriever = VectorStoreRetriever(
vectorstore=self.vectorstore,
search_kwargs={"k": 5}
)
# 使用多查询检索器提高检索质量
retriever = MultiQueryRetriever.from_llm(
retriever=base_retriever,
llm=self.llm
)
return retriever
def retrieve_documents(self, query):
"""检索相关文档"""
docs = self.retriever.get_relevant_documents(query)
return docs4. 生成系统
python
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
class RAGSystem:
def __init__(self, retriever, llm):
self.retriever = retriever
self.llm = llm
self.qa_chain = self._create_qa_chain()
def _create_qa_chain(self):
"""创建问答链"""
prompt_template = """
基于以下上下文信息回答问题。如果上下文中没有相关信息,请说"我无法从提供的信息中找到答案"。
上下文:
{context}
问题: {question}
答案:
"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.retriever,
chain_type_kwargs={"prompt": PROMPT}
)
return qa_chain
def ask_question(self, question):
"""回答问题"""
result = self.qa_chain({"query": question})
return result["result"]🎯 完整实现
python
class RAGApplication:
def __init__(self, openai_api_key, persist_directory="./chroma_db"):
self.openai_api_key = openai_api_key
self.persist_directory = persist_directory
self.document_processor = DocumentProcessor()
self.vector_store = VectorStore(persist_directory)
self.llm = OpenAI(openai_api_key=openai_api_key)
self.rag_system = None
def setup_knowledge_base(self, documents_directory):
"""设置知识库"""
print("正在处理文档...")
chunks = self.document_processor.process_documents(documents_directory)
print("正在创建向量数据库...")
self.vector_store.create_vectorstore(chunks)
print("正在初始化RAG系统...")
retrieval_system = RetrievalSystem(self.vector_store.vectorstore, self.llm)
self.rag_system = RAGSystem(retrieval_system.retriever, self.llm)
print("知识库设置完成!")
def load_existing_knowledge_base(self):
"""加载已存在的知识库"""
print("正在加载向量数据库...")
self.vector_store.load_vectorstore()
print("正在初始化RAG系统...")
retrieval_system = RetrievalSystem(self.vector_store.vectorstore, self.llm)
self.rag_system = RAGSystem(retrieval_system.retriever, self.llm)
print("知识库加载完成!")
def chat(self, question):
"""与RAG系统对话"""
if self.rag_system is None:
print("请先设置知识库!")
return
answer = self.rag_system.ask_question(question)
return answer
# 使用示例
if __name__ == "__main__":
# 初始化RAG应用
rag_app = RAGApplication(openai_api_key="your-api-key")
# 设置知识库(首次运行)
# rag_app.setup_knowledge_base("./documents")
# 加载已存在的知识库
rag_app.load_existing_knowledge_base()
# 开始对话
while True:
question = input("请输入问题(输入'quit'退出): ")
if question.lower() == 'quit':
break
answer = rag_app.chat(question)
print(f"回答: {answer}\n")🔧 优化技巧
1. 检索优化
python
def hybrid_search(self, query, alpha=0.7):
"""混合搜索:结合关键词搜索和语义搜索"""
# 语义搜索
semantic_docs = self.vectorstore.similarity_search(query, k=5)
# 关键词搜索
keyword_docs = self.vectorstore.similarity_search_with_score(query, k=5)
# 合并结果
combined_docs = self._combine_search_results(
semantic_docs, keyword_docs, alpha
)
return combined_docs
def rerank_documents(self, query, documents, top_k=3):
"""重新排序文档"""
# 使用更复杂的重排序算法
scores = []
for doc in documents:
score = self._calculate_relevance_score(query, doc)
scores.append((doc, score))
# 按分数排序
scores.sort(key=lambda x: x[1], reverse=True)
return [doc for doc, score in scores[:top_k]]2. 提示词优化
python
def create_optimized_prompt(self):
"""创建优化的提示词模板"""
prompt_template = """
你是一个专业的AI助手,请基于以下上下文信息回答问题。
上下文信息:
{context}
用户问题: {question}
回答要求:
1. 基于上下文信息提供准确、详细的答案
2. 如果上下文中没有相关信息,请明确说明
3. 回答要结构清晰,逻辑性强
4. 如果可能,提供具体的例子或数据支持
答案:
"""
return PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)3. 缓存机制
python
import redis
import hashlib
import json
class RAGCache:
def __init__(self, redis_url="redis://localhost:6379"):
self.redis_client = redis.from_url(redis_url)
self.cache_ttl = 3600 # 1小时
def get_cache_key(self, query):
"""生成缓存键"""
return hashlib.md5(query.encode()).hexdigest()
def get_cached_answer(self, query):
"""获取缓存的答案"""
cache_key = self.get_cache_key(query)
cached = self.redis_client.get(cache_key)
if cached:
return json.loads(cached)
return None
def cache_answer(self, query, answer):
"""缓存答案"""
cache_key = self.get_cache_key(query)
self.redis_client.setex(
cache_key,
self.cache_ttl,
json.dumps(answer)
)📊 性能监控
python
import time
import logging
from typing import Dict, Any
class RAGMetrics:
def __init__(self):
self.metrics = {
'total_queries': 0,
'avg_response_time': 0,
'cache_hit_rate': 0,
'retrieval_accuracy': 0
}
self.logger = logging.getLogger(__name__)
def log_query(self, query: str, response_time: float,
cache_hit: bool, retrieved_docs: int):
"""记录查询指标"""
self.metrics['total_queries'] += 1
# 更新平均响应时间
current_avg = self.metrics['avg_response_time']
total_queries = self.metrics['total_queries']
self.metrics['avg_response_time'] = (
(current_avg * (total_queries - 1) + response_time) / total_queries
)
# 记录日志
self.logger.info(f"Query: {query[:50]}... | "
f"Response time: {response_time:.2f}s | "
f"Cache hit: {cache_hit} | "
f"Retrieved docs: {retrieved_docs}")
def get_metrics(self) -> Dict[str, Any]:
"""获取性能指标"""
return self.metrics.copy()🎯 部署建议
1. Docker部署
dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]2. API服务
python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="RAG API", version="1.0.0")
class QueryRequest(BaseModel):
question: str
max_tokens: int = 500
class QueryResponse(BaseModel):
answer: str
sources: list
response_time: float
@app.post("/query", response_model=QueryResponse)
async def query_rag(request: QueryRequest):
try:
start_time = time.time()
answer = rag_app.chat(request.question)
response_time = time.time() - start_time
return QueryResponse(
answer=answer,
sources=[], # 可以添加来源文档
response_time=response_time
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))🎉 总结
RAG系统开发涉及多个技术环节,从文档处理到向量检索,再到生成优化。通过合理的架构设计和优化策略,可以构建出高质量的RAG应用。
关键成功因素:
- 高质量的文档预处理
- 合适的embedding模型选择
- 有效的检索策略
- 优化的提示词设计
- 完善的监控和缓存机制
RAG让AI更智能! 🤖