RAG系列(三):实践案例与高级优化

一、完整案例:企业知识问答系统

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平台
   • 自动优化
   • 开箱即用的解决方案
相关推荐
m0_380167142 小时前
最适合交易机器人的加密数据 API:CoinGlass API 指南
人工智能·ai·区块链
踏着七彩祥云的小丑2 小时前
AI学习——LangChain + Agent
人工智能·学习·ai
Zeeland2 小时前
我做了一个 Agent Team 协作平台——Rudder:让 Agent Team 在实践中成长
人工智能·ai·agent
AndrewHZ2 小时前
【大模型技术博客】什么是大语言模型(LLM)?从零认识AI新范式
人工智能·深度学习·ai·语言模型·大模型·llm·transformer
乐兮创想 小林2 小时前
金融投资官网的工程化设计:投资者关系信息架构、合规内容管理与系统对接
金融·架构·网站建设·企业官网·北京网站建设公司
云原生指北2 小时前
从想法到发布:Harness Engineering 的一次完整实践
ai·agentic-coding·opensec
ftpeak2 小时前
深入浅出 LoongSuite Python Agent:让你的 AI 应用「透明化」(下篇)
开发语言·人工智能·ai·ai编程·ai开发
旧书包的青春3 小时前
知识库RAGFLOW
ai·ragflow
m0_46644103詹湛3 小时前
定价的艺术
大数据·人工智能·ai·创业创新