隐私保护的RAG知识库

06-19-2025

在本博客中,我们将讨论如何构建一个本地RAG(检索增强生成)知识库,既保护隐私又兼顾经济性。所谓”本地”,指的是被分析的文档不会被任何云端大模型处理。这不仅保护了数据隐私,还避免了对OpenAI等服务的API调用,从而节省了成本。

我们将从基于Langchain和开源嵌入模型的基础RAG实现开始。接着,加入Agent支持以提升RAG的智能性。最后,我们会对RAG进行优化以提升性能。

RAG架构概览

检索增强生成(RAG)结合了大语言模型与知识库的能力,能够提供准确且有上下文的回答。架构分解如下:

  1. 文档处理层
    • 支持多种文档类型(PDF、TXT、EPUB)
    • 文档存储在可配置的知识库目录
    • 针对不同文件类型使用专用加载器(PyPDF2、TextLoader、EbookLib)
  2. 文本处理层
    • 使用RecursiveCharacterTextSplitter将文档切分为可管理的块
    • 默认:每块1000字符,重叠200字符
    • 保留每块的源元数据
  3. 嵌入层
    • 使用OllamaEmbeddings和”nomic-embed-text”模型
    • 将文本块转换为向量表示
    • 优化用于语义相似度匹配
  4. 向量存储层
    • 支持多种向量数据库:
      • Chroma(默认):持久化存储在”chroma_db”
      • FAISS:索引存储在”faiss_index”
    • 存储嵌入和元数据
  5. 检索与生成层
    • 相似度检索找到相关块(默认前4条)
    • 本地LLM(通过Ollama的Deepseek)结合上下文处理查询
    • 返回带有来源文档的答案
  6. 实时更新系统
    • FileWatcher监控知识库目录
    • 文档变更时自动重建索引
    • 保证知识库内容实时更新

架构图

下图展示了完整的RAG架构和Agent辅助流程:

六层RAG架构

graph TB subgraph "1. 文档处理层" A1[知识库目录] A2[PDF加载器] A3[TXT加载器] A4[EPUB加载器] A1 --> A2 A1 --> A3 A1 --> A4 end subgraph "2. 文本处理层" B1[递归字符切分器] B2[1000字符/块] B3[200字符重叠] B4[元数据保留] B1 --> B2 B1 --> B3 B1 --> B4 end subgraph "3. 嵌入层" C1[Ollama嵌入] C2[nomic-embed-text模型] C3[向量表示] C1 --> C2 C2 --> C3 end subgraph "4. 向量存储层" D1[向量数据库] D2[Chroma数据库] D3[FAISS索引] D4[元数据存储] D1 --> D2 D1 --> D3 D2 --> D4 D3 --> D4 end subgraph "5. 检索与生成层" E1[相似度检索] E2[前4条结果] E3[本地LLM/Deepseek] E4[上下文处理] E5[答案生成] E1 --> E2 E2 --> E3 E3 --> E4 E4 --> E5 end subgraph "6. 实时更新系统" F1[文件监控] F2[自动重建索引] F3[知识库更新] F1 --> F2 F2 --> F3 end A4 --> B1 B4 --> C1 C3 --> D1 D4 --> E1 F3 --> A1

Agent辅助RAG流程

sequenceDiagram participant U as 用户 participant A as Agent participant KB as 知识库 participant T as 工具 participant LLM as 本地LLM participant V as 向量存储 U->>A: 复杂查询 A->>A: 分析查询需求 par 工具执行 A->>KB: 检索本地知识库 KB->>V: 向量检索 V-->>KB: 相关块 KB-->>A: 本地上下文 and 外部检索 A->>T: 外部工具调用 T-->>A: 外部数据 end A->>LLM: 合并并处理结果 LLM-->>A: 分析后的回复 A->>U: 最终答案及引用 Note over A,LLM: Agent编排多工具和知识源

现在我们了解了RAG系统的架构和流程,下面用LangChain和本地LLM实现细节。

实现细节

让我们看看如何用LangChain和本地LLM实现RAG系统。

什么是LangChain?

LangChain是一个用于开发大语言模型应用的框架。它提供:

  1. 常用操作的组件(加载文档、切分文本、管理提示)
  2. 与多种LLM、嵌入模型和向量存储的集成
  3. 构建链和Agent的工具

有了这些基础,我们可以实现RAG系统,从基础组件到Agent能力。

基础RAG实现(本地LLM)

不使用OpenAI API(需要API密钥且收费),我们可以通过Ollama使用本地模型。以下是用Deepseek模型实现基础RAG的步骤:

  1. 首先,在Ollama中安装Deepseek:
    ollama pull deepseek-coder:1.3b
    
  2. 基础LangChain RAG流程:
# 1. 加载文档
from langchain_community.document_loaders import TextLoader
loader = TextLoader("file.txt")
docs = loader.load()

# 2. 文档切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(docs)

# 3. 嵌入并存储到向量库
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings

embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma.from_documents(chunks, embeddings)

4. 用本地LLM设置检索链

from langchain_community.llms import Ollama

llm = Ollama(model=”deepseek-coder:1.3b”) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type=”stuff”, retriever=vectorstore.as_retriever() )

5. 提问

question = “这份文档讲了什么?” answer = qa_chain.run(question)


此实现:
1. 获取用户问题
2. 用检索器找出最相关的文档块
3. 将这些文档和问题一起"填充"到提示中
4. 通过Ollama将带上下文的提示发送给本地LLM

