From d4369e82198c7c2b3420bfbaafcc2daad1df41ec Mon Sep 17 00:00:00 2001 From: imac-maxwell Date: Tue, 9 Dec 2025 10:28:34 +0800 Subject: [PATCH] vault backup: 2025-12-09 10:28:34 --- app_prd/知识增强服务.md | 192 ++++++++++++++++++++++++++++++++++ app_prd/题目解析.md | 30 +++--- 2 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 app_prd/知识增强服务.md diff --git a/app_prd/知识增强服务.md b/app_prd/知识增强服务.md new file mode 100644 index 0000000..a4a5505 --- /dev/null +++ b/app_prd/知识增强服务.md @@ -0,0 +1,192 @@ + +# 产品需求文档:知识增强服务 (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. **构建 Payload**: 拼接 `ocr_text` (含公式/图片链接) 和 `answer_md`。 +3. **调用 API**: 发送 HTTP POST 请求给 `summary_api`。 +4. **结果处理**: + * **成功**: + * 将返回的 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` (字段补充) + +| 字段名 | 类型 | 说明 | +| :--- | :--- | :--- | +| ... | ... | (模块二已有的基础字段) | +| `problem_summary` | TEXT | 题目一句话摘要 (AI生成) | +| `knowledges` | JSONB | AI提取: `["k1", "k2"]` | +| `methods` | JSONB | AI提取: `["m1"]` | +| `updated_knowledges`| JSONB | **人工审核版** (应用层展示以此为准) | +| `updated_methods` | JSONB | **人工审核版** | +| `enrich_status` | VARCHAR | `pending` (待处理), `processing`, `done`, `failed` | +| `enrich_error` | TEXT | 调用外部API失败的错误日志 | +| `is_reviewed` | BOOLEAN | 是否经过人工确认 (默认 False) | +| `reviewer_id` | INT | 审核人ID | +| `reviewed_at` | TIMESTAMP | 审核时间 | + +--- + +## 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 密集型节点)。 \ No newline at end of file diff --git a/app_prd/题目解析.md b/app_prd/题目解析.md index 4978d6e..71c5171 100644 --- a/app_prd/题目解析.md +++ b/app_prd/题目解析.md @@ -75,21 +75,21 @@ ### 4.1 Table: `questions` (题库核心表) -| 字段名 | 类型 | 说明 | -| :--- | :--- | :--- | -| `id` | BIGINT (PK) | 题目唯一主键 | -| `document_id` | UUID (FK) | **关联 UDC-M 的 documents 表** | -| `batch_id` | UUID | 关联本次提取任务 ID | -| `question_seq` | INT | 在原文档中的顺序号 (1, 2, 3...) | -| `question_type` | VARCHAR | `choice` (选择), `fill` (填空), `essay` (解答) | -| `content_md` | TEXT | **题干内容** (含 Markdown 图片和 Latex) | -| `options_json` | JSONB | 选择题选项 `[{"label":"A","text":"..."}, ...]` | -| `answer_md` | TEXT | 参考答案 | -| `analysis_md` | TEXT | 解析 (如果有) | -| `image_urls` | JSONB | 提取出的图片链接列表 `["http...", "http..."]` (冗余字段,方便索引) | -| `content_hash` | VARCHAR(64) | 用于去重 | -| `enrich_status` | VARCHAR | `pending` (待打标), `done` (已打标) | -| `created_at` | TIMESTAMP | | +| 字段名 | 类型 | 说明 | +| :-------------- | :---------- | :---------------------------------------------- | +| `id` | BIGINT (PK) | 题目唯一主键 | +| `document_id` | UUID (FK) | **关联 UDC-M 的 documents 表** | +| `batch_id` | UUID | 关联本次提取任务 ID | +| `question_seq` | INT | 在原文档中的顺序号 (1, 2, 3...) | +| `question_type` | VARCHAR | `choice` (选择), `fill` (填空), `essay` (解答) | +| `content_md` | TEXT | **题干内容** (含 Markdown 图片和 Latex) | +| `options_json` | JSONB | 选择题选项 `[{"label":"A","text":"..."}, ...]` | +| `answer_md` | TEXT | 参考答案 | +| `analysis_md` | TEXT | 解析 (如果有) | +| `image_urls` | JSONB | 提取出的图片链接列表 `["http...", "http..."]` (冗余字段,方便索引) | +| `content_hash` | VARCHAR(64) | 用于去重 | +| `enrich_status` | VARCHAR | `pending` (待打标), `done` (已打标) | +| `created_at` | TIMESTAMP | | ### 4.2 Table: `extraction_tasks` (提取任务记录) 用于记录每次 LLM 调用的消耗和状态。