This commit is contained in:
@@ -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()` 释放。
|
||||
|
||||
Reference in New Issue
Block a user