init: RAG 知识库服务初始提交

- 后端 API(Flask + Gunicorn)
- RAG 引擎(混合检索 + 云端 Reranker + 引用溯源)
- 文档解析(MinerU + 多格式支持)
- Docker 生产部署配置
- 排除前端项目、敏感配置、模型文件
This commit is contained in:
lacerate551
2026-06-04 17:35:27 +08:00
commit 100d1a06eb
158 changed files with 64534 additions and 0 deletions

View File

@@ -0,0 +1,785 @@
# 企业文档更新管理方案
> 本文档合并了企业文档管理的多种方案比较分析包括增量更新、完整版本管理和软删除等以及目前项目中所采纳的“方案C智能全量更新”的具体实现细则。
## 第一部分:文档更新管理方案横评
# 企业文档管理方案分析与建议
> 针对企业文档部分更新、文件废止等场景的最佳实践
---
## 📋 企业文档管理的实际需求
### 典型场景
1. **文档部分更新**
- 制度文件修订报销制度第3条修改
- 附件更新(如:报销单模板更新)
- 内容勘误(如:错别字修正)
2. **文档废止**
- 旧制度失效2023年报销制度被2024年版本替代
- 临时文件过期(如:疫情期间的临时政策)
- 部门撤销(如:某部门解散,相关文档废止)
3. **版本管理**
- 多版本共存(如:新旧制度过渡期)
- 历史追溯(如:查询某个时间点的制度内容)
- 变更记录(如:审计需要查看修改历史)
---
## 🔍 当前实现分析
### 现有机制sync.py
**优点**
- ✅ 自动检测文件变更(新增、修改、删除)
- ✅ 基于文件 Hash 判断内容是否变化
- ✅ 支持多向量库(按目录自动分类)
- ✅ 实时监控文件系统变化
**处理策略**
```python
# 当前的"全量更新"策略
if change.change_type == ChangeType.MODIFIED:
# 1. 删除旧文档的所有 chunks
deleted = kb_manager.delete_document(kb_name, filename)
# 2. 重新解析并添加新文档的所有 chunks
chunks_added = kb_manager.add_file_to_kb(kb_name, filepath)
```
**问题**
- ❌ 即使只修改一个字,也要重新解析整个文档
- ❌ 删除所有旧 chunks可能影响正在使用的查询
- ❌ 没有保留历史版本
- ❌ 无法追溯变更内容
---
## 💡 解决方案对比
### 方案 A增量更新diff.py 的设计思路)
**原理**
```python
# 1. 解析新旧文档
old_chunks = parse_document(old_version)
new_chunks = parse_document(new_version)
# 2. 计算差异
diff = DocumentDiffAnalyzer().compute_diff(old_chunks, new_chunks)
# 3. 增量更新
for chunk in diff.added:
kb_manager.add_chunk(chunk) # 只添加新增的
for chunk in diff.deleted:
kb_manager.delete_chunk(chunk.id) # 只删除被删的
for chunk in diff.modified:
kb_manager.update_chunk(chunk.id, chunk.new_content) # 只更新修改的
```
**优点**
- ✅ 性能优化:只处理变化的部分
- ✅ 减少重复计算:不需要重新 Embedding 未变化的内容
- ✅ 平滑过渡:不影响正在使用的 chunks
**缺点**
- ❌ 实现复杂:需要精确匹配新旧 chunks
- ❌ 匹配困难:文档结构变化时难以对应
- ❌ 边界问题chunk 边界变化导致误判
- ❌ 维护成本高525 行代码,逻辑复杂
**适用场景**
- 超大文档1000+ 页)
- 频繁小改动(每天多次更新)
- 对性能要求极高
**企业实际情况**
- ❌ 大部分企业文档 < 100 页
- ❌ 更新频率低(每月/每季度)
- ❌ 全量更新耗时可接受(几秒到几十秒)
---
### 方案 B版本管理 + 软删除lifecycle.py 的设计思路)
**原理**
```python
# 1. 保留所有版本
document_versions = [
{"version": "v1", "status": "superseded", "upload_time": "2023-01-01"},
{"version": "v2", "status": "superseded", "upload_time": "2023-06-01"},
{"version": "v3", "status": "active", "upload_time": "2024-01-01"}
]
# 2. 查询时只返回 active 版本
chunks = kb_manager.query(kb_name, query, filter={"status": "active"})
# 3. 废止文档(软删除)
lifecycle_manager.deprecate_document(kb_name, doc_id, reason="制度已更新")
# 实际操作:将 status 改为 "deprecated",不删除数据
```
**优点**
- ✅ 历史追溯:可以查询任意时间点的内容
- ✅ 安全回滚:废止操作可逆
- ✅ 审计友好:完整的变更记录
- ✅ 过渡期支持:新旧版本可以共存
**缺点**
- ❌ 存储成本:保留所有历史版本
- ❌ 查询复杂:需要过滤 status
- ❌ 数据膨胀:向量库体积增大
**适用场景**
- 合规要求高(金融、医疗)
- 需要审计追溯
- 文档变更频繁但需要保留历史
---
### 方案 C智能全量更新推荐
**原理**
```python
# 1. 检测变更
if file_hash_changed:
# 2. 标记旧版本(软删除)
kb_manager.mark_document_as_deprecated(kb_name, doc_id, version="v1")
# 3. 添加新版本
kb_manager.add_file_to_kb(
kb_name,
filepath,
extra_metadata={
"status": "active",
"version": "v2",
"previous_version": "v1",
"change_reason": "制度修订"
}
)
# 4. 异步清理旧版本(可选)
schedule_cleanup(kb_name, doc_id, version="v1", delay="7 days")
```
**优点**
- ✅ 实现简单:基于现有 sync.py
- ✅ 性能可接受:全量更新耗时短
- ✅ 可靠性高:不依赖复杂的 diff 算法
- ✅ 灵活性好:可选保留历史版本
**缺点**
- ⚠️ 短暂的双份数据(新旧版本共存期间)
**适用场景**
- ✅ 大部分企业场景
- ✅ 文档更新频率适中
- ✅ 对性能要求不极端
---
## 📊 方案对比总结
| 方案 | 实现复杂度 | 性能 | 存储成本 | 历史追溯 | 适用场景 |
|------|-----------|------|---------|---------|---------|
| **A. 增量更新** | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐⭐ 优 | ⭐⭐⭐⭐⭐ 低 | ❌ 无 | 超大文档、频繁更新 |
| **B. 完整版本管理** | ⭐⭐⭐⭐ 中高 | ⭐⭐⭐ 中 | ⭐⭐ 高 | ✅ 完整 | 金融、医疗等合规场景 |
| **C. 智能全量更新** | ⭐⭐ 低 | ⭐⭐⭐⭐ 良 | ⭐⭐⭐⭐ 中 | ✅ 可选 | **大部分企业场景** ⭐ |
---
## 🎯 最终建议
### 推荐方案:方案 C智能全量更新 + 轻量级版本管理)
**理由**
1.**实现简单**:基于现有 sync.py增量开发
2.**性能足够**:全量更新耗时可接受(秒级)
3.**功能完整**:支持版本管理、软删除、历史追溯
4.**维护成本低**:逻辑清晰,不易出错
5.**适用性广**:覆盖 90% 的企业场景
**不推荐**
- ❌ 方案 A增量更新实现复杂收益不明显
- ⚠️ 方案 B完整版本管理存储成本高大部分企业用不到
### 具体操作
1. **删除 diff.py**525 行)
2. **简化 lifecycle.py**(保留 ~200 行核心功能)
3. **增强 sync.py**(添加版本管理逻辑)
4. **补充 API**(废止、恢复、历史查询)
**预期效果**
- 减少 ~800 行冗余代码
- 保留实际需要的功能
- 满足企业文档管理需求
---
**文档版本**: v1.0
**创建时间**: 2026-04-20
**维护者**: RAG 服务开发组
---
## 第二部分选定方案方案C详细落地落实说明
# 方案 C文档、废止状态与向量库关系详解
> 详细说明智能全量更新方案的数据流和状态管理
---
## 📊 核心概念
### 三个层次
1. **物理层**`documents/` 目录(文件系统)
2. **逻辑层**:文档状态管理(数据库)
3. **检索层**向量库ChromaDB/Milvus
---
## 🔄 完整数据流图
```
┌─────────────────────────────────────────────────────────────┐
│ 1. 物理层documents/ │
│ │
│ documents/ │
│ ├── public/ │
│ │ ├── 报销制度_v1.pdf ← 旧版本(物理存在) │
│ │ ├── 报销制度_v2.pdf ← 新版本(物理存在) │
│ │ └── 临时防疫政策.pdf ← 已废止(物理存在) │
│ └── finance/ │
│ └── 差旅管理办法.pdf │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. 逻辑层document_versions 表 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ kb_name │ document_id │ version │ status │ │
│ ├─────────┼────────────────────┼─────────┼─────────────┤ │
│ │ public │ 报销制度_v1.pdf │ v1 │ superseded │ │
│ │ public │ 报销制度_v2.pdf │ v2 │ active │ │
│ │ public │ 临时防疫政策.pdf │ v1 │ deprecated │ │
│ │ finance │ 差旅管理办法.pdf │ v1 │ active │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. 检索层向量库ChromaDB
│ │
│ Collection: public_kb │
│ ┌────────────────────────────────────────────────────┐ │
│ │ chunk_id │ content │ metadata │ │
│ ├──────────┼──────────────┼───────────────────────────┤ │
│ │ c1 │ 报销流程... │ {doc: 报销制度_v1.pdf, │ │
│ │ │ │ status: superseded, │ │
│ │ │ │ version: v1} │ │
│ ├──────────┼──────────────┼───────────────────────────┤ │
│ │ c2 │ 报销流程... │ {doc: 报销制度_v2.pdf, │ │
│ │ │ │ status: active, │ │
│ │ │ │ version: v2} │ │
│ ├──────────┼──────────────┼───────────────────────────┤ │
│ │ c3 │ 防疫要求... │ {doc: 临时防疫政策.pdf, │ │
│ │ │ │ status: deprecated, │ │
│ │ │ │ version: v1} │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 📝 详细场景说明
### 场景 1文档更新报销制度 v1 → v2
#### 步骤 1用户上传新版本
```bash
# 用户操作
documents/public/报销制度_v2.pdf # 上传新文件
```
#### 步骤 2系统检测变更
```python
# sync.py 自动检测
change = DocumentChange(
document_id="public/报销制度_v2.pdf",
change_type=ChangeType.ADDED,
new_hash="abc123..."
)
```
#### 步骤 3处理更新
```python
# 方案 C 的处理逻辑
def process_document_update(kb_name, old_doc_id, new_doc_path):
# 1. 标记旧版本为 superseded不删除
kb_manager.update_metadata(
kb_name="public",
filter={
"document_id": "报销制度_v1.pdf",
"status": "active"
},
update={
"status": "superseded",
"superseded_by": "报销制度_v2.pdf",
"superseded_time": "2024-01-15 10:00:00"
}
)
# 2. 添加新版本
chunks_added = kb_manager.add_file_to_kb(
kb_name="public",
filepath="documents/public/报销制度_v2.pdf",
extra_metadata={
"status": "active",
"version": "v2",
"previous_version": "v1",
"document_id": "报销制度_v2.pdf",
"upload_time": "2024-01-15 10:00:00"
}
)
# 3. 记录版本历史
db.insert_version_record({
"kb_name": "public",
"document_id": "报销制度_v2.pdf",
"version": "v2",
"status": "active",
"previous_version": "v1",
"change_reason": "制度修订"
})
# 4. 可选7天后清理旧版本
schedule_cleanup(
kb_name="public",
document_id="报销制度_v1.pdf",
delay_days=7
)
```
#### 步骤 4查询时的效果
```python
# 用户查询:"报销流程是什么?"
results = kb_manager.query_kb(
kb_name="public",
query="报销流程是什么",
top_k=5,
where_filter={"status": "active"} # 只查询 active 状态
)
# 返回结果:
# ✅ 报销制度_v2.pdf 的内容(新版本)
# ❌ 报销制度_v1.pdf 的内容(被过滤掉)
```
#### 数据状态对比
**物理层documents/**
```
documents/public/
├── 报销制度_v1.pdf ← 仍然存在(用户可能需要查看旧版)
└── 报销制度_v2.pdf ← 新版本
```
**逻辑层document_versions 表)**
```sql
-- 旧版本记录
INSERT INTO document_versions VALUES (
'public', '报销制度_v1.pdf', 'v1', 'superseded',
'2023-01-01', '被 v2 替代'
);
-- 新版本记录
INSERT INTO document_versions VALUES (
'public', '报销制度_v2.pdf', 'v2', 'active',
'2024-01-15', NULL
);
```
**检索层(向量库)**
```python
# 旧版本 chunksstatus=superseded查询时被过滤
{
"chunk_id": "c1",
"content": "报销流程:先填写申请单...",
"metadata": {
"document_id": "报销制度_v1.pdf",
"status": "superseded", # ← 关键字段
"version": "v1"
}
}
# 新版本 chunksstatus=active查询时返回
{
"chunk_id": "c2",
"content": "报销流程:使用新系统提交...",
"metadata": {
"document_id": "报销制度_v2.pdf",
"status": "active", # ← 关键字段
"version": "v2"
}
}
```
---
### 场景 2文档废止临时防疫政策失效
#### 步骤 1管理员废止文档
```python
# API 调用
POST /api/kb/public/documents/临时防疫政策.pdf/deprecate
{
"reason": "疫情结束,政策失效"
}
```
#### 步骤 2系统处理废止
```python
def deprecate_document(kb_name, doc_id, reason):
# 1. 更新向量库 metadata
kb_manager.update_metadata(
kb_name="public",
filter={
"document_id": "临时防疫政策.pdf",
"status": "active"
},
update={
"status": "deprecated",
"deprecated_reason": "疫情结束,政策失效",
"deprecated_time": "2024-01-20 15:00:00"
}
)
# 2. 更新版本表
db.update_version_status(
kb_name="public",
document_id="临时防疫政策.pdf",
status="deprecated",
reason="疫情结束,政策失效"
)
# 3. 记录废止日志
db.insert_change_log({
"kb_name": "public",
"document_id": "临时防疫政策.pdf",
"change_type": "deprecate",
"reason": "疫情结束,政策失效",
"operator": "admin"
})
```
#### 步骤 3查询时的效果
```python
# 用户查询:"防疫政策是什么?"
results = kb_manager.query_kb(
kb_name="public",
query="防疫政策是什么",
top_k=5,
where_filter={"status": "active"} # 只查询 active 状态
)
# 返回结果:
# ❌ 临时防疫政策.pdf 的内容(被过滤掉)
# ✅ 其他 active 状态的文档
```
#### 数据状态
**物理层documents/**
```
documents/public/
└── 临时防疫政策.pdf ← 仍然存在(可能需要归档)
```
**逻辑层document_versions 表)**
```sql
UPDATE document_versions
SET status = 'deprecated',
status_reason = '疫情结束,政策失效',
deprecated_time = '2024-01-20 15:00:00'
WHERE kb_name = 'public'
AND document_id = '临时防疫政策.pdf';
```
**检索层(向量库)**
```python
# 废止后的 chunksstatus=deprecated查询时被过滤
{
"chunk_id": "c3",
"content": "疫情期间需要佩戴口罩...",
"metadata": {
"document_id": "临时防疫政策.pdf",
"status": "deprecated", # ← 关键字段
"deprecated_reason": "疫情结束,政策失效"
}
}
```
---
### 场景 3恢复已废止的文档
#### 步骤 1管理员恢复文档
```python
# API 调用
POST /api/kb/public/documents/临时防疫政策.pdf/restore
{
"reason": "疫情反复,政策恢复"
}
```
#### 步骤 2系统处理恢复
```python
def restore_document(kb_name, doc_id, reason):
# 1. 更新向量库 metadata
kb_manager.update_metadata(
kb_name="public",
filter={
"document_id": "临时防疫政策.pdf",
"status": "deprecated"
},
update={
"status": "active",
"restored_reason": "疫情反复,政策恢复",
"restored_time": "2024-02-01 09:00:00"
}
)
# 2. 更新版本表
db.update_version_status(
kb_name="public",
document_id="临时防疫政策.pdf",
status="active",
reason="疫情反复,政策恢复"
)
```
#### 步骤 3查询时的效果
```python
# 用户查询:"防疫政策是什么?"
results = kb_manager.query_kb(
kb_name="public",
query="防疫政策是什么",
top_k=5,
where_filter={"status": "active"}
)
# 返回结果:
# ✅ 临时防疫政策.pdf 的内容(已恢复)
```
---
## 🔍 查询行为详解
### 默认查询(只返回 active 文档)
```python
def query_kb(self, kb_name: str, query: str, top_k: int = 5):
"""默认查询:只返回生效的文档"""
results = self.collection.query(
query_texts=[query],
n_results=top_k,
where={
"status": "active" # ← 自动过滤
}
)
return results
```
**效果**
- ✅ 返回报销制度_v2.pdfactive
- ❌ 过滤报销制度_v1.pdfsuperseded
- ❌ 过滤:临时防疫政策.pdfdeprecated
### 历史查询(包含所有版本)
```python
def query_kb_with_history(self, kb_name: str, query: str, top_k: int = 5):
"""历史查询:包含所有版本"""
results = self.collection.query(
query_texts=[query],
n_results=top_k,
where={
"status": {"$in": ["active", "superseded", "deprecated"]}
}
)
return results
```
**效果**
- ✅ 返回报销制度_v2.pdfactive
- ✅ 返回报销制度_v1.pdfsuperseded
- ✅ 返回:临时防疫政策.pdfdeprecated
### 特定版本查询
```python
def query_specific_version(self, kb_name: str, query: str, version: str):
"""查询特定版本"""
results = self.collection.query(
query_texts=[query],
n_results=5,
where={
"version": version # 指定版本
}
)
return results
```
---
## 📊 数据清理策略
### 自动清理(可选)
```python
def schedule_cleanup(kb_name: str, doc_id: str, delay_days: int = 7):
"""
定期清理 superseded 版本
策略:
1. 保留最近 7 天的 superseded 版本(防止误操作)
2. 7 天后自动删除向量库中的 chunks
3. 保留版本记录document_versions 表)
"""
# 7 天后执行
schedule_task(
task=lambda: kb_manager.delete_chunks(
kb_name=kb_name,
filter={
"document_id": doc_id,
"status": "superseded"
}
),
delay=timedelta(days=delay_days)
)
```
### 手动清理
```python
# API 端点
POST /api/kb/public/cleanup
{
"strategy": "superseded", # 清理 superseded 版本
"older_than_days": 30 # 超过 30 天的
}
```
---
## 🎯 关键优势
### 1. 物理层与逻辑层分离
**物理层documents/**
- 文件可以保留(用户可能需要下载旧版)
- 文件可以删除(不影响向量库)
- 灵活管理
**逻辑层(向量库)**
- 通过 metadata 控制可见性
- 不需要物理删除
- 支持快速恢复
### 2. 查询时自动过滤
```python
# 用户无感知,系统自动过滤废止文档
results = query_kb(kb_name, query) # 只返回 active 文档
```
### 3. 历史可追溯
```python
# 管理员可以查询历史版本
history = get_document_history(kb_name, doc_id)
# 返回v1 (superseded), v2 (active)
```
### 4. 操作可逆
```python
# 废止操作可以恢复
deprecate_document(kb_name, doc_id) # 废止
restore_document(kb_name, doc_id) # 恢复
```
---
## 📈 存储成本分析
### 短期7天内
```
向量库大小 = active 文档 + superseded 文档7天内
存储成本 = 1.2x ~ 1.5x(相比只保留 active
```
### 长期7天后自动清理
```
向量库大小 = active 文档
存储成本 = 1.0x(与只保留 active 相同)
```
### 版本记录(永久保留)
```
document_versions 表大小 = 每个版本 ~1KB
100 个文档 × 平均 3 个版本 = 300KB可忽略
```
---
## ✅ 总结
### 方案 C 的核心特点
1. **物理层**documents/ 目录可以保留或删除文件,不影响向量库
2. **逻辑层**:通过 status 字段控制文档可见性
3. **检索层**:查询时自动过滤非 active 文档
4. **历史追溯**:保留版本记录,支持审计
5. **操作可逆**:废止/恢复操作不删除数据
6. **自动清理**:定期清理旧版本,控制存储成本
### 与现有方案的区别
| 方面 | 当前方案 | 方案 C |
|------|---------|--------|
| 文档更新 | 删除旧 chunks添加新 chunks | 标记旧 chunks 为 superseded添加新 chunks |
| 文档废止 | 删除 chunks | 标记 chunks 为 deprecated |
| 历史追溯 | ❌ 无法查询旧版本 | ✅ 可以查询任意版本 |
| 操作可逆 | ❌ 删除后无法恢复 | ✅ 废止后可以恢复 |
| 存储成本 | 低 | 中(短期略高,长期相同) |
---
**文档版本**: v1.0
**创建时间**: 2026-04-20
**维护者**: RAG 服务开发组