LangChain的`RetrievalQA`链负责将上下文与问题结合,并管理与LLM的交互。虽然这是基础实现,但它为后续增强提供了基础:
- 自定义提示模板
- 更好的上下文预处理与选择
- 高级检索策略
- 来源验证与引用要求

## 高级Agent-RAG实现

在上述Agent增强方法基础上,下面实现一个综合系统:

### Agent工具与能力

Agent-RAG的强大之处在于能针对不同任务调用不同工具。以下是常见工具的实现方式:

```python
from langchain.tools import BaseTool
from typing import Optional

class LocalKnowledgeBaseTool(BaseTool):
    name = "local_kb_search"
    description = "在本地知识库中检索相关信息"

    def _run(self, query: str) -> str:
        results = vectorstore.similarity_search(query, k=4)
        return self._format_results(results)

    def _format_results(self, results) -> str:
        return "\n".join([doc.page_content for doc in results])

class WebSearchTool(BaseTool):
    name = "web_search"
    description = "检索网络补充信息"

    def _run(self, query: str) -> str:
        # 实现网络检索逻辑
        return web_results

# 工具编排
class ToolOrchestrator:
    def __init__(self, tools: List[BaseTool]):
        self.tools = {tool.name: tool for tool in tools}
        self.fallback_tool = None

    def execute_with_fallback(self, tool_name: str, query: str) -> str:
        try:
            return self.tools[tool_name]._run(query)
        except Exception as e:
            if self.fallback_tool:
                return self.fallback_tool._run(query)
            raise e

性能优化

可采用以下优化策略提升RAG性能:

# 1. 并行处理
from concurrent.futures import ThreadPoolExecutor

def parallel_document_processing(documents: List[Document]):
    with ThreadPoolExecutor() as executor:
        chunks = executor.map(text_splitter.split_text, documents)
    return list(chunks)

# 2. 缓存系统
from functools import lru_cache

class CachedVectorStore:
    def __init__(self, vectorstore):
        self.vectorstore = vectorstore
        self.cache = {}

    @lru_cache(maxsize=1000)
    def similarity_search(self, query: str, k: int = 4):
        return self.vectorstore.similarity_search(query, k=k)

# 3. 索引优化
def optimize_index(vectorstore):
    if isinstance(vectorstore, FAISS):
        vectorstore.index.nprobe = 8  # 根据索引大小调整
    return vectorstore

# 4. 查询优化
def refine_query(query: str, context: Optional[str] = None) -> str:
    refined = llm(f"""优化此检索查询,同时保持原意。
    原始查询: {query}
    上下文: {context or '无'}
    优化后查询:""")
    return refined.strip()

真实场景用法

以下是常见场景的实用模式:

class EnhancedRAGAgent:
    def __init__(self, tools, llm, vectorstore):
        self.tools = tools
        self.llm = llm
        self.vectorstore = vectorstore
        self.history = []

    def process_query(self, query: str) -> str:
        # 1. 查询分析
        query_type = self.analyze_query_type(query)
        
        # 2. 工具选择
        selected_tools = self.select_tools(query_type)
        
        # 3. 并行信息收集
        with ThreadPoolExecutor() as executor:
            results = list(executor.map(
                lambda tool: tool._run(query),
                selected_tools
            ))
        
        # 4. 上下文管理
        context = self.merge_results(results)
        context = self.filter_relevant_context(context, query)
        
        # 5. 回答生成
        response = self.generate_response(query, context)
        
        # 6. 添加引用
        final_response = self.add_citations(response, results)
        
        self.history.append((query, final_response))
        return final_response

    def filter_relevant_context(self, context: str, query: str) -> str:
        relevance_scores = self.calculate_relevance(context, query)
        return self.select_top_relevant(context, relevance_scores)

    def add_citations(self, response: str, results: List[str]) -> str:
        sources = self.extract_sources(results)
        return f"{response}\n\n来源:\n" + "\n".join(sources)

实际应用示例:

# 初始化增强型RAG系统
agent = EnhancedRAGAgent(
    tools=[
        LocalKnowledgeBaseTool(),
        WebSearchTool(),
        ImageAnalysisTool(),
    ],
    llm=Ollama(model="deepseek-coder:1.3b"),
    vectorstore=CachedVectorStore(
        optimize_index(
            Chroma(embedding_function=OllamaEmbeddings())
        )
    )
)

# 处理复杂查询
response = agent.process_query(
    "对比我们公司的病假政策与湾区科技公司的行业标准。"
)

此实现:

  • 多工具并行调用
  • 错误处理健壮
  • 提供来源引用
  • 缓存优化性能
  • 有效管理上下文
  • 生成结构化回答

未来挑战与机遇

  1. 高级上下文理解
    • 处理文档间复杂逻辑关系
    • 理解并保留文档层级结构
    • 跨文档指代消解
    • 针对不同LLM动态调整上下文窗口
  2. 质量与可信度
    • 实时事实核查机制
    • 生成回答的置信度评分
    • 偏见检测与缓解
    • 隐私保护的信息检索
  3. 多模态支持
    • 处理图片、音频、视频内容
    • 跨模态推理与检索
    • 多模态答案生成
    • 结构化数据集成
  4. 高级交互
    • 交互式澄清对话
    • 多轮推理能力
    • Agent决策过程解释
    • 用户偏好学习

这些挑战为RAG系统的未来发展带来机遇。随着领域进步,解决这些问题将推动知识管理系统更智能、更可信、更好地服务用户需求。