- 后端 API(Flask + Gunicorn) - RAG 引擎(混合检索 + 云端 Reranker + 引用溯源) - 文档解析(MinerU + 多格式支持) - Docker 生产部署配置 - 排除前端项目、敏感配置、模型文件
786 lines
24 KiB
Markdown
786 lines
24 KiB
Markdown
# 企业文档更新管理方案
|
||
|
||
> 本文档合并了企业文档管理的多种方案比较分析(包括增量更新、完整版本管理和软删除等),以及目前项目中所采纳的“方案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
|
||
# 旧版本 chunks(status=superseded,查询时被过滤)
|
||
{
|
||
"chunk_id": "c1",
|
||
"content": "报销流程:先填写申请单...",
|
||
"metadata": {
|
||
"document_id": "报销制度_v1.pdf",
|
||
"status": "superseded", # ← 关键字段
|
||
"version": "v1"
|
||
}
|
||
}
|
||
|
||
# 新版本 chunks(status=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
|
||
# 废止后的 chunks(status=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.pdf(active)
|
||
- ❌ 过滤:报销制度_v1.pdf(superseded)
|
||
- ❌ 过滤:临时防疫政策.pdf(deprecated)
|
||
|
||
### 历史查询(包含所有版本)
|
||
|
||
```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.pdf(active)
|
||
- ✅ 返回:报销制度_v1.pdf(superseded)
|
||
- ✅ 返回:临时防疫政策.pdf(deprecated)
|
||
|
||
### 特定版本查询
|
||
|
||
```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 服务开发组
|