From 8b5fbbd3955627e79e19a941246b7e2e59052313 Mon Sep 17 00:00:00 2001 From: root <953994191@qq.com> Date: Tue, 5 May 2026 14:06:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- backend/app/rag/README.md | 398 +++++++++++++------------------------- rag_indexer/README.md | 316 ++++++------------------------ 3 files changed, 198 insertions(+), 521 deletions(-) diff --git a/README.md b/README.md index 5fbf105..4b35643 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,7 @@ - ✅ **向后兼容**:可通过 use_hybrid_router=True/False 切换混合路由/纯 React 模式 --- -好的,以下是根据我们讨论优化后的完整架构部分。你直接粘贴到 README 中即可使用。 -```markdown ## 🏗️ 技术架构 ### 1. 技术栈总览 @@ -422,7 +420,8 @@ graph LR |------|------|---------| | **PostgreSQL** | 对话历史、长期记忆 | 远程服务器,SQLAlchemy ORM | | **Qdrant** | 文档向量、知识库 | 远程服务器,gRPC/HTTP API | -``` + +--- ### 数据流向图 diff --git a/backend/app/rag/README.md b/backend/app/rag/README.md index 26423d7..4c05f1f 100644 --- a/backend/app/rag/README.md +++ b/backend/app/rag/README.md @@ -6,81 +6,84 @@ ### 技术栈 -| 组件 | 技术选型 | 版本 | 说明 | -|:-----|:---------|:-----|:-----| -| **基础检索** | `Qdrant` | 1.17+ | HNSW 稠密向量检索 | -| **混合检索** | `Qdrant` + `BM25` | 内置 | 稠密 + 稀疏向量融合 | -| **查询改写** | `LangChain` | 内置 | `MultiQueryGenerator` 多路改写 | -| **RRF 融合** | 自实现 | - | `reciprocal_rank_fusion` 倒数排名融合 | -| **重排序** | `llama.cpp` | 本地服务 | OpenAI 兼容 Rerank API | -| **编排框架** | `asyncio` | Python 3.10+ | 异步并行检索 | +| 组件 | 技术选型 | 说明 | +|:-----|:---------|:-----| +| **基础检索** | `Qdrant` | HNSW 稠密向量检索 | +| **混合检索** | `Qdrant` + `BM25` | 稠密 + 稀疏向量融合,Qdrant 原生 RRF | +| **查询改写** | `LangChain` | `MultiQueryGenerator` 多路改写 | +| **重排序** | `llama.cpp` | 本地服务,OpenAI 兼容 Rerank API | +| **编排框架** | `asyncio` | 异步并行检索 | +| **服务接入** | `model_services` | 统一获取嵌入、LLM、重排序服务 | ### 检索流水线 ``` -┌─────────────────────────────────────────────────────────────┐ -│ 用户提问 │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ MultiQueryGenerator │ -│ 多路查询改写 (num_queries=3) │ -│ "如何申请项目资金?" → ["项目资金申请流程", "经费申请步骤"] │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 并行检索 (asyncio.gather) │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ 查询1 检索 │ │ 查询2 检索 │ │ 查询3 检索 │ │ -│ │ (k=20) │ │ (k=20) │ │ (k=20) │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ reciprocal_rank_fusion (RRF) │ -│ RRF_score(d) = Σ 1/(k + rank_q(d)) (k=60) │ -│ 融合多路检索结果,去重排序 │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ LLaMaCPPReranker │ -│ 远程重排序 (bge-reranker-v2-m3) │ -│ 返回 Top-N (top_n=5) 最相关文档 │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 返回增强上下文 │ -│ format_context() → 格式化输出 │ -└─────────────────────────────────────────────────────────────┘ + 用户查询 + ↓ + ┌───────────────────┐ + │ MultiQueryGenerator │ (可选) + │ 多路查询改写 │ + └─────────┬─────────┘ + ↓ + ┌───────────────────┐ + │ 并行检索 │ + │ (asyncio.gather) │ + └─────────┬─────────┘ + ↓ + ┌───────────────────┐ + │ Qdrant 混合检索 │ + │ (dense + sparse) │ + └─────────┬─────────┘ + ↓ + ┌───────────────────┐ + │ RRF 分数融合 │ (Qdrant 原生) + └─────────┬─────────┘ + ↓ + ┌───────────────────┐ + │ 重排序 (可选) │ + └─────────┬─────────┘ + ↓ + ┌───────────────────┐ + │ 父文档获取 │ + └─────────┬─────────┘ + ↓ + 增强上下文 ``` ### 技术特性 +- ✅ **Qdrant 原生混合检索**:稠密向量 + BM25 稀疏向量,服务端 RRF 分数融合 +- ✅ **父子文档策略**:子块精准检索,父块提供完整上下文 - ✅ **多路查询改写**:通过 LLM 将单一问题改写为多个不同角度的查询 -- ✅ **RRF 融合算法**:Reciprocal Rank Fusion,无需评分归一化的融合算法 - ✅ **远程重排序**:使用 llama.cpp 服务的 OpenAI 兼容 Rerank API -- ✅ **混合检索支持**:稠密向量 + BM25 稀疏向量混合检索 -- ✅ **异步并行检索**:多路查询并行执行,提升检索速度 -- ✅ **优雅降级**:重排序器不可用时自动降级到基础融合结果 +- ✅ **完全异步化**:检索、融合、重排序全链路 async / await +- ✅ **统一服务接入**:所有模型服务通过 `model_services` 获取 ## 📂 架构与文件结构 ``` -app/rag/ +backend/app/rag/ ├── __init__.py -├── retriever.py # Qdrant 基础检索与混合检索 -├── reranker.py # llama.cpp 远程重排序器 -├── query_transform.py # 多路查询改写生成器 -├── fusion.py # RRF 倒数排名融合算法 -├── pipeline.py # RAG 流水线编排 -└── tools.py # LangChain Tool 封装 -``` +├── retriever.py # Qdrant 混合检索器(含父子文档支持) +├── rerank.py # llama.cpp 远程重排序器 +├── query_transform.py # 多路查询改写生成器 +├── fusion.py # RRF 融合算法(备用) +├── pipeline.py # RAG 流水线编排 +├── tools.py # LangChain Tool 封装 +├── evaluate.py # 评估工具 +└── README.md # 本文档 +backend/app/model_services/ +├── embedding_services.py # 嵌入服务 +├── chat_services.py # LLM 服务 +└── rerank_services.py # 重排序服务 + +backend/rag_core/ +├── vector_store.py # Qdrant 混合存储 +├── sparse_embedder.py # BM25 稀疏嵌入 +├── doc_store.py # PostgreSQL 文档存储 +└── ... # 其他核心组件 +``` ## 🎯 演进路线与算法详解 (Roadmap) ### Level 1: 基础向量搜索 (Basic Similarity Search) @@ -88,97 +91,39 @@ app/rag/ - **核心算法**: 近似最近邻搜索 (ANN, 常用 HNSW 算法)。将用户问题转化为向量后,计算它与库中向量的余弦相似度 (Cosine Similarity),取距离最近的 K 个块。 - **优缺点**: 速度极快。但只能捕捉"语义相似",如果用户搜索特定专有名词、编号、订单号,纯向量检索往往会失效(产生"幻觉"匹配)。 - **实现指南**: - - 使用 `rag_indexer.embedders.LlamaCppEmbedder` 作为嵌入模型 - - 使用 `app/rag/retriever.py` 中的 `create_base_retriever` 创建基础检索器 - - 配置 `search_kwargs={"k": 20}` 进行初步召回 - -```python -from app.rag.retriever import create_base_retriever - -retriever = create_base_retriever( - collection_name="rag_documents", - embeddings=embeddings, - search_kwargs={"k": 20} -) -docs = retriever.invoke("什么是 RAG?") -``` + - 使用 `model_services.embedding_services` 获取嵌入模型 + - 使用 `app.rag.retriever` 中的 `create_base_retriever` 创建基础检索器 + - 配置检索返回数量进行初步召回 ### Level 2: 混合检索与重排序(Hybrid Search + Reranker) 混合检索旨在结合向量的"语义泛化"与关键词的"精确匹配",随后利用重排序模型过滤噪声。 -**⚠️ 现状说明**: -- `create_hybrid_retriever` 函数已实现框架,能检测 Qdrant 集合是否有稀疏向量配置 -- 目前 Qdrant 集合**未配置**稀疏向量字段,混合检索会优雅降级为纯稠密检索 -- 如果需要启用完整混合检索,需: - 1. 使用 BM25 计算稀疏向量 - 2. 在 Qdrant 集合配置 sparse_vectors - 3. 更新索引器以同时存储稠密和稀疏向量 +**当前状态**: +- 混合检索已完全实现,使用 Qdrant 原生双向量存储 + RRF 分数融合 +- Qdrant 集合已配置稀疏向量字段,支持 dense + sparse 同步检索 +- 重排序器使用 llama.cpp 服务的 OpenAI 兼容 Rerank API -**1. 基础召回(纯稠密检索)** +**1. 混合检索(核心)**: +- **核心原理**: 结合基于 HNSW 的 Dense Vector 相似度检索与基于 TF-IDF 的 BM25 稀疏检索 (Sparse Vector) +- **实现指南**: 使用 `app.rag.retriever` 中的 `create_hybrid_retriever` 或 `create_parent_hybrid_retriever` 函数 +- **技术优势**: Qdrant 原生支持,无需客户端后处理,性能更优 -- **核心原理**: 结合基于 HNSW 的 Dense Vector 相似度搜索与基于 TF-IDF 的 BM25 稀疏检索 (Sparse Vector)。 -- **实现指南**: 使用 `app/rag/retriever.py` 中的 `create_hybrid_retriever` 函数,配置 `dense_k=10` 和 `sparse_k=10`,总召回 20 条结果。 - -```python -from app.rag.retriever import create_hybrid_retriever - -retriever = create_hybrid_retriever( - collection_name="rag_documents", - embeddings=embeddings, - dense_k=10, - sparse_k=10, - score_threshold=0.3 -) -``` - -**2. 二次精排 (Cross-Encoder)** - -- **核心原理**: 不同于双塔模型(分别算向量再求距离),交叉编码器将"用户问题 + 检索到的单例文档"拼接后整体输入 Transformer 模型,由模型直接输出 0~1 的相关性得分,精度极高。 -- **实现指南**: - - 使用 `app/rag/reranker.py` 中的 `LLaMaCPPReranker` 类,加载 `bge-reranker-v2-m3` 模型 - - 设置 `top_n=5` 保留最相关的 5 条结果 - -```python -from app.rag.reranker import LLaMaCPPReranker - -reranker = LLaMaCPPReranker( - base_url="http://127.0.0.1:8083", - api_key="your-api-key", - top_n=5 -) -sorted_docs = reranker.compress_documents(documents, query) -``` +**2. 二次精排 (Cross-Encoder)**: +- **核心原理**: 不同于双塔模型(分别算向量再求距离),交叉编码器将"用户问题 + 检索到的单例文档"拼接后整体输入 Transformer 模型,由模型直接输出 0~1 的相关性得分,精度极高 +- **实现指南**: 使用 `app.rag.rerank` 中的 `LLaMaCPPReranker` 类,设置 `top_n` 保留最相关结果 ### Level 3: RAG-Fusion (多路改写与倒数排名融合) RAG-Fusion 通过大模型发散思维,将单一问题改写为多个相似问题,扩大搜索面,再利用数学统计算法合并结果。 -**1. 多路查询改写** +**1. 多路查询改写**: +- **核心原理**: 克服用户初始提问词不达意或视角受限的问题 +- **实现指南**: 使用 `app.rag.query_transform` 中的 `MultiQueryGenerator` 类,配置 `num_queries` 生成不同角度的查询 -- **核心原理**: 克服用户初始提问词不达意或视角受限的问题。 -- **实现指南**: 使用 `app/rag/query_transform.py` 中的 `MultiQueryGenerator` 类,配置 `num_queries=3` 生成 3 个不同角度的查询。 - -```python -from app.rag.query_transform import MultiQueryGenerator - -generator = MultiQueryGenerator(llm=llm, num_queries=3) -queries = await generator.agenerate("如何申请项目资金?") -# 返回:["如何申请项目资金?", "项目资金申请流程是什么?", "申请项目经费需要哪些步骤?"] -``` - -**2. 倒数排名融合 (RRF)** - -- **核心原理**: RRF (Reciprocal Rank Fusion) 是一种无需评分归一化的融合算法。公式为 `RRF_score(d) = Σ 1/(k + rank_q(d))`,有效避免某一极端检索结果主导全局。 -- **实现指南**: 使用 `app/rag/fusion.py` 中的 `reciprocal_rank_fusion` 函数,配置 `k=60` 实现倒数排名融合。 - -```python -from app.rag.fusion import reciprocal_rank_fusion - -# 多个查询的检索结果 -doc_lists = [result1, result2, result3] -fused_docs = reciprocal_rank_fusion(doc_lists, k=60) -``` +**2. 倒数排名融合 (RRF)**: +- **核心原理**: RRF (Reciprocal Rank Fusion) 是一种无需评分归一化的融合算法。公式为 `RRF_score(d) = Σ 1/(k + rank_q(d))`,有效避免某一极端检索结果主导全局 +- **当前实现**: 使用 Qdrant 原生 FusionQuery 进行服务端 RRF 融合,性能更优 ### Level 4: Agentic RAG / Self-RAG (智能体与自我反思) @@ -203,25 +148,12 @@ fused_docs = reciprocal_rank_fusion(doc_lists, k=60) ### Level 5: GraphRAG 集成 (基于图和关系的 RAG) -- **核心原理**: 结合知识图谱的结构化关系和向量检索的语义相似度,解决跨文档复杂关系推理问题。 +- **核心原理**: 结合知识图谱的结构化关系和向量检索的语义相似度,解决跨文档复杂关系推理问题 - **实现指南**: - 使用 `langchain_community.graphs` 模块构建知识图谱 - 配置本地大模型(如 `Gemma-4-E4B`)用于实体关系抽取 - 实现混合检索逻辑,结合向量相似度和图路径分析 -```python -from langchain_community.graphs import Neo4jGraph -from langchain_experimental.graph_transformers import LLMGraphTransformer - -# 实体关系抽取 -transformer = LLMGraphTransformer(llm=local_llm) -graph_documents = transformer.convert_to_graph_documents(documents) - -# 存储到图数据库 -graph = Neo4jGraph(url="bolt://localhost:7687") -graph.add_graph_documents(graph_documents) -``` - ## 🔧 核心组件详解 ### 1. 检索器 (retriever.py) @@ -229,163 +161,104 @@ graph.add_graph_documents(graph_documents) 提供基于 Qdrant 的向量检索能力。 **基础检索器**: -```python -from app.rag.retriever import create_base_retriever - -retriever = create_base_retriever( - collection_name="rag_documents", - embeddings=embeddings, - search_kwargs={"k": 20} -) -``` +- 功能:纯稠密向量检索 +- 配置:集合名称、检索数量 **混合检索器**: -```python -from app.rag.retriever import create_hybrid_retriever +- 功能:稠密 + 稀疏向量混合检索,Qdrant 原生 RRF 融合 +- 配置:集合名称、检索数量、分数阈值(可选) -retriever = create_hybrid_retriever( - collection_name="rag_documents", - embeddings=embeddings, - dense_k=10, - sparse_k=10, - score_threshold=0.3 -) -``` +**父子文档混合检索器**: +- 功能:子块检索 → 父块获取,提供完整上下文 +- 配置:集合名称、检索数量 ### 2. 多路查询改写 (query_transform.py) 通过 LLM 将用户问题改写为多个不同版本,扩大搜索面。 -```python -from app.rag.query_transform import MultiQueryGenerator - -generator = MultiQueryGenerator(llm=llm, num_queries=3) -queries = await generator.agenerate("如何申请项目资金?") -``` +**配置**: +- LLM 服务:从 `model_services.chat_services` 获取 +- 查询数量:默认 3 个 ### 3. RRF 融合算法 (fusion.py) Reciprocal Rank Fusion 算法,公式:`RRF_score(d) = Σ 1/(k + rank_q(d))` -```python -from app.rag.fusion import reciprocal_rank_fusion +**当前状态**: +- 主要使用 Qdrant 原生 FusionQuery 进行服务端融合 +- 本模块为备用实现,用于特殊场景 -# 多个查询的检索结果 -doc_lists = [result1, result2, result3] -fused_docs = reciprocal_rank_fusion(doc_lists, k=60) -``` - -### 4. 重排序器 (reranker.py) +### 4. 重排序器 (rerank.py) 使用 llama.cpp 服务的 OpenAI 兼容 Rerank API 对检索结果重排序。 -```python -from app.rag.reranker import LLaMaCPPReranker - -reranker = LLaMaCPPReranker( - base_url="http://127.0.0.1:8083", - api_key="your-api-key", - top_n=5 -) -sorted_docs = reranker.compress_documents(documents, query) -``` +**配置**: +- 服务地址:从环境变量或配置获取 +- API Key:从环境变量或配置获取 +- Top N:返回最相关的 N 个结果 ### 5. RAG 流水线 (pipeline.py) 组合上述组件的完整检索流水线。 -```python -from app.rag.pipeline import RAGPipeline +**主要功能**: +- 多路查询改写(可选) +- 混合检索(支持父子文档) +- 重排序(可选) +- 上下文格式化 -pipeline = RAGPipeline( - retriever=retriever, - llm=llm, - num_queries=3, - rerank_top_n=5, -) - -# 异步检索 -docs = await pipeline.aretrieve("如何申请项目资金?") - -# 格式化上下文 -context = pipeline.format_context(docs) -``` +**配置选项**: +- 集合名称 +- LLM 服务 +- 是否使用多路改写 +- 是否使用重排序 +- 检索/重排序数量 ## 🔄 与 Agent 系统集成 ### 封装为 LangChain Tool -```python -from langchain_core.tools import tool -from app.rag.pipeline import RAGPipeline - -@tool -def search_knowledge_base(query: str) -> str: - """搜索知识库获取相关信息""" - docs = pipeline.retrieve(query) - return pipeline.format_context(docs) -``` +- 创建工具函数,用于搜索知识库获取相关信息 +- 工具描述要清晰,说明功能和用途 ### 绑定到 LangGraph -```python -from app.main_graph.utils.main_graph_builder import build_react_main_graph - -# 将 RAG 工具添加到工具列表 -tools = AVAILABLE_TOOLS + [search_knowledge_base] - -# 构建图 -graph = build_react_main_graph(llm, tools).compile(checkpointer=checkpointer) -``` +- 将 RAG 工具添加到 Agent 可用工具列表 +- 构建 LangGraph 图时传入工具列表 +- 配置检查点(可选)用于状态持久化 ## ⚙️ 环境配置 | 变量名 | 说明 | 默认值 | |:-------|:-----|:-------| -| `QDRANT_URL` | Qdrant 向量数据库地址 | `http://127.0.0.1:6333` | +| `QDRANT_URL` | Qdrant 向量数据库地址 | `http://115.190.121.151:6333` | | `QDRANT_API_KEY` | Qdrant API 密钥 | - | -| `LLAMACPP_RERANKER_URL` | llama.cpp 重排序服务地址 | `http://127.0.0.1:8083` | -| `LLAMACPP_API_KEY` | llama.cpp API 密钥 | - | +| `DB_HOST` | PostgreSQL 主机 | `115.190.121.151` | +| `DB_PORT` | PostgreSQL 端口 | `5432` | +| `DB_USER` | PostgreSQL 用户 | `postgres` | +| `DB_PASSWORD` | PostgreSQL 密码 | `postgres` | +| `DB_NAME` | PostgreSQL 数据库 | `rag_db` | +| `LLAMACPP_EMBEDDING_URL` | llama.cpp 嵌入服务地址 | `http://127.0.0.1:18001` | +| `LLAMACPP_RERANK_URL` | llama.cpp 重排序服务地址 | `http://127.0.0.1:18002` | +| `LLAMACPP_CHAT_URL` | llama.cpp 聊天服务地址 | `http://127.0.0.1:18000` | +| `LLAMACPP_API_KEY` | llama.cpp API 密钥 | `huang1998` | ## 🚀 快速开始 -```python -# 1. 初始化嵌入模型 -from rag_core.embedders import LlamaCppEmbedder -embedder = LlamaCppEmbedder() -embeddings = embedder.as_langchain_embeddings() - -# 2. 创建检索器 -from app.rag.retriever import create_base_retriever -retriever = create_base_retriever( - collection_name="rag_documents", - embeddings=embeddings, - search_kwargs={"k": 20} -) - -# 3. 创建 RAG 流水线 -from app.rag.pipeline import RAGPipeline -pipeline = RAGPipeline( - retriever=retriever, - llm=llm, - num_queries=3, - rerank_top_n=5, -) - -# 4. 执行检索 -docs = pipeline.retrieve("如何申请项目资金?") - -# 5. 格式化上下文 -context = pipeline.format_context(docs) -print(context) -``` +**步骤概览**: +1. 初始化模型服务:从 `model_services` 获取嵌入、LLM、重排序服务 +2. 创建检索器:选择 `create_base_retriever`、`create_hybrid_retriever` 或 `create_parent_hybrid_retriever` +3. 创建 RAG 流水线:配置是否使用多路改写、是否使用重排序等 +4. 执行检索:调用 `aretrieve` 进行异步检索 +5. 格式化上下文:调用 `format_context` 整理为 LLM 可用格式 ## 📊 检索策略对比 | 策略 | 优点 | 缺点 | 适用场景 | |:-----|:-----|:-----|:---------| | **基础向量检索** | 速度快,语义理解好 | 专有名词匹配差 | 通用问答 | -| **混合检索** | 语义 + 关键词匹配 | 需要配置稀疏向量 | 专业术语查询 | +| **混合检索** | 语义 + 关键词匹配 | - | 专业术语查询 | +| **父子文档混合检索** | 检索精准 + 上下文完整 | - | 生产环境通用 | | **多路改写 + RRF** | 搜索面广,结果稳定 | 延迟略高 | 复杂问题 | | **重排序** | 精度高 | 依赖额外模型 | 最终精排 | @@ -394,5 +267,6 @@ print(context) - **向量存储**:共享 Qdrant 集合,确保嵌入模型一致 - **文档存储**:使用 PostgreSQL 存储父块,通过 UUID 映射 - **集合名称**:默认使用 `rag_documents` 集合 +- **服务接入**:统一使用 `model_services` 获取模型服务 详见 [rag_indexer/README.md](../../rag_indexer/README.md) diff --git a/rag_indexer/README.md b/rag_indexer/README.md index 209a49a..078b2b2 100644 --- a/rag_indexer/README.md +++ b/rag_indexer/README.md @@ -126,17 +126,6 @@ backend/app/model_services/ - 实例化时设置 `chunk_size`(如 500)和 `chunk_overlap`(如 50) - 直接调用 `.split_documents(raw_docs)` 方法 -```python -from langchain_text_splitters import RecursiveCharacterTextSplitter - -splitter = RecursiveCharacterTextSplitter( - chunk_size=500, - chunk_overlap=50, - separators=["\n\n", "\n", "。", "!", "?", " ", ""] -) -chunks = splitter.split_documents(documents) -``` - ### Level 2: 语义动态切分 (Semantic Chunking) - **核心算法**: 句子级相似度阈值算法。 @@ -150,87 +139,35 @@ chunks = splitter.split_documents(documents) - 实现 `SemanticChunkerAdapter` 继承 `TextSplitter`,解决类型不兼容问题 - 实例化时需要传入已配置好的 Embedding 模型实例 -```python -from langchain_experimental.text_splitter import SemanticChunker - -chunker = SemanticChunker( - embeddings=embeddings, - breakpoint_threshold_type="percentile", - breakpoint_threshold_amount=95, - min_chunk_size=100 -) -chunks = chunker.split_documents(documents) -``` - ### Level 3: 高级父子块策略 (Parent-Child / Auto-merging) - **核心算法**: 层次化双重存储与映射(自定义实现)。 - **切分机制**: 首先将文档粗切为较大的"父块 (Parent Chunk, 约 1000 字符)",随后将父块细切为较小的"子块 (Child Chunk, 约 200 字符)" - - **存储机制**: + - **存储机制**: - **子块**: 存入 Qdrant,同时生成 dense 向量(语义)和 sparse 向量(关键词),payload 中包含 `parent_id` - **父块**: 存入 PostgreSQL,通过 UUID 与子块映射 - **核心思路**: 解决 RAG 领域经典的矛盾——检索时块越小越容易精确命中(去除噪声);但生成回答时,块越大越能给大模型提供充足的上下文背景。 -- **实现**: +- **实现**: - 完全自定义实现,不依赖 LangChain 的 `ParentDocumentRetriever` - 支持异步批量写入 - 支持双向量混合检索 -```python -from rag_indexer.index_builder import IndexBuilder, IndexBuilderConfig -from rag_indexer.splitters import SplitterType - -config = IndexBuilderConfig( - collection_name="rag_documents", - splitter_type=SplitterType.PARENT_CHILD, - parent_chunk_size=1000, - child_chunk_size=200, -) - -builder = IndexBuilder(config) -await builder.build_from_file("document.pdf") -``` - ### Level 3.1: PostgreSQL DocStore 集成 - **核心优势**: 利用 PostgreSQL 作为持久化存储,适合生产环境。使用异步连接池,支持高并发。 - **实现步骤**: 1. **配置连接**: 设置 `DB_URI` 环境变量或通过 `docstore_conn_string` 参数指定 - 2. **创建 docstore**: 使用 `rag_indexer.store.create_docstore()` 工厂函数 + 2. **创建 docstore**: 使用 `rag_core.doc_store.create_docstore()` 工厂函数 3. **注入到 IndexBuilder**: 通过构造函数参数注入 -```python -from rag_indexer.store import create_docstore - -docstore, conn_info = create_docstore( - connection_string="postgresql://user:pass@host:5432/db", - pool_config={"min_size": 5, "max_size": 20}, -) -``` - ### Level 3.2: 语义切分与父子块策略结合 - **核心优势**: 结合语义切分的连贯性和父子块策略的层次化存储优势,实现更精准的检索和更丰富的上下文。 - **实现原理**: - - **父块切分**: 使用 `RecursiveCharacterTextSplitter` 创建大块(约1000字符),提供完整的上下文背景 + - **父块切分**: 使用 `RecursiveCharacterTextSplitter` 创建大块(约 1000 字符),提供完整的上下文背景 - **子块切分**: 使用 `SemanticChunkerAdapter` 创建小块,根据语义连贯性动态切分,提高检索精度 - **存储机制**: 子块向量存入 Qdrant 用于精准检索,父块内容存入 PostgreSQL 提供完整上下文 -```python -from rag_indexer.index_builder import IndexBuilder, IndexBuilderConfig -from rag_indexer.splitters import SplitterType - -config = IndexBuilderConfig( - collection_name="rag_documents", - splitter_type=SplitterType.PARENT_CHILD, - parent_chunk_size=1000, - child_chunk_size=200, - child_splitter_type=SplitterType.SEMANTIC, # 子块使用语义切分 - docstore=DocstoreConfig( - connection_string="postgresql://user:***@host:5432/db", - ), -) -``` - ### Level 3.3: 混合检索架构(稠密 + 稀疏) - **核心算法**: Qdrant 原生双向量存储 + RRF 分数融合 @@ -243,19 +180,6 @@ config = IndexBuilderConfig( - 使用 Qdrant 的 `query_points` API + `Prefetch` 并行检索 - 通过 `FusionQuery` 自动进行 RRF 分数融合 -```python -from app.rag.retriever import create_parent_hybrid_retriever - -# 创建父子文档混合检索器 -retriever = create_parent_hybrid_retriever( - collection_name="rag_documents", - search_k=5 -) - -# 异步检索相关文档 -docs = await retriever.ainvoke("用户查询") -``` - --- ## 📦 存储结构详解 @@ -313,42 +237,14 @@ CREATE TABLE parent_documents ( ### Qdrant 存储结构(子文档) -#### 集合配置 +**集合配置**: +- 支持 dense 向量配置:根据嵌入模型输出维度,距离函数使用 Cosine +- 支持 sparse 向量配置:BM25 稀疏向量 -```python -vectors_config = { - "dense": VectorParams( - size=2048, # 或 1024、4096,取决于嵌入模型 - distance=Distance.COSINE - ) -} - -sparse_vectors_config = { - "sparse": SparseVectorParams() -} -``` - -#### 点结构(Point) - -```json -{ - "id": "child-12345", - "vector": { - "dense": [0.123, 0.456, ...], - "sparse": { - "indices": [10, 50, 234, ...], - "values": [0.8, 0.5, 0.3, ...] - } - }, - "payload": { - "text": "这是一个子文档块,用于检索...", - "parent_id": "parent-12345", - "source": "file_name.pdf", - "page": 10, - "chunk_index": 0 - } -} -``` +**点结构(Point)**: +- `id`: 子文档唯一标识 +- `vector`: 包含 dense 和 sparse 双向量 +- `payload`: 包含文本内容、parent_id、来源元数据 --- @@ -504,48 +400,18 @@ graph.add_graph_documents(graph_documents) **支持格式**:PDF、DOCX、DOC、TXT、MD、HTML、PPTX、XLSX、JSON -```python -from rag_indexer.loaders import DocumentLoader - -loader = DocumentLoader( - strategy="auto", # 解析策略:auto/fast/hi_res/ocr_only - ocr_languages=["chi_sim", "eng"], # OCR 语言 - languages=["zh"], # 文档主语言 - extract_images=False, # 是否提取图片 - pdf_infer_table_structure=True, # 是否识别表格 -) - -# 加载单个文件 -docs = loader.load_file("document.pdf") - -# 加载整个目录 -docs = loader.load_directory("./docs/", recursive=True) -``` - ### 2. 文本切分器 (splitters.py) 提供三种切分策略: **递归字符切分**: -```python -from rag_indexer.splitters import SplitterType, get_splitter - -splitter = get_splitter( - SplitterType.RECURSIVE, - chunk_size=500, - chunk_overlap=50, -) -``` +- 使用 `SplitterType.RECURSIVE` 类型 +- 可配置 `chunk_size` 和 `chunk_overlap` **语义切分**: -```python -splitter = get_splitter( - SplitterType.SEMANTIC, - embeddings=embeddings, - breakpoint_threshold_type="percentile", - min_chunk_size=100, -) -``` +- 使用 `SplitterType.SEMANTIC` 类型 +- 基于句子相似度阈值动态切分 +- 需要 Embedding 模型支持 **父子块策略**:在 `IndexBuilder` 中自动配置。 @@ -553,64 +419,31 @@ splitter = get_splitter( 核心编排模块,串联整个索引构建流程。 -```python -from rag_indexer.index_builder import IndexBuilder, IndexBuilderConfig, DocstoreConfig -from rag_indexer.splitters import SplitterType - -# 配置 -config = IndexBuilderConfig( - collection_name="rag_documents", - splitter_type=SplitterType.PARENT_CHILD, - parent_chunk_size=1000, - child_chunk_size=200, - child_splitter_type=SplitterType.SEMANTIC, - docstore=DocstoreConfig( - connection_string="postgresql://user:pass@host:5432/db", - ), -) - -# 构建索引 -async with IndexBuilder(config) as builder: - # 从单个文件构建 - count = await builder.build_from_file("document.pdf") - - # 或从目录批量构建 - count = await builder.build_from_directory("./docs/") - - print(f"已索引 {count} 个文档") -``` +**主要功能**: +- 支持单块切分模式和父子块切分模式 +- 自动管理 PostgreSQL 文档存储和 Qdrant 向量存储 +- 支持异步批量写入和重试机制 +- 提供上下文管理器资源管理 ### 4. 向量存储 (vector_store.py) 封装 Qdrant 向量数据库操作。 -```python -from rag_core import QdrantHybridStore +**主要功能**: +- 创建和管理向量集合 +- 支持 dense 和 sparse 双向量写入 +- 提供同步和异步客户端 +- 自动处理批量操作和重试 -vector_store = QdrantHybridStore( - collection_name="rag_documents", - embeddings=embeddings, -) - -# 创建集合 -vector_store.create_collection() - -# 添加文档 -vector_store.add_documents(chunks) -``` - -### 5. PostgreSQL DocStore (store/postgres.py) +### 5. PostgreSQL DocStore (doc_store.py) 持久化存储父块内容,支持异步连接池。 -```python -from rag_core.store import create_docstore - -docstore, conn_info = create_docstore( - connection_string="postgresql://user:pass@host:5432/db", - pool_config={"min_size": 5, "max_size": 20}, -) -``` +**主要功能**: +- 异步连接池管理 +- 文档的增删改查 +- 批量操作支持 +- UUID 映射管理 ## 📊 切分策略对比 @@ -624,37 +457,18 @@ docstore, conn_info = create_docstore( ### 命令行方式 -```bash -# 设置环境变量 -export QDRANT_URL="http://115.190.121.151:6333" -export DB_URI="postgresql://postgres:password@host:5432/langgraph_db?sslmode=disable" - -# 执行索引构建 -python -m rag_indexer.cli --path data/user_docs/ --recursive -``` +使用 `rag_indexer/cli.py` 提供的命令行工具: +- `build`: 从文件或目录构建索引 +- `clear`: 清空指定 Qdrant 集合 +- `reset`: 重置指定 Qdrant 集合 ### Python API 方式 -```python -import asyncio -from rag_indexer.index_builder import IndexBuilder, IndexBuilderConfig, DocstoreConfig -from rag_indexer.splitters import SplitterType - -async def main(): - config = IndexBuilderConfig( - collection_name="rag_documents", - splitter_type=SplitterType.PARENT_CHILD, - parent_chunk_size=1000, - child_chunk_size=200, - child_splitter_type=SplitterType.SEMANTIC, - ) - - async with IndexBuilder(config) as builder: - count = await builder.build_from_directory("./user_docs/") - print(f"索引构建完成,共处理 {count} 个文档") - -asyncio.run(main()) -``` +使用 `IndexBuilder` 类进行程序化索引构建: +- 配置 `IndexBuilderConfig` 设置切分策略和存储参数 +- 使用 `build_from_file()` 从单个文件构建 +- 使用 `build_from_directory()` 从目录批量构建 +- 推荐使用异步上下文管理器 `async with` 自动管理资源 ## ⚙️ 环境配置 @@ -662,54 +476,44 @@ asyncio.run(main()) |:-------|:-----|:-------| | `QDRANT_URL` | Qdrant 向量数据库地址 | `http://127.0.0.1:6333` | | `QDRANT_API_KEY` | Qdrant API 密钥 | - | -| `DB_URI` | PostgreSQL 连接字符串 | - | -| `LLAMACPP_EMBEDDING_URL` | Embedding 服务地址 | `http://127.0.0.1:8082/v1` | -| `LLAMACPP_API_KEY` | llama.cpp API 密钥 | - | +| `DB_HOST` | PostgreSQL 主机 | `127.0.0.1` | +| `DB_PORT` | PostgreSQL 端口 | `5432` | +| `DB_USER` | PostgreSQL 用户 | `postgres` | +| `DB_PASSWORD` | PostgreSQL 密码 | `postgres` | +| `DB_NAME` | PostgreSQL 数据库 | `rag_db` | +| `LLAMACPP_EMBEDDING_URL` | Embedding 服务地址 | `http://127.0.0.1:18001` | +| `LLAMACPP_API_KEY` | llama.cpp API 密钥 | `huang1998` | ## 🔄 与 app/rag 集成 - **向量存储**:共享 Qdrant 集合,确保嵌入模型一致 - **文档存储**:父块存入 PostgreSQL,通过 UUID 与子块关联 - **集合名称**:默认使用 `rag_documents` 集合 -- **嵌入模型**:使用相同的 `LlamaCppEmbedder` 确保向量空间一致 +- **服务接入**:使用 `model_services` 统一获取嵌入、LLM、重排序服务 -详见 [app/rag/README.md](../app/rag/README.md) +详见 [app/rag/README.md](../backend/app/rag/README.md) ## 📝 高级配置 ### 自定义切分参数 -```python -config = IndexBuilderConfig( - collection_name="my_docs", - splitter_type=SplitterType.PARENT_CHILD, - parent_chunk_size=1500, # 更大的父块 - child_chunk_size=300, # 更大的子块 - parent_chunk_overlap=150, # 父块重叠 - child_chunk_overlap=30, # 子块重叠 - search_k=10, # 检索返回数量 -) -``` +`IndexBuilderConfig` 支持以下配置: +- `collection_name`: 集合名称 +- `splitter_type`: 切分器类型(RECURSIVE/SEMANTIC/PARENT_CHILD) +- `parent_chunk_size`: 父块大小(默认 1000) +- `child_chunk_size`: 子块大小(默认 200) +- `parent_chunk_overlap`: 父块重叠 +- `child_chunk_overlap`: 子块重叠 +- `child_splitter_type`: 子块切分器类型 +- `search_k`: 检索返回数量 ### 批量处理与重试 索引构建器内置自动重试机制,处理网络波动: - - 最大重试次数:5 次 - 退避策略:指数退避(2s, 4s, 8s, 16s, 32s) - 批量大小:10 个文档/批次 ### 资源管理 -```python -# 方式一:上下文管理器(推荐) -async with IndexBuilder(config) as builder: - await builder.build_from_directory("./docs/") - -# 方式二:手动管理 -builder = IndexBuilder(config) -try: - await builder.build_from_directory("./docs/") -finally: - await builder.aclose() -``` +推荐使用异步上下文管理器自动管理资源,也支持手动 `await builder.aclose()` 释放。