Files
rag/docs/风险边界问题修复注意事项.md
lacerate551 cb75b9b274 fix(boundary): 修复多库边界问题、版本管理及删除清理
多库检索与存储修复:
- RRF 融合去重改用 (collection, chunk_id) 复合键,修复同名文件结果被吞
- DocStore 存储路径加 collection 前缀,修复跨库同名切片数据覆盖
- search_multiple 去重改用复合键
- chunk_id 解析改用 rsplit 兼容下划线文件名

上传与版本管理修复:
- 同名文件上传改为覆盖模式,自动清理旧切片
- 修复首次上传不创建版本记录
- 修复覆盖上传版本号回退到 v1
- sync ADDED 分支改用动态版本号生成
- _generate_version_id 改为基于全部版本递增
- 废止/恢复操作同步 SQLite 版本记录
- mark_document_as_superseded 改为仅更新 SQLite

删除清理修复:
- 删除文档时同步清理 SQLite 版本记录和变更日志
- 删除向量库时同步清理该库所有版本记录
- cleanup 改为清理 SQLite 记录而非 ChromaDB

测试:
- test_version_management.py: 27 条版本管理单元测试
- test_edge_cases.py: 28 条边界用例测试
- test_upload_dedup.py: 5 条上传去重测试
- e2e_risk_test.py: 27 条端到端风险测试

文档:
- 新增风险边界问题修复注意事项.md(面向后端的对接文档)
- 新增向量库边界风险分析.md
- 更新多篇现有文档
2026-06-04 23:58:44 +08:00

120 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 风险边界问题修复注意事项
### 一、修复了哪些会出问题的情况
以下场景之前会报错或数据异常,现在已修复,不会再出问题:
| 场景 | 之前的问题 | 修复后 |
|------|-----------|--------|
| 同名文件覆盖上传 | 旧版本历史丢失,版本记录缺失 | 旧版本自动标记为 `superseded`,新版本正确记录,历史完整保留 |
| 首次上传文档 | 不创建版本记录,后续覆盖时无法追溯旧版本 | 首次上传也会创建 v1 版本记录 |
| 多次覆盖上传同一文件 | 版本号始终停留在 v1新记录覆盖旧记录 | 版本号正确递增v1 → v2 → v3... |
| 废止文档后查询版本历史 | SQLite 版本状态没同步,仍显示 active | 废止和恢复操作同步更新 ChromaDB 和 SQLite |
| 废止后再废止 | 可能报错 | 不报错,正常返回成功 |
| 恢复未废止的文档 | 可能报错 | 不报错,返回错误提示 |
| 废止不存在的文档 | 可能崩溃 | 不崩溃,返回错误提示 |
| 上传空文件 | 可能崩溃 | 不崩溃,正常处理 |
| 废止的文档出现在搜索结果中 | deprecated/superseded 切片未被过滤 | 搜索引擎自动过滤,不再返回 |
| 删除文档后版本历史残留 | SQLite 版本记录不清理,查询返回幽灵数据 | 删除文档时同步清理版本记录和变更日志 |
| 删除向量库后版本历史残留 | 整个库的版本记录不清理,重建同名库后版本号错乱 | 删除向量库时同步清理所有版本记录和变更日志 |
### 二、端口和接口
端口不变,仍然是 `5001`。所有接口路径不变,无新增接口。
版本历史查询接口(已有,无变化):
```
GET /collections/{kb_name}/documents/{filename}/versions
```
### 三、返回数据字段变化
#### 上传接口 `POST /documents/upload`
响应 `data.file` 中新增 `replaced` 字段:
```json
{
"success": true,
"data": {
"file": {
"filename": "制度.pdf",
"collection": "public_kb",
"path": "public_kb/制度.pdf",
"size": 1024,
"replaced": true
},
"sync_status": "已保存并添加到向量库"
}
}
```
`replaced``true` 表示本次上传覆盖了同名旧文件。首次上传时为 `false`。后端可据此判断是否需要更新自己的版本记录。
#### 版本历史接口返回格式
```json
{
"success": true,
"document_id": "制度.pdf",
"collection": "public_kb",
"versions": [
{
"version": "v2",
"status": "active",
"created_at": "2026-06-04T15:30:00",
"change_summary": "新增文档",
"chunk_count": 12
},
{
"version": "v1",
"status": "superseded",
"deprecated_date": "2026-06-04T15:30:00",
"deprecated_reason": "重新上传覆盖"
}
]
}
```
`status` 可能的值:`active`(使用中)、`deprecated`(手动废止)、`superseded`(被新版本替代)。
#### 废止/恢复接口返回格式(无变化)
废止:
```json
{
"success": true,
"deprecated_chunks": 5,
"document_id": "制度.pdf",
"collection": "public_kb",
"deprecated_date": "2026-06-04T15:00:00"
}
```
恢复:
```json
{
"success": true,
"restored_chunks": 5,
"document_id": "制度.pdf",
"collection": "public_kb"
}
```
### 四、后端需要注意的
1. **chunk_id 必须配合 collection 使用** — chunk_id 格式是 `{文件名}_{序号}`,不同向量库中同名文件的 chunk_id 相同,存数据库时必须用 `(collection, chunk_id)` 做联合键。
2. **citation 的 content 截断至 300 字** — 需要完整内容请调切片查询接口。
3. **同名文件上传是覆盖模式** — 不再加时间戳重命名,直接覆盖。
### 五、已知限制(后端需规避)
以下场景当前未做特殊处理,后端在业务层需要注意规避:
1. **不要并发上传同名文件** — 两个请求同时上传同名文件到同一知识库可能产生竞态,导致版本记录或切片数据不一致。如有并发场景,请在业务层加锁或排队。
2. **superseded 文档不可恢复** — 被覆盖上传替代的旧版本(状态 `superseded`),其切片已从向量库中物理删除,无法通过恢复接口还原。恢复接口仅支持恢复手动废止(`deprecated`)的文档。对 superseded 文档调恢复接口会返回 `"未找到已废止的文档"`
3. **批量上传中不要包含同名文件** — 如果一次批量上传请求中包含两个同名文件,第一个会被第二个无意义地覆盖。请确保单次批量上传中文件名不重复。