187 lines
7.5 KiB
Markdown
187 lines
7.5 KiB
Markdown
|
||
# 产品需求文档:知识增强服务 (KES)
|
||
**Knowledge Enrichment Service - Backend**
|
||
|
||
| 文档版本 | V1.0 |
|
||
| :--- | :--- |
|
||
| **依赖模块** | UDC-M (模块一), QPES (模块二) |
|
||
| **最后更新** | 2025-12-09 |
|
||
| **状态** | 待开发 |
|
||
| **涉及端** | 后端 API, 异步 Worker, 外部 API 交互 |
|
||
|
||
---
|
||
|
||
## 1. 项目背景与目标
|
||
### 1.1 背景
|
||
模块二(QPES)已经完成了“文档 -> 题目”的物理拆解,数据库中已经有了干净的题干、答案和图片。
|
||
但此时的题目是“孤立”的,无法支持“查找所有关于‘勾股定理’的题”或“生成一份‘三角函数’的试卷”等业务场景。我们需要为题目注入**元数据(Metadata)**。
|
||
|
||
### 1.2 目标
|
||
构建一个**异步的知识增强与人工审核服务**。
|
||
* **自动打标**:监控未处理的题目,调用外部 `summary_api` 自动提取知识点和方法。
|
||
* **人工介入 (HITL)**:提供接口,允许教研人员对 AI 提取的标签进行审核、修改和确认。
|
||
* **数据回填**:将处理结果结构化地更新回主数据库。
|
||
|
||
---
|
||
|
||
## 2. 系统架构设计
|
||
|
||
系统设计为 **“生产者-消费者”** 模式,以解耦数据库读取与外部 API 调用(防止外部 API 阻塞系统)。
|
||
|
||
### 2.1 核心组件
|
||
1. **Enrichment Scheduler (调度器)**:
|
||
* 定时任务 (Celery Beat) 或 事件触发。
|
||
* 扫描 `questions` 表中 `enrich_status = 'pending'` 的记录。
|
||
* 将任务推送到 Redis 队列。
|
||
2. **API Client Worker (执行器)**:
|
||
* 消费队列。
|
||
* 调用外部 `summary_api`。
|
||
* **关键特性**: 实现流控 (Rate Limiting)、重试 (Retry) 和熔断机制。
|
||
3. **Review API (审核接口)**:
|
||
* 提供给前端/教研后台,用于人工修改知识点。
|
||
|
||
### 2.2 外部接口定义 (Mock Summary API)
|
||
假设外部接口契约如下(开发时需对接真实接口):
|
||
* **Input**: `{"question_text": "...", "answer": "..."}`
|
||
* **Output**:
|
||
```json
|
||
{
|
||
"summary": "一道关于利用勾股定理求解直角三角形斜边的应用题",
|
||
"knowledges": ["勾股定理", "直角三角形性质"],
|
||
"methods": ["数形结合", "方程思想"]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 功能需求详细说明
|
||
|
||
### 3.1 自动打标流程
|
||
1. **任务获取**: 查询 `questions` 表,条件:`enrich_status='pending'`。
|
||
2. Write-Protect (写保护)。IF question.is_reviewed == True: UPDATE ONLY raw_fields; ELSE: UPDATE raw_fields AND updated_fields;
|
||
3. **构建 Payload**: 拼接 `ocr_text` (含公式/图片链接) 和 `answer_md`。
|
||
4. **调用 API**: 发送 HTTP POST 请求给 `summary_api`。
|
||
5. **结果处理**:
|
||
* **成功**:
|
||
* 将返回的 JSON 填入 `knowledges`, `methods`, `problem_summary` 字段。
|
||
* 同时将这些值 **Copy** 一份给 `updated_knowledges`, `updated_methods` (作为人工审核的默认值)。
|
||
* 更新状态 `enrich_status = 'done'`。
|
||
* **失败 (5xx/Timeout)**:
|
||
* 触发重试(指数退避,最多 3 次)。
|
||
* 若最终失败,更新状态 `enrich_status = 'failed'`,并记录 `error_msg`。
|
||
|
||
### 3.2 人工审核 (Human-in-the-Loop)
|
||
* **审核逻辑**: AI 的提取结果并不总是 100% 准确。系统信任链为:**人工数据 > AI 数据**。
|
||
* **数据流**:
|
||
1. 前端展示 `updated_knowledges` (初始值等于 AI 提取值)。
|
||
2. 人工修改(增删改)。
|
||
3. 调用 `PUT /questions/{id}/review` 接口保存。
|
||
4. 系统更新 `updated_at` 和 `reviewer_id`。
|
||
|
||
### 3.3 知识库一致性 (Optional Advanced)
|
||
* *需求*: 虽然初期可以存自由文本的 Tag,但为了检索准确,建议引入“标准知识点树”。
|
||
* *逻辑*: 在保存 Tag 前,检查 Tag 是否在系统的 `knowledge_tree` 表中。如果不在,标记为“新词”,提示管理员审核。
|
||
|
||
---
|
||
|
||
## 4. 数据库设计 (PostgreSQL)
|
||
|
||
沿用模块二的 `questions` 表,本模块主要负责 **UPDATE** 操作。
|
||
### **4.1 Table: questions (字段更新)**
|
||
|
||
本模块负责该表的 **更新 (UPDATE)** 操作。
|
||
|
||
- **读取条件**: WHERE enrich_status = 'pending'
|
||
|
||
- **更新字段 (AI)**: knowledges, methods, problem_summary, enrich_status。
|
||
|
||
- **更新字段 (人工)**: updated_knowledges, updated_methods, is_reviewed。
|
||
|
||
- (完整表结构定义请参考“统一数据库设计规范”文档)
|
||
---
|
||
|
||
## 5. API 接口定义 (RESTful)
|
||
|
||
**Base URL**: `/api/v1/enrichments`
|
||
|
||
### 5.1 触发/重试打标
|
||
* **POST** `/trigger`
|
||
* **Description**: 手动触发一次批量打标(通常由定时任务调用,也可以人工触发重试失败任务)。
|
||
* **Body**: `{"retry_failed": true}`
|
||
* **Response**: `{"triggered_count": 50}`
|
||
|
||
### 5.2 提交人工审核结果
|
||
* **PUT** `/questions/{question_id}/review`
|
||
* **Description**: 教研人员提交修正后的知识点。
|
||
* **Body**:
|
||
```json
|
||
{
|
||
"knowledges": ["修正后的知识点1", "修正后的知识点2"],
|
||
"methods": ["方法1"],
|
||
"confirm": true // 标记 is_reviewed = true
|
||
}
|
||
```
|
||
* **Response**: `200 OK`
|
||
|
||
### 5.3 获取待审核题目列表
|
||
* **GET** `/questions/review-queue`
|
||
* **Query**:
|
||
* `status`: `done` (已AI打标,等待人工)
|
||
* `is_reviewed`: `false`
|
||
* **Response**: 题目列表 (含 AI 提取的 Tag)。
|
||
|
||
---
|
||
|
||
## 6. 技术选型 (Vibe Coding Stack)
|
||
|
||
### 6.1 核心技术
|
||
* **HTTP Client**: **`httpx`** (异步) 或 **`aiohttp`**。
|
||
* *理由*: 相比 `requests`,异步客户端在 Celery Worker 中能支持更高的并发(Event Loop),适合 I/O 密集型任务。
|
||
* **Resilience (弹性)**: **`tenacity`**。
|
||
* *理由*: Python 最好的重试库。用装饰器 `@retry` 就能实现指数退避,代码极简。
|
||
* **Rate Limiting**: **`Redis` + `Lua Script`** 或 Celery 自带的 rate limit。
|
||
* *理由*: 防止把外部 `summary_api` 打挂。
|
||
|
||
### 6.2 代码结构示例 (Service Layer)
|
||
|
||
```python
|
||
# services/enrichment_service.py
|
||
import httpx
|
||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||
|
||
class SummaryAPIClient:
|
||
def __init__(self, base_url: str, api_key: str):
|
||
self.client = httpx.AsyncClient(base_url=base_url, headers={"Authorization": api_key})
|
||
|
||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
|
||
async def get_summary(self, text: str):
|
||
response = await self.client.post("/summarize", json={"text": text})
|
||
response.raise_for_status()
|
||
return response.json()
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 模块间联动总结
|
||
|
||
到此为止,三个模块已经形成了一个完美的闭环:
|
||
|
||
1. **UDC-M (文档与转换)**:
|
||
* **Input**: 用户上传 PDF/Docx。
|
||
* **Output**: 永久存储原始文件 + 标准 Markdown (含 MinIO 图片链)。
|
||
* **Data**: `documents` 表。
|
||
|
||
2. **QPES (题目解析)**:
|
||
* **Input**: 读取 `documents` 里的 Markdown。
|
||
* **Action**: 调用 DeepSeek 切分题目。
|
||
* **Output**: 结构化题目。
|
||
* **Data**: `questions` 表 (基础字段)。
|
||
|
||
3. **KES (知识增强)**:
|
||
* **Input**: 读取 `questions` (Pending 状态)。
|
||
* **Action**: 调用 Summary API + 人工审核。
|
||
* **Output**: 完整的、带标签的题库。
|
||
* **Data**: `questions` 表 (Tag 字段)。
|
||
|
||
**部署建议**:
|
||
这三个模块可以部署在**同一个代码仓库 (Monorepo)** 中,共享数据库 Model 定义,但通过**Docker Compose** 拆分为不同的服务容器运行,以便根据负载独立扩容(例如:UDC 扩容 GPU 节点,KES 扩容 IO 密集型节点)。 |