# 产品需求文档:知识增强服务 (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. **结果处理**: * **成功**: - **第一步:更新 AI 原始字段**。将 API 返回的 JSON 数据填入 knowledges, methods, problem_summary 字段。这些字段始终反映 AI 的最新判断。 - **第二步:更新人工审核字段 (写保护逻辑)**。 - **检查该题目的 is_reviewed 字段状态**。 - **场景 A (未审核, is_reviewed = False)**:将 AI 提取的结果 **复制 (Copy)** 一份给 updated_knowledges 和 updated_methods。此时认为 AI 结果即为当前最佳结果。 - **场景 B (已审核, is_reviewed = True)**:**跳过** 对 updated_knowledges 和 updated_methods 的更新。**绝对禁止** 覆盖人工已经校对过的数据,仅更新 AI 原始字段供参考。 - **第三步:更新状态**。将 enrich_status 更新为 'done',清空 enrich_error。 * **失败 (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 密集型节点)。