修改readme
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Failing after 6m35s

This commit is contained in:
2026-05-05 14:06:36 +08:00
parent d050dcf835
commit 8b5fbbd395
3 changed files with 198 additions and 521 deletions

View File

@@ -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()` 释放。