# RAG 知识库文档管理接口与行为变更说明

> 最后整理：2026-05-11
> 对应后端提交：`8206ed7`
> 对应控制器：`fastapi_modules/fastapi_leaudit/controllers/ragChatController.py`
> 对应服务：`fastapi_modules/fastapi_leaudit/services/impl/ragDatasetServiceImpl.py`
> 统一前缀：`/api/v3/rag`

## 1. 目标

本文档说明本次 RAG 知识库“文档管理”相关后端改动，重点覆盖：

- 知识库文档批量删除
- 删除结果明细返回
- 处理中 / 索引中文档删除限制
- 文档上传改为异步处理
- 文档重处理改为异步处理
- 前端轮询所依赖的状态流转
- 向量化调用的分批与错误处理

不在本文范围内：

- 聊天接口
- 应用管理接口
- 知识库基础 CRUD
- 子分段 / 分段编辑细节

## 2. 相关权限

本次涉及的接口主要使用以下权限：

| 接口 | 权限 |
|------|------|
| `GET /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}/indexing-status` | `rag:dataset:read` |
| `POST /api/v3/rag/datasets/{DatasetId}/documents` | `rag:dataset:update` |
| `POST /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}/update-by-file` | `rag:dataset:update` |
| `DELETE /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}` | `rag:dataset:delete` |
| `POST /api/v3/rag/datasets/{DatasetId}/documents/batch-delete` | `rag:dataset:delete` |

补充：

- 后端控制器层会先检查权限键。
- 服务层还会继续校验角色范围。
- 当前允许执行文档上传 / 删除的角色范围仍为：
  - `provincial_admin`
  - `super_admin`
  - `admin`

## 3. 本次行为变化总览

### 3.1 批量删除

新增接口：

- `POST /api/v3/rag/datasets/{DatasetId}/documents/batch-delete`

行为：

- 支持单请求删除多个文档
- 返回删除成功数量、跳过数量、已删除 ID、跳过原因明细

### 3.2 删除限制

删除接口不再允许删除“处理中”的文档。

当前视为处理中、不可删除的状态：

- `waiting`
- `parsing`
- `cleaning`
- `splitting`
- `indexing`

当前允许删除的状态：

- `completed`
- `error`
- `paused`

### 3.3 上传与重处理改为异步

此前：

- 上传接口会等完整解析、切分、向量化完成后才返回

现在：

- 上传接口创建文档记录后立即返回
- 后台使用异步任务继续完成后续处理
- 前端通过 `indexing-status` 接口轮询状态

### 3.4 向量化调用分批

此前：

- 一次性将全部 chunk 发给 embedding 接口

现在：

- 按 `RAG_CONFIG["EMBED_BATCH_SIZE"]` 分批提交
- 默认值由配置控制，未配置时使用 `10`

目的：

- 降低 DashScope / embedding 服务因单次输入过大返回 `400 Bad Request` 的概率

## 4. 接口说明

### 4.1 上传知识库文档

`POST /api/v3/rag/datasets/{DatasetId}/documents`

请求：

- `multipart/form-data`
- `file`: 文件内容
- `data`: 可选，JSON 字符串，表示处理配置

当前支持格式：

- `.pdf`
- `.docx`
- `.txt`
- `.md`
- `.json`

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "document": {
      "id": "12",
      "name": "示例文档.pdf",
      "indexing_status": "indexing",
      "word_count": 0,
      "hit_count": 0,
      "enabled": true
    },
    "batch": "12"
  }
}
```

说明：

- 返回时不代表向量化已经完成。
- `batch` 当前等同于 `documentId`，前端可直接拿去轮询状态。

### 4.2 重处理知识库文档

`POST /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}/update-by-file`

行为与上传接口一致：

- 先把文档状态置为 `indexing`
- 清理旧向量
- 立即返回
- 后台异步重建 chunk 与向量

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "document": {
      "id": "12",
      "name": "示例文档.pdf",
      "indexing_status": "indexing",
      "word_count": 0,
      "hit_count": 0,
      "enabled": true
    },
    "batch": "12"
  }
}
```

