# RAG 聊天接口

> 最后整理：2026-05-11
> 本文已按当前后端实现逐项核对
> 对应后端：`fastapi_modules/fastapi_leaudit/controllers/ragChatController.py`
> 统一前缀：`/api/v3/rag`

## 1. 目标与范围

本组接口用于替代旧的 `dify_chat/*` 对话代理，直接提供自有 RAG 聊天能力。

当前已落地能力：

- 获取当前用户可见的 RAG 应用
- 获取默认 RAG 应用
- 获取当前用户可见知识库
- 获取聊天页面参数
- 发起流式对话
- 查询会话列表
- 查询会话消息
- 重命名会话
- 删除会话
- 消息反馈

当前不在本文档范围内：

- 知识库 CRUD 管理
- 文档切分 / 入库任务
- Chroma 集合构建脚本

## 2. 鉴权与权限

### 2.1 鉴权方式

所有接口都要求请求头带：

```http
Authorization: Bearer <access_token>
```

JWT 解析逻辑见：

- `fastapi_common/fastapi_common_security/security.py`
- `fastapi_common/fastapi_common_security/jwtService.py`

JWT payload 至少会被后端消费这些字段：

- `user_id`
- `username`
- `area`
- `user_role`
- `type`，必须为 `access`

### 2.2 权限键

| 接口 | 权限 |
|------|------|
| `GET /api/v3/rag/apps` | `rag:app:read` |
| `GET /api/v3/rag/apps/default` | `rag:app:read` |
| `GET /api/v3/rag/datasets/my` | `rag:dataset:read` |
| `GET /api/v3/rag/chat/parameters` | `rag:chat:use` 或 `rag:app:read` 其一即可 |
| `POST /api/v3/rag/chat/messages` | `rag:chat:use` |
| `GET /api/v3/rag/chat/conversations` | `rag:conversation:read` |
| `GET /api/v3/rag/chat/conversations/{ConversationId}/messages` | `rag:conversation:read` |
| `PATCH /api/v3/rag/chat/conversations/{ConversationId}` | `rag:conversation:update` |
| `DELETE /api/v3/rag/chat/conversations/{ConversationId}` | `rag:conversation:delete` |
| `POST /api/v3/rag/chat/messages/{MessageId}/feedback` | `rag:message:feedback` |

说明：

- 权限检查使用 `HasAnyPermission`，即“列表中的任一权限命中即可通过”。
- 具体实现见 `fastapi_modules/fastapi_leaudit/services/impl/permissionServiceImpl.py`。

## 3. 通用返回格式

除流式接口外，统一返回：

```json
{
  "code": 200,
  "msg": "success",
  "data": {}
}
```

权限不足时通常返回：

```json
{
  "code": 403,
  "msg": "当前用户没有对应权限",
  "data": null
}
```

## 4. 接口列表

### 4.1 获取可见应用列表

`GET /api/v3/rag/apps`

用途：

- 聊天页面加载应用下拉
- 根据地区 / 省级角色做应用可见性过滤

请求参数：无

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "data": [
      {
        "appId": "1",
        "appName": "法务问答",
        "description": "默认烟草法务知识问答",
        "isDefault": true
      }
    ],
    "total": 1
  }
}
```

可见性规则：

- `provincial_admin` 可见全部启用应用
- 其他角色仅可见：
  - `rag_chat_app.area = 用户 area`
  - `rag_chat_app.area = '省级'`
  - `rag_chat_app.area = ''`
  - 或关联数据集 `rag_dataset.is_public = true`

### 4.2 获取默认应用

`GET /api/v3/rag/apps/default`

用途：

- 聊天页初始化默认应用

请求参数：无

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "appId": "1",
    "appName": "法务问答",
    "description": "默认烟草法务知识问答",
    "isDefault": true
  }
}
```

补充说明：

- 如果没有显式默认应用，会退回到当前用户可见的第一条应用。
- 如果完全没有可见应用，返回 `data: null`。

