一、完整案例:企业知识问答系统
1.1 系统架构
┌──────────────────────────────────────────────────────┐
│ 企业知识问答系统架构 │
└──────────────────────────────────────────────────────┘
用户界面
↓
┌─────────────────┐
│ API Gateway │
└────────┬─────────┘
↓
┌─────────────────────────────┐
│ RAG服务 (FastAPI) │
│ • 查询处理 │
│ • 检索管理 │
│ • 生成管理 │
└──────┬──────────────┬───────┘
│ │
┌────────▼────┐ ┌─────▼──────┐
│ 向量数据库 │ │ LLM API │
│ (Milvus) │ │ (GPT-4) │
└─────────────┘ └────────────┘
离线处理:
┌────────────────────────────────────────┐
│ 文档处理Pipeline │
│ • 文档监控(新增/修改) │
│ • 自动提取、切分、向量化 │
│ • 增量更新向量数据库 │
└────────────────────────────────────────┘
1.2 完整代码实现
步骤1:环境准备
bash
# 安装依赖
pip install langchain openai chromadb fastapi uvicorn python-multipart pypdf
# 目录结构
enterprise-rag/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI主程序
│ ├── models.py # 数据模型
│ ├── config.py # 配置
│ └── services/
│ ├── __init__.py
│ ├── document_processor.py # 文档处理
│ ├── vectorstore.py # 向量存储
│ └── rag_engine.py # RAG引擎
├── data/
│ ├── raw/ # 原始文档
│ └── processed/ # 处理后的数据
├── chroma_db/ # 向量数据库
└── requirements.txt
步骤2:配置文件
python
# app/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""系统配置"""
# OpenAI配置
OPENAI_API_KEY: str
EMBEDDING_MODEL: str = "text-embedding-3-small"
LLM_MODEL: str = "gpt-4"
LLM_TEMPERATURE: float = 0.0
# 向量数据库配置
VECTOR_DB_PATH: str = "./chroma_db"
COLLECTION_NAME: str = "enterprise_docs"
# 文档处理配置
CHUNK_SIZE: int = 500
CHUNK_OVERLAP: int = 50
# 检索配置
RETRIEVAL_TOP_K: int = 5
SIMILARITY_THRESHOLD: float = 0.7
class Config:
env_file = ".env"
settings = Settings()
步骤3:文档处理服务
python
# app/services/document_processor.py
from typing import List
from langchain.document_loaders import (
PyPDFLoader,
TextLoader,
UnstructuredWordDocumentLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
import os
class DocumentProcessor:
"""文档处理器"""
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=["\n\n", "\n", " ", ""]
)
def load_document(self, file_path: str) -> List[Document]:
"""根据文件类型加载文档"""
file_ext = os.path.splitext(file_path)[1].lower()
loaders = {
'.pdf': PyPDFLoader,
'.txt': TextLoader,
'.docx': UnstructuredWordDocumentLoader,
}
if file_ext not in loaders:
raise ValueError(f"不支持的文件格式: {file_ext}")
loader = loaders[file_ext](file_path)
return loader.load()
def process_documents(self, file_path: str) -> List[Document]:
"""完整的文档处理流程"""
# 1. 加载文档
documents = self.load_document(file_path)
# 2. 添加元数据
for doc in documents:
doc.metadata.update({
'source': os.path.basename(file_path),
'file_path': file_path,
})
# 3. 切分文档
chunks = self.text_splitter.split_documents(documents)
# 4. 为每个chunk添加ID
for i, chunk in enumerate(chunks):
chunk.metadata['chunk_id'] = f"{os.path.basename(file_path)}_{i}"
return chunks
def process_directory(self, directory: str) -> List[Document]:
"""处理目录中的所有文档"""
all_chunks = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(('.pdf', '.txt', '.docx')):
file_path = os.path.join(root, file)
try:
chunks = self.process_documents(file_path)
all_chunks.extend(chunks)
print(f"✓ 处理完成: {file} ({len(chunks)} chunks)")
except Exception as e:
print(f"✗ 处理失败: {file} - {str(e)}")
return all_chunks
步骤4:向量存储服务
python
# app/services/vectorstore.py
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.schema import Document
from typing import List, Tuple
from app.config import settings
class VectorStoreService:
"""向量存储服务"""
def __init__(self):
self.embeddings = OpenAIEmbeddings(
model=settings.EMBEDDING_MODEL,
openai_api_key=settings.OPENAI_API_KEY
)
self.vectorstore = None
def initialize(self):
"""初始化向量数据库"""
self.vectorstore = Chroma(
collection_name=settings.COLLECTION_NAME,
embedding_function=self.embeddings,
persist_directory=settings.VECTOR_DB_PATH
)
return self
def add_documents(self, documents: List[Document]):
"""添加文档到向量数据库"""
if not self.vectorstore:
self.initialize()
self.vectorstore.add_documents(documents)
self.vectorstore.persist()
print(f"✓ 已添加 {len(documents)} 个文档块到向量数据库")
def similarity_search(
self,
query: str,
k: int = 5,
filter_dict: dict = None
) -> List[Tuple[Document, float]]:
"""相似度搜索"""
if not self.vectorstore:
self.initialize()
results = self.vectorstore.similarity_search_with_score(
query,
k=k,
filter=filter_dict
)
return results
def delete_by_source(self, source: str):
"""删除指定来源的文档"""
if not self.vectorstore:
self.initialize()
# 根据元数据删除
self.vectorstore.delete(
filter={"source": source}
)
print(f"✓ 已删除来源为 {source} 的文档")
def get_stats(self) -> dict:
"""获取统计信息"""
if not self.vectorstore:
self.initialize()
collection = self.vectorstore._collection
return {
"total_documents": collection.count(),
"collection_name": settings.COLLECTION_NAME
}
步骤5:RAG引擎
python
# app/services/rag_engine.py
from typing import List, Dict
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from app.config import settings
from app.services.vectorstore import VectorStoreService
class RAGEngine:
"""RAG问答引擎"""
def __init__(self):
self.vectorstore_service = VectorStoreService().initialize()
self.llm = ChatOpenAI(
model=settings.LLM_MODEL,
temperature=settings.LLM_TEMPERATURE,
openai_api_key=settings.OPENAI_API_KEY
)
# 定义Prompt模板
self.prompt_template = """
你是一个专业的企业知识助手。请基于以下参考文档回答用户问题。
参考文档:
{context}
用户问题:{question}
回答要求:
1. 仅基于提供的参考文档回答
2. 如果文档中没有相关信息,明确说明"根据现有文档,我无法找到相关信息"
3. 回答要准确、专业、简洁
4. 在回答中引用文档来源
5. 使用中文回答
答案:"""
self.prompt = PromptTemplate(
template=self.prompt_template,
input_variables=["context", "question"]
)
def query(
self,
question: str,
top_k: int = None,
filter_dict: dict = None
) -> Dict:
"""
执行RAG查询
Args:
question: 用户问题
top_k: 返回的文档数量
filter_dict: 元数据过滤条件
Returns:
包含答案和来源的字典
"""
if top_k is None:
top_k = settings.RETRIEVAL_TOP_K
# 1. 检索相关文档
retrieved_docs = self.vectorstore_service.similarity_search(
query=question,
k=top_k,
filter_dict=filter_dict
)
# 2. 过滤低相关度文档
filtered_docs = [
(doc, score) for doc, score in retrieved_docs
if score <= (1 - settings.SIMILARITY_THRESHOLD)
]
if not filtered_docs:
return {
"answer": "抱歉,我在知识库中没有找到相关信息。",
"sources": [],
"confidence": "low"
}
# 3. 构建检索器
retriever = self.vectorstore_service.vectorstore.as_retriever(
search_kwargs={"k": top_k}
)
# 4. 创建QA链
qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": self.prompt}
)
# 5. 执行查询
result = qa_chain({"query": question})
# 6. 处理结果
sources = []
for doc in result['source_documents']:
sources.append({
"content": doc.page_content[:200] + "...",
"metadata": doc.metadata
})
return {
"answer": result['result'],
"sources": sources,
"confidence": "high" if len(filtered_docs) >= 2 else "medium"
}
def batch_query(self, questions: List[str]) -> List[Dict]:
"""批量查询"""
return [self.query(q) for q in questions]
步骤6:FastAPI应用
python
# app/main.py
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
import os
import shutil
from app.services.document_processor import DocumentProcessor
from app.services.vectorstore import VectorStoreService
from app.services.rag_engine import RAGEngine
from app.config import settings
# 初始化FastAPI
app = FastAPI(
title="企业知识问答系统",
description="基于RAG的智能问答API",
version="1.0.0"
)
# CORS配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 初始化服务
document_processor = DocumentProcessor(
chunk_size=settings.CHUNK_SIZE,
chunk_overlap=settings.CHUNK_OVERLAP
)
vectorstore_service = VectorStoreService().initialize()
rag_engine = RAGEngine()
# 数据模型
class QueryRequest(BaseModel):
question: str
top_k: Optional[int] = 5
filter: Optional[dict] = None
class QueryResponse(BaseModel):
answer: str
sources: List[dict]
confidence: str
# API路由
@app.get("/")
async def root():
"""健康检查"""
return {
"status": "running",
"service": "企业知识问答系统"
}
@app.post("/upload", summary="上传文档")
async def upload_document(file: UploadFile = File(...)):
"""
上传文档到知识库
- 支持格式:PDF, TXT, DOCX
- 自动处理并索引
"""
# 保存上传的文件
upload_dir = "data/raw"
os.makedirs(upload_dir, exist_ok=True)
file_path = os.path.join(upload_dir, file.filename)
try:
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# 处理文档
chunks = document_processor.process_documents(file_path)
# 添加到向量数据库
vectorstore_service.add_documents(chunks)
return {
"message": "文档上传成功",
"filename": file.filename,
"chunks_created": len(chunks)
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/query", response_model=QueryResponse, summary="问答查询")
async def query(request: QueryRequest):
"""
执行RAG查询
- **question**: 用户问题
- **top_k**: 返回的文档数量(默认5)
- **filter**: 元数据过滤条件(可选)
"""
try:
result = rag_engine.query(
question=request.question,
top_k=request.top_k,
filter_dict=request.filter
)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/stats", summary="统计信息")
async def get_stats():
"""获取知识库统计信息"""
try:
stats = vectorstore_service.get_stats()
return stats
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/documents/{source}", summary="删除文档")
async def delete_document(source: str):
"""根据来源删除文档"""
try:
vectorstore_service.delete_by_source(source)
return {"message": f"已删除文档: {source}"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# 启动命令
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
步骤7:使用示例
bash
# 1. 启动服务
python -m uvicorn app.main:app --reload
# 2. 上传文档(使用curl)
curl -X POST "http://localhost:8000/upload" \
-F "file=@company_handbook.pdf"
# 3. 查询(使用curl)
curl -X POST "http://localhost:8000/query" \
-H "Content-Type: application/json" \
-d '{
"question": "公司的年假政策是什么?",
"top_k": 3
}'
# 4. 查看统计
curl "http://localhost:8000/stats"
Python客户端示例:
python
import requests
# API基础URL
BASE_URL = "http://localhost:8000"
# 1. 上传文档
def upload_document(file_path):
with open(file_path, 'rb') as f:
files = {'file': f}
response = requests.post(f"{BASE_URL}/upload", files=files)
return response.json()
# 2. 查询
def query(question, top_k=5):
payload = {
"question": question,
"top_k": top_k
}
response = requests.post(f"{BASE_URL}/query", json=payload)
return response.json()
# 使用
if __name__ == "__main__":
# 上传文档
result = upload_document("company_handbook.pdf")
print("上传结果:", result)
# 查询
answer = query("公司的年假政策是什么?")
print("\n问题:", "公司的年假政策是什么?")
print("答案:", answer['answer'])
print("\n来源文档:")
for i, source in enumerate(answer['sources'], 1):
print(f"{i}. {source['metadata']['source']}")
二、高级RAG技术
2.1 自查询检索(Self-Query Retrieval)
自动从用户问题中提取过滤条件:
python
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
# 定义元数据属性
metadata_field_info = [
AttributeInfo(
name="source",
description="文档来源",
type="string"
),
AttributeInfo(
name="page",
description="页码",
type="integer"
),
AttributeInfo(
name="category",
description="文档分类(如:人事、财务、技术)",
type="string"
),
]
# 创建自查询检索器
self_query_retriever = SelfQueryRetriever.from_llm(
llm=llm,
vectorstore=vectorstore,
document_contents="公司内部文档",
metadata_field_info=metadata_field_info,
verbose=True
)
# 使用:会自动识别查询中的过滤条件
# 问题:"人事文档中关于年假的政策"
# → 自动提取:category="人事",查询="年假政策"
docs = self_query_retriever.get_relevant_documents(
"人事文档中关于年假的政策"
)
2.2 对话式RAG(Conversational RAG)
支持多轮对话,保持上下文:
python
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
# 创建记忆
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
output_key="answer"
)
# 创建对话式RAG链
conversational_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=vectorstore.as_retriever(),
memory=memory,
return_source_documents=True
)
# 多轮对话示例
# 第一轮
response1 = conversational_chain({
"question": "公司的年假政策是什么?"
})
print("回答1:", response1['answer'])
# 第二轮(有上下文)
response2 = conversational_chain({
"question": "那试用期员工呢?" # 系统知道"那"指的是年假
})
print("回答2:", response2['answer'])
# 第三轮
response3 = conversational_chain({
"question": "如何申请?" # 系统知道是申请年假
})
print("回答3:", response3['answer'])
2.3 Agent式RAG
让LLM自主决定何时检索:
python
from langchain.agents import Tool, initialize_agent, AgentType
# 定义检索工具
retrieval_tool = Tool(
name="KnowledgeBase",
func=lambda q: rag_engine.query(q)['answer'],
description="用于查询公司内部知识库。当需要查找公司政策、流程、规定时使用此工具。"
)
# 定义其他工具
calculator_tool = Tool(
name="Calculator",
func=lambda x: eval(x), # 简化示例,实际应使用安全的计算器
description="用于数学计算"
)
# 初始化Agent
agent = initialize_agent(
tools=[retrieval_tool, calculator_tool],
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# 使用:Agent会自主决定使用哪个工具
result = agent.run("如果我工作了3年,按每年10天年假计算,总共有多少天年假?")
# Agent思考过程:
# 1. 需要知道年假政策 → 使用KnowledgeBase工具
# 2. 需要计算 → 使用Calculator工具
# 3. 整合信息并回答
2.4 HyDE(Hypothetical Document Embeddings)
生成假设性答案来改进检索:
python
from langchain.chains import HypotheticalDocumentEmbedder
# 创建HyDE链
hyde_embeddings = HypotheticalDocumentEmbedder.from_llm(
llm=llm,
base_embeddings=embeddings,
prompt_key="web_search" # 使用内置的web_search提示
)
# 创建使用HyDE的向量存储
hyde_vectorstore = Chroma(
embedding_function=hyde_embeddings,
persist_directory="./chroma_db_hyde"
)
# 工作原理:
# 1. 用户问题:"公司的年假政策是什么?"
# 2. LLM生成假设性答案:"公司规定正式员工享有每年10天带薪年假..."
# 3. 使用假设性答案的embedding进行检索
# 4. 检索到的文档更准确
2.5 多文档Agent
处理多个知识源:
python
from langchain.agents import AgentExecutor
from langchain.tools.retriever import create_retriever_tool
# 创建多个检索器
hr_retriever = hr_vectorstore.as_retriever()
finance_retriever = finance_vectorstore.as_retriever()
tech_retriever = tech_vectorstore.as_retriever()
# 为每个检索器创建工具
hr_tool = create_retriever_tool(
hr_retriever,
name="人事文档",
description="用于查询人事相关信息,如招聘、薪酬、福利、考勤等"
)
finance_tool = create_retriever_tool(
finance_retriever,
name="财务文档",
description="用于查询财务相关信息,如报销、预算、审批流程等"
)
tech_tool = create_retriever_tool(
tech_retriever,
name="技术文档",
description="用于查询技术相关信息,如开发规范、架构文档、API文档等"
)
# 初始化Agent
agent = initialize_agent(
tools=[hr_tool, finance_tool, tech_tool],
llm=llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True
)
# 使用:Agent会选择合适的知识库
result = agent.run("年假申请的报销流程是什么?")
# Agent会先查询人事文档(年假),再查询财务文档(报销流程)
三、RAG评估与监控
3.1 评估指标
python
from typing import List, Tuple
class RAGEvaluator:
"""RAG系统评估器"""
def __init__(self, rag_engine):
self.rag_engine = rag_engine
def evaluate_retrieval(
self,
test_cases: List[Tuple[str, List[str]]]
) -> dict:
"""
评估检索质量
test_cases: [(问题, [相关文档ID列表]), ...]
"""
total_precision = 0
total_recall = 0
total_mrr = 0 # Mean Reciprocal Rank
for question, relevant_doc_ids in test_cases:
# 执行检索
retrieved_docs = self.rag_engine.vectorstore_service.similarity_search(
query=question,
k=10
)
retrieved_ids = [
doc.metadata.get('chunk_id')
for doc, _ in retrieved_docs
]
# 计算Precision@K
k = 5
retrieved_top_k = retrieved_ids[:k]
relevant_in_top_k = len(
set(retrieved_top_k) & set(relevant_doc_ids)
)
precision = relevant_in_top_k / k
# 计算Recall
recall = relevant_in_top_k / len(relevant_doc_ids)
# 计算MRR
mrr = 0
for i, doc_id in enumerate(retrieved_ids, 1):
if doc_id in relevant_doc_ids:
mrr = 1 / i
break
total_precision += precision
total_recall += recall
total_mrr += mrr
n = len(test_cases)
return {
"precision@5": total_precision / n,
"recall": total_recall / n,
"mrr": total_mrr / n
}
def evaluate_generation(
self,
test_cases: List[Tuple[str, str]]
) -> dict:
"""
评估生成质量
test_cases: [(问题, 参考答案), ...]
"""
from langchain.evaluation import load_evaluator
# 使用LLM评估答案质量
evaluator = load_evaluator(
"criteria",
criteria="correctness",
llm=self.rag_engine.llm
)
scores = []
for question, reference_answer in test_cases:
result = self.rag_engine.query(question)
generated_answer = result['answer']
# 评估
eval_result = evaluator.evaluate_strings(
prediction=generated_answer,
reference=reference_answer,
input=question
)
scores.append(eval_result['score'])
return {
"avg_correctness": sum(scores) / len(scores)
}
# 使用示例
evaluator = RAGEvaluator(rag_engine)
# 准备测试用例
retrieval_test_cases = [
("公司的年假政策是什么?", ["hr_handbook_12", "hr_handbook_13"]),
("如何申请报销?", ["finance_guide_5", "finance_guide_6"]),
]
generation_test_cases = [
(
"公司的年假政策是什么?",
"公司规定正式员工享有每年10天带薪年假,工作满3年增加到15天。"
),
]
# 评估
retrieval_metrics = evaluator.evaluate_retrieval(retrieval_test_cases)
generation_metrics = evaluator.evaluate_generation(generation_test_cases)
print("检索指标:", retrieval_metrics)
print("生成指标:", generation_metrics)
3.2 监控与日志
python
import logging
from datetime import datetime
import json
class RAGMonitor:
"""RAG系统监控"""
def __init__(self, log_file="rag_monitor.log"):
self.logger = logging.getLogger("RAGMonitor")
self.logger.setLevel(logging.INFO)
# 文件处理器
fh = logging.FileHandler(log_file)
fh.setLevel(logging.INFO)
# 格式化
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
fh.setFormatter(formatter)
self.logger.addHandler(fh)
def log_query(
self,
question: str,
answer: str,
sources: List[dict],
latency: float,
confidence: str
):
"""记录查询日志"""
log_data = {
"timestamp": datetime.now().isoformat(),
"question": question,
"answer": answer,
"num_sources": len(sources),
"latency_ms": latency * 1000,
"confidence": confidence
}
self.logger.info(json.dumps(log_data, ensure_ascii=False))
def log_error(self, error: Exception, context: dict):
"""记录错误"""
log_data = {
"timestamp": datetime.now().isoformat(),
"error": str(error),
"error_type": type(error).__name__,
"context": context
}
self.logger.error(json.dumps(log_data, ensure_ascii=False))
# 在RAG引擎中集成监控
class MonitoredRAGEngine(RAGEngine):
"""带监控的RAG引擎"""
def __init__(self):
super().__init__()
self.monitor = RAGMonitor()
def query(self, question: str, **kwargs):
import time
start_time = time.time()
try:
result = super().query(question, **kwargs)
latency = time.time() - start_time
# 记录成功查询
self.monitor.log_query(
question=question,
answer=result['answer'],
sources=result['sources'],
latency=latency,
confidence=result['confidence']
)
return result
except Exception as e:
# 记录错误
self.monitor.log_error(e, {"question": question})
raise
四、常见问题与解决方案
4.1 问题:检索不准确
症状:
- 返回不相关的文档
- 找不到明显相关的信息
解决方案:
1. 优化文档切分
• 调整chunk_size(尝试300-800)
• 增加overlap(10-30%)
• 使用语义切分而非固定长度
2. 改进查询
• 查询改写
• 查询扩展
• 添加上下文
3. 使用混合检索
• 结合向量检索和BM25
• 使用重排序模型
4. 调整相似度阈值
• 过滤低质量结果
• 动态调整top_k
代码示例:查询优化
python
def improve_query(original_query: str) -> str:
"""优化查询"""
prompt = f"""
请将以下用户问题改写为更适合检索的查询:
原始问题:{original_query}
要求:
1. 保留关键信息
2. 去除口语化表达
3. 添加相关的同义词
4. 简洁明确
改写后的查询:
"""
response = llm.predict(prompt)
return response.strip()
# 使用
original = "咱们公司休假咋规定的啊?"
improved = improve_query(original)
print(f"原始: {original}")
print(f"优化: {improved}")
# 输出:公司休假政策 规定 年假 病假 请假制度
4.2 问题:回答有幻觉
症状:
- LLM编造不存在的信息
- 答案与文档内容不符
解决方案:
1. 改进Prompt
• 明确要求"仅基于文档"
• 要求引用来源
• 设置"无法回答"的条件
2. 降低Temperature
• 设置temperature=0或接近0
• 减少随机性
3. 后处理验证
• 检查答案是否包含文档中的关键词
• 使用另一个LLM验证答案
4. 使用更好的模型
• GPT-4 > GPT-3.5
• Claude-3 Opus > Haiku
代码示例:防幻觉Prompt
python
anti_hallucination_template = """
你是一个严谨的助手。请严格基于以下参考文档回答问题。
参考文档:
{context}
用户问题:{question}
重要规则:
1. 只使用参考文档中的信息回答
2. 不要添加文档中没有的信息
3. 如果文档中没有相关信息,必须回答:"根据提供的文档,我无法找到相关信息。"
4. 在回答中引用具体文档内容
5. 不确定时,宁可说"不知道"也不要猜测
请回答:
"""
prompt = PromptTemplate(
template=anti_hallucination_template,
input_variables=["context", "question"]
)
4.3 问题:响应速度慢
症状:
- 查询响应时间长(>5秒)
- 用户体验差
优化方案:
1. 减少检索量
• 降低top_k(5→3)
• 提前过滤低相关度文档
2. 优化Embedding
• 使用更小的模型
• 批量处理
• 启用缓存
3. 使用更快的LLM
• GPT-4 → GPT-3.5-turbo
• 或使用本地模型
4. 并行处理
• 异步调用
• 流式输出
5. 缓存常见查询
• Redis缓存
• LRU缓存
代码示例:缓存
python
from functools import lru_cache
import hashlib
class CachedRAGEngine(RAGEngine):
"""带缓存的RAG引擎"""
def __init__(self, cache_size=100):
super().__init__()
self.cache = {}
self.max_cache_size = cache_size
def _get_cache_key(self, question: str) -> str:
"""生成缓存键"""
return hashlib.md5(question.encode()).hexdigest()
def query(self, question: str, **kwargs):
# 检查缓存
cache_key = self._get_cache_key(question)
if cache_key in self.cache:
print("✓ 缓存命中")
return self.cache[cache_key]
# 执行查询
result = super().query(question, **kwargs)
# 存入缓存
if len(self.cache) >= self.max_cache_size:
# 删除最老的条目(简化的LRU)
self.cache.pop(next(iter(self.cache)))
self.cache[cache_key] = result
return result
4.4 问题:多语言支持
python
class MultilingualRAGEngine:
"""多语言RAG引擎"""
def __init__(self):
# 使用多语言Embedding模型
self.embeddings = HuggingFaceEmbeddings(
model_name="intfloat/multilingual-e5-large"
)
self.llm = ChatOpenAI(model="gpt-4") # GPT-4支持多语言
def detect_language(self, text: str) -> str:
"""检测语言"""
from langdetect import detect
return detect(text)
def query(self, question: str):
# 检测问题语言
lang = self.detect_language(question)
# 根据语言调整Prompt
if lang == 'zh-cn':
system_message = "你是一个中文助手..."
elif lang == 'en':
system_message = "You are a helpful assistant..."
# ... 其他语言
# 执行查询(使用相应的Prompt)
# ...
五、生产部署最佳实践
5.1 Docker部署
dockerfile
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY .. .
# 创建数据目录
RUN mkdir -p /app/data/raw /app/data/processed /app/chroma_db
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
yaml
# docker-compose.yml
version: '3.8'
services:
rag-api:
build: .
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./data:/app/data
- ./chroma_db:/app/chroma_db
restart: unless-stopped
# 可选:添加向量数据库
milvus:
image: milvusdb/milvus:latest
ports:
- "19530:19530"
volumes:
- ./milvus_data:/var/lib/milvus
5.2 性能优化清单
✓ 数据层
• 使用专业向量数据库(Milvus、Weaviate)
• 创建合适的索引
• 定期优化数据库
• 考虑分片(大规模数据)
✓ 检索层
• 设置合理的top_k(3-10)
• 使用相似度阈值过滤
• 启用查询缓存
• 考虑重排序
✓ 生成层
• 选择合适大小的LLM
• 启用流式输出
• 设置合理的token限制
• 使用响应缓存
✓ 架构层
• 异步处理
• 负载均衡
• 限流保护
• 监控和日志
✓ 成本优化
• 批量处理Embedding
• 使用更便宜的模型(GPT-3.5)
• 缓存常见查询
• 优化Prompt长度
5.3 安全考虑
python
from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
"""验证JWT token"""
token = credentials.credentials
# 实现token验证逻辑
if not validate_token(token):
raise HTTPException(status_code=401, detail="Invalid token")
return token
@app.post("/query")
async def query(
request: QueryRequest,
token: str = Depends(verify_token)
):
"""受保护的查询端点"""
# 实现查询逻辑
pass
# 其他安全措施:
# 1. 限流(Rate Limiting)
# 2. 输入验证和清理
# 3. 敏感信息过滤
# 4. 审计日志
# 5. HTTPS加密
六、总结与展望
6.1 RAG技术栈总结
┌─────────────────────────────────────────────┐
│ 完整的RAG技术栈 │
├─────────────────────────────────────────────┤
│ 数据层 │
│ • 文档加载:LangChain Loaders │
│ • 文档切分:RecursiveCharacterTextSplitter │
│ • 向量化:OpenAI Embeddings / BGE │
│ • 存储:Chroma / Milvus / Pinecone │
│ │
│ 检索层 │
│ • 基础检索:向量相似度 │
│ • 混合检索:Vector + BM25 │
│ • 重排序:Cohere Rerank / CrossEncoder │
│ • 高级:Self-Query / Multi-Query │
│ │
│ 生成层 │
│ • LLM:GPT-4 / Claude / Llama │
│ • Prompt Engineering │
│ • 输出解析和验证 │
│ │
│ 应用层 │
│ • API:FastAPI / Flask │
│ • 前端:Streamlit / Gradio │
│ • 监控:Langsmith / Phoenix │
└─────────────────────────────────────────────┘
6.2 最佳实践回顾
1. 文档处理
✓ chunk_size: 300-500 tokens
✓ overlap: 10-20%
✓ 保留元数据
✓ 定期更新知识库
2. Embedding
✓ 商业场景:OpenAI ada-002/3
✓ 开源场景:BGE(中文)、E5(多语言)
✓ 批量处理提高效率
3. 检索
✓ 混合检索(向量+关键词)
✓ 重排序提高准确率
✓ 设置相似度阈值
4. 生成
✓ 清晰的Prompt指令
✓ 要求引用来源
✓ 防止幻觉
✓ Temperature=0(准确性优先)
5. 评估
✓ 检索指标:Precision, Recall, MRR
✓ 生成指标:Correctness, Relevance
✓ 用户反馈
✓ A/B测试
6. 生产
✓ 监控和日志
✓ 缓存优化
✓ 安全控制
✓ 成本控制
6.3 未来趋势
1. 更智能的检索
• 多模态检索(文本+图片+表格)
• 图谱增强RAG
• 因果推理
2. 更强的生成
• 长上下文LLM(100K+)
• 多步推理
• 工具使用能力
3. 更好的评估
• 自动化评估
• 人机协作评估
• 实时反馈优化
4. 更易用的工具
• 低代码/无代码RAG平台
• 自动优化
• 开箱即用的解决方案