### 4.3 查询文档处理状态

`GET /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}/indexing-status`

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": [
    {
      "id": "12",
      "indexing_status": "splitting",
      "processing_started_at": 1778490000,
      "parsing_completed_at": 1778490003,
      "cleaning_completed_at": 1778490005,
      "splitting_completed_at": null,
      "completed_at": null,
      "paused_at": null,
      "error": null,
      "stopped_at": null,
      "completed_segments": 0,
      "total_segments": 24
    }
  ]
}
```

状态流转顺序：

- `indexing`（创建记录后的初始状态）
- `parsing`
- `cleaning`
- `splitting`
- `indexing`（写入向量阶段）
- `completed`

失败时：

- 状态会更新为 `error`
- `error` 字段会记录截断后的错误信息

### 4.4 单文档删除

`DELETE /api/v3/rag/datasets/{DatasetId}/documents/{DocumentId}`

行为：

- 实际内部复用批量删除逻辑
- 如果目标文档处于处理中状态，会直接返回 400

错误示例：

```json
{
  "code": 400,
  "msg": "文档仍在处理中，暂不允许删除",
  "data": null
}
```

### 4.5 批量删除文档

`POST /api/v3/rag/datasets/{DatasetId}/documents/batch-delete`

请求体：

```json
{
  "document_ids": [12, 13, 14]
}
```

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "result": "success",
    "requestedCount": 3,
    "deletedCount": 2,
    "skippedCount": 1,
    "deletedIds": [12, 13],
    "skipped": [
      {
        "id": 14,
        "name": "处理中示例.pdf",
        "reason": "文档仍在处理中，暂不允许删除"
      }
    ]
  }
}
```

说明：

- 后端会先查询所有目标文档
- 对可删除文档：
  - 删除 Chroma 中对应向量
  - 删除 OSS 对象
  - 逻辑删除 `rag_document`
- 完成后统一回写：
  - `rag_dataset.document_count`
  - `rag_dataset.total_chunks`

## 5. 后端内部状态处理说明

### 5.1 异步处理任务

上传 / 重处理现在通过内部异步任务 `_run_document_indexing_task(...)` 执行后续工作。

任务步骤：

1. 更新状态为 `parsing`
2. 提取文本
3. 更新状态为 `cleaning`
4. 构建 chunk
5. 更新状态为 `splitting`
6. 调 embedding 接口生成向量
7. 更新状态为 `indexing`
8. 写入 Chroma
9. 更新状态为 `completed`
10. 同步 `rag_dataset` 统计字段

异常时：

- 状态写为 `error`
- `indexing_error` 写入错误摘要

### 5.2 向量化分批

方法：

- `_embed_texts(texts, model_name)`

变化：

- 以前单次请求提交全部文本
- 现在按批次循环提交

错误处理：

- 若 embedding 服务返回 HTTP 错误
- 服务层会转成：

```text
向量化服务调用失败: <上游错误文本>
```

以便前端直接显示可读错误

## 6. 前后端联调注意事项

### 6.1 上传成功不等于处理完成

前端不能再以“上传接口返回 200”判断文档已经可检索。

正确做法：

1. 调上传接口
2. 读取响应中的 `batch`
3. 轮询 `indexing-status`
4. 直到状态变为：
   - `completed`
   - 或 `error`

### 6.2 批量删除要接删除明细

前端不应只提示“删除成功”。

建议按以下优先级展示：

- 全部删除成功：提示删除数量
- 部分删除成功：提示成功数 + 跳过数
- 全部跳过：提示未删除，并展示跳过原因

### 6.3 处理中状态要禁用删除入口

虽然服务端已经拦截，但前端仍应同步禁用：

- 单删按钮
- 批量多选框

避免用户误操作后频繁打到 400

## 7. 对应提交

本次后端变更对应提交：

- `8206ed7` `feat: improve rag dataset document management`

如果后续继续扩展：

- 文档状态更多细粒度字段
- chunk 统计维度
- 删除结果补充更多结构化信息

建议继续更新本文，而不是另起一份零散说明。