### 4.3 获取当前用户可见知识库

`GET /api/v3/rag/datasets/my`

用途：

- 聊天页展示“当前可用知识库”时使用

请求参数：无

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "data": [
      {
        "id": 1,
        "name": "广东烟草法规库",
        "description": "省级法规与制度",
        "area": "省级",
        "isPublic": true,
        "isDefault": true,
        "documentCount": 120,
        "totalChunks": 8345,
        "status": 1
      }
    ],
    "total": 1
  }
}
```

### 4.4 获取聊天参数

`GET /api/v3/rag/chat/parameters`

查询参数：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `appId` | int | 否 | 指定应用 ID；不传则取默认应用 |

用途：

- 初始化开场白
- 初始化建议问题
- 初始化上传能力配置

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "openingStatement": "你好，我可以帮你解答烟草行业法务问题。",
    "suggestedQuestions": [
      "烟草专卖许可延续的法定条件是什么？",
      "行政处罚文书审查重点有哪些？"
    ],
    "userInputForm": [],
    "fileUpload": {
      "image": {
        "enabled": false
      }
    }
  }
}
```

补充说明：

- 如果 `appId` 不存在、不可见，或者系统默认应用 / 排序第一条应用均不可见，当前实现不会报 404，而是返回一个空参数对象：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "openingStatement": "",
    "suggestedQuestions": [],
    "userInputForm": [],
    "fileUpload": {
      "image": {
        "enabled": false
      }
    }
  }
}
```

### 4.5 发起流式对话

`POST /api/v3/rag/chat/messages`

请求体：

```json
{
  "query": "烟草专卖许可证延续申请的审查要点是什么？",
  "conversationId": null,
  "appId": 1
}
```

字段说明：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `query` | string | 是 | 用户问题，不能为空 |
| `conversationId` | string \| null | 否 | 会话 ID；新对话可传 `null`、不传，当前实现也把 `"-1"` 视为新对话 |
| `appId` | int \| null | 否 | 应用 ID；不传则自动回退默认应用 |

返回类型：

- `text/event-stream`

SSE 事件 1：流式正文片段

```text
data: {"event":"message","task_id":"...","message_id":"...","conversation_id":"...","answer":"第一段内容","created_at":1746580000}

```

SSE 事件 2：流式结束

```text
data: {"event":"message_end","task_id":"...","message_id":"...","conversation_id":"...","metadata":{"usage":{"total_tokens":1234},"retriever_resources":[...],"suggested_questions":["问题1","问题2"]}}

```

SSE 事件 3：模型异常

```text
data: {"event":"error","task_id":"...","message_id":"...","code":"llm_error","message":"..."}

```

服务端行为说明：

- 若 `conversationId` 为空，会自动创建新会话
- 会先落一条 `role = user` 消息，再流式生成回答
- 流结束后会落一条 `role = assistant` 消息
- 若命中知识库，会把引用结果写入 `sources / metadata`
- 会根据对话内容追加 `suggested_questions`
- 当前应用解析顺序是：指定 `appId` -> 任意默认应用 -> 排序第一条应用；每一步都只检查当前命中的那一条记录是否可见，不会遍历全部可见应用

### 4.6 获取会话列表

`GET /api/v3/rag/chat/conversations`

查询参数：

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `appId` | int | 否 | - | 按应用过滤 |
| `page` | int | 否 | `1` | 页码，从 1 开始 |
| `pageSize` | int | 否 | `20` | 每页数量，最大 100 |

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "data": [
      {
        "id": "b17d3b0b-xxxx-xxxx",
        "name": "新对话",
        "introduction": "",
        "createdAt": 1746580000,
        "updatedAt": 1746580066
      }
    ],
    "hasMore": false,
    "limit": 20
  }
}
```

### 4.7 获取会话消息

`GET /api/v3/rag/chat/conversations/{ConversationId}/messages`

路径参数：

| 参数 | 类型 | 说明 |
|------|------|------|
| `ConversationId` | string | 会话 ID |

查询参数：

| 参数 | 类型 | 必填 | 默认值 |
|------|------|------|--------|
| `page` | int | 否 | `1` |
| `pageSize` | int | 否 | `20` |

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "data": [
      {
        "id": "assistant-message-id",
        "conversationId": "b17d3b0b-xxxx-xxxx",
        "query": "烟草专卖许可证延续申请的审查要点是什么？",
        "answer": "先核对主体资格，再核对经营条件……",
        "feedback": {
          "rating": "like"
        },
        "retrieverResources": [
          {
            "position": 1,
            "dataset_id": "1",
            "dataset_name": "广东烟草法规库",
            "document_id": "12",
            "document_name": "行政许可审查规范.pdf",
            "segment_id": "chunk-1",
            "score": 0.9132
          }
        ],
        "createdAt": 1746580000
      }
    ],
    "hasMore": false,
    "limit": 20
  }
}
```

说明：

- 返回结构是按“问答对”聚合后的结果，不是底层 `rag_message` 原始逐条结果。
- 分页查询实际先按底层 `rag_message` 行分页，再在服务层折叠成“用户问 + 助手答”结构，所以单页返回条数可能少于 `pageSize`。

### 4.8 重命名会话

`PATCH /api/v3/rag/chat/conversations/{ConversationId}`

请求体：

```json
{
  "name": "许可证延续审查要点"
}
```

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "result": "success",
    "name": "许可证延续审查要点"
  }
}
```

### 4.9 删除会话

`DELETE /api/v3/rag/chat/conversations/{ConversationId}`

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "result": "success"
  }
}
```

说明：

- 逻辑删除，实际是设置 `rag_conversation.deleted_at`

### 4.10 消息反馈

`POST /api/v3/rag/chat/messages/{MessageId}/feedback`

请求体：

```json
{
  "rating": "like"
}
```

约定值：

- `like`
- `dislike`
- `null`

补充说明：

- 当前 DTO 仅声明 `rating: str | null`，后端不会强校验枚举，除 `null` 外其余字符串也会被写入 `rag_message.feedback`。

成功响应示例：

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "result": "success"
  }
}
```

## 5. 数据表对应关系

| 表 | 用途 |
|----|------|
| `rag_dataset` | 知识库定义、地区可见性、检索参数 |
| `rag_document` | 知识库文档、启停状态、命中次数 |
| `rag_chat_app` | 聊天应用配置、默认应用、应用绑定知识库 |
| `rag_conversation` | 用户会话 |
| `rag_message` | 底层消息落库，包含引用与反馈 |

## 6. 当前实现约束

- 当前只提供 `GET /datasets/my`，不提供知识库管理 CRUD
- `fileUpload.image.enabled` 固定为 `false`
- 检索依赖 Chroma；Chroma 不可用时，接口仍可回答，但会退化成无知识库上下文
- 建议问题 `suggestedQuestions` 由二次模型调用生成，失败时会降级为空数组
- `GET /chat/parameters` / `POST /chat/messages` 的应用回退逻辑目前不是“从全部可见应用里挑第一条”，而是“默认应用优先，其次全表第一条”；当默认应用或首条应用不可见时，可能出现空参数或 `未配置可用聊天应用`

## 7. 联调建议

最小联调顺序：

1. `GET /api/v3/rag/apps`
2. `GET /api/v3/rag/apps/default`
3. `GET /api/v3/rag/chat/parameters`
4. `POST /api/v3/rag/chat/messages`
5. `GET /api/v3/rag/chat/conversations`
6. `GET /api/v3/rag/chat/conversations/{ConversationId}/messages`

优先检查：

- 401：JWT 是否有效
- 403：`rag:*` 权限是否已写入并分配
- 500：LLM、数据库、Chroma、知识库配置是否可用
