Architecture transformation: - Replace Fan-out (Manager->Child->Last Child Wins) with Scatter+Aggregator pattern - API layer directly dispatches N independent jobs (no Manager) - Worker only writes its own Result row, never touches Task table (zero row-lock) - Aggregator polls groupBy for completion + zombie cleanup (replaces Sweeper) - Reduce red lines from 13 to 9, eliminate distributed complexity Documents updated (10 files): - 08-Tool3 main architecture doc: v2.0 rewrite (schema, Task 2.3/2.4, red lines, risks) - 08d-Code patterns: rewrite sections 4.1-4.6 (API dispatch, SingleWorker, Aggregator) - 08a-M1 sprint: rewrite M1-3 core (Worker+Aggregator), red lines, acceptance criteria - 08b-M2 sprint: simplify SSE (NOTIFY/LISTEN downgraded to P2 optional) - 08c-M3 sprint: milestone table wording update - New: Scatter+Polling Aggregator pattern guide v1.1 (Level 2 cookbook) - New: V2.0 architecture deep review and gap-fix report - Updated: ASL module status, system status, capability layer index Co-authored-by: Cursor <cursoragent@cursor.com>
187 lines
8.5 KiB
Markdown
187 lines
8.5 KiB
Markdown
# M2:血肉丰满 — The HITL Workbench
|
||
|
||
> **所属:** 工具 3 全文智能提取工作台 V2.0
|
||
> **架构总纲:** `08-工具3-全文智能提取工作台V2.0开发计划.md`
|
||
> **代码手册:** `08d-工具3-代码模式与技术规范.md`(所有代码模式均在此手册中,开发时按需查阅)
|
||
> **前置依赖:** M1 全部完成(散装管线 + Aggregator 已验证、PKB ACL 已通、纯文本提取可跑通)
|
||
> **建议时间:** Week 2-3(8-9 天)
|
||
> **核心目标:** 接入 MinerU 视觉大模型提升表格准确率,完成前端最复杂的 HITL 审核抽屉,交付一个"完全可用"的 V1 产品。
|
||
|
||
---
|
||
|
||
## Demo 形态
|
||
|
||
完整的 V1 体验:前端有打字机风格的终端日志流、右侧滑出包含 Quote 高亮比对的审核抽屉、能导出标准科研 Excel 宽表。虽然不能自定义字段,但用标准 RCT 模板提取文献已经足够惊艳。
|
||
|
||
---
|
||
|
||
## 任务清单
|
||
|
||
### M2-1:接入 MinerU 表格引擎 + Clean Data 缓存(2 天)
|
||
|
||
**做什么:**
|
||
- `PdfProcessingPipeline.ts` 升级:M1 的纯文本降级 → 完整双引擎流水线
|
||
- 从 PKB `snapshotStorageKey` 下载 PDF Buffer → 调用 MinerU Cloud API → 返回结构化 HTML 表格
|
||
- **MinerU Clean Data OSS 缓存**(Cache-Aside):调用前先检查 `pkb/{kbId}/{docId}_mineru_clean.html`,命中则 <1 秒返回
|
||
- Worker 内部串行 `await mineruClient.extractTables()`(无需独立子队列,`teamConcurrency: 10` 即为 MinerU 隐式并发上限)
|
||
|
||
**不做什么:**
|
||
- 不改散装架构(M1 已稳定)
|
||
- 不做动态 Prompt(M3),继续用写死的 RCT Schema
|
||
|
||
**验收标准:**
|
||
- [ ] MinerU 返回 HTML 表格,含 `<table>` + `colspan/rowspan`
|
||
- [ ] OSS 缓存命中时跳过 MinerU 调用(日志可见 "Cache hit")
|
||
- [ ] `teamConcurrency: 10` 全局并发控制生效(MinerU 调用受此限制)
|
||
- [ ] MinerU 超时(>3min)自动降级到纯文本
|
||
|
||
> 📖 缓存代码模式见架构总纲 Task 2.2
|
||
> 📖 研发红线 2(计算卸载):Node.js 禁碰 MinerU 解析,仅 HTTP 调用 Cloud API
|
||
|
||
---
|
||
|
||
### M2-2:XML 隔离 Prompt + fuzzyQuoteMatch 算法(1.5 天)
|
||
|
||
**做什么:**
|
||
- `DynamicPromptBuilder.ts`(M2 阶段仅支持基座模板,不做动态 Schema):
|
||
- User Prompt 中用 `<FULL_TEXT>` 和 `<HIGH_FIDELITY_TABLES>` XML 标签隔离双引擎输出
|
||
- System Prompt 中声明表格优先级规则
|
||
- `ExtractionValidator.ts`:实现 `fuzzyQuoteMatch` 算法
|
||
- `buildQuoteSearchScope()`:MinerU HTML 用 `html-to-text` 剥离标签 + 拼接 pymupdf4llm Markdown
|
||
- Unicode NFKC 标准化 → 剥离非字母数字 → 精确包含检查 → Levenshtein ≤5% 容错
|
||
- 返回三级置信度:≥0.95(绿色)/ 0.80-0.95(黄色)/ <0.80(红色)
|
||
|
||
**验收标准:**
|
||
- [ ] LLM 收到的 Prompt 中 `<FULL_TEXT>` 和 `<HIGH_FIDELITY_TABLES>` 标签正确隔离
|
||
- [ ] `fuzzyQuoteMatch` 搜索范围 = pymupdf4llm 全文 + MinerU 纯文本(非仅 Markdown)
|
||
- [ ] 对 8 篇测试 PDF 的 Quote 验证误报率 < 5%
|
||
- [ ] LLM 引用 MinerU 表格中的数字(如 "410 (22.4%)")能被正确匹配
|
||
|
||
> 📖 XML 隔离设计见架构总纲 Task 2.1
|
||
> 📖 fuzzyQuoteMatch 代码见架构总纲 Task 2.3 补丁 1
|
||
> 📖 红线 8:Quote 搜索池必须含 MinerU 文本
|
||
|
||
---
|
||
|
||
### M2-3:SSE 终端日志流(1 天)
|
||
|
||
**做什么:**
|
||
- `ExtractionController.ts` 新增 SSE 端点 `GET /tasks/:taskId/stream`
|
||
- SSE 事件类型:`sync`(首帧)、`log`、`error`
|
||
- **首帧 sync 降级方案**:`recentLogs: []`(不依赖内存 logBuffer),前端检测到空日志时打印 "--- 监控已重新连接 ---"
|
||
- `ProcessingTerminal.tsx` 组件:深色终端风格,来源颜色区分(MinerU 蓝 / DeepSeek 紫 / System 绿)
|
||
- `useExtractionLogs.ts` Hook:仅驱动日志区,不影响主业务流
|
||
- SSE 使用**本 Pod 内存事件**即可(Worker 和 API 在同一 Pod 时日志实时可达)
|
||
|
||
**M1 已完成的不动:**
|
||
- `useTaskStatus.ts`(React Query 轮询 + groupBy 进度)继续驱动进度条和步骤跳转
|
||
- `complete` 检测完全由 React Query 轮询到 `status === 'completed'` 触发,不依赖 SSE
|
||
|
||
**[P2 可选] SSE 跨 Pod 广播 — NOTIFY/LISTEN:**
|
||
> 散装架构下进度条由 React Query 轮询驱动,SSE 仅为日志增强。
|
||
> 多 Pod 部署后若日志体验不佳,可后续实施 `SseNotifyBridge.ts`(代码见 08d §7.6)。
|
||
> M2 阶段不强制实施。
|
||
|
||
**验收标准:**
|
||
- [ ] SSE 连接后立即收到 `sync` 首帧
|
||
- [ ] 日志实时打字机效果(`[MinerU]`、`[DeepSeek]`、`[System]` 分色)
|
||
- [ ] SSE 断开后进度条不受影响(React Query 继续轮询)
|
||
- [ ] 多 Pod 环境下 SSE 重连到其他 Pod → 显示 "监控已重新连接" 提示(本 Pod 无历史日志时)
|
||
|
||
> 📖 双轨制架构见架构总纲 Task 4.1
|
||
> 📖 SSE 降级方案见架构总纲 Task 2.4
|
||
> 📖 [P2 可选] NOTIFY/LISTEN 代码模式见 08d §7.6
|
||
|
||
---
|
||
|
||
### M2-4:智能审核抽屉(3 天)⚠️ M2 核心战役
|
||
|
||
**做什么:**
|
||
|
||
**Step A — ExtractionDrawer 主体(1.5 天):**
|
||
- 700px 右侧抽屉,4 大模块:基础元数据 / 基线特征 / RoB 2.0 / 结局指标
|
||
- `Collapse` 折叠面板懒渲染(默认仅展开"基础元数据")
|
||
- 每个字段下方展示 `QuoteBlock`:灰色背景 + 关键数字黄色 `<mark>` 高亮
|
||
- 字段可编辑,修改追踪到 `manualOverrides`
|
||
- 底部:[取消] + [核准保存] → `PUT /results/:resultId/review`
|
||
|
||
**Step B — HITL 死锁解套(0.5 天):**
|
||
- Quote 红色警告旁新增 `[强制认可]` + `[手动修改数值]` 双按钮
|
||
- 所有红色警告必须被处置后 "核准保存" 才可点击
|
||
- `manualOverrides` 记录 `{ fieldName_quote_force_accepted: true }` 用于审计
|
||
|
||
**Step C — 性能优化(0.5 天):**
|
||
- 每个 FieldGroup 用 `React.memo` 包裹
|
||
- 使用 Ant Design `Form.shouldUpdate` 精确控制字段级重渲染
|
||
- `manualOverrides` 通过 `Form.onValuesChange` 差量追踪
|
||
|
||
**Step D — 签名 URL 懒加载(0.5 天):**
|
||
- "查看源 PDF" 按钮点击时才生成签名 URL(10 分钟有效期)
|
||
- 前端 `usePdfViewer` Hook 监听 403 → 自动重签
|
||
|
||
**验收标准:**
|
||
- [ ] 抽屉打开 < 200ms(Collapse 懒渲染生效)
|
||
- [ ] Quote 三级置信度正确展示(绿/黄/红)
|
||
- [ ] 红色 Quote 的 [强制认可] 和 [手动修改数值] 按钮可用
|
||
- [ ] 未处置红色警告时 "核准保存" 按钮禁用
|
||
- [ ] 核准后该篇状态变为 Approved
|
||
- [ ] "查看源 PDF" → 10 分钟内可正常查看 → 过期后 403 自动重签
|
||
- [ ] 修改字段值后 `manualOverrides` 正确记录
|
||
|
||
> 📖 抽屉布局见架构总纲 Task 5.2
|
||
> 📖 HITL 解锁见架构总纲 Task 5.2 v1.4 修正
|
||
> 📖 签名 URL 见架构总纲 Task 5.3
|
||
|
||
---
|
||
|
||
### M2-5:Excel 宽表导出(0.5 天)
|
||
|
||
**做什么:**
|
||
- `ExtractionExcelExporter.ts`:标准科研 Excel 数据宽表
|
||
- 每个变量列右侧紧跟 `_quote` 原文列
|
||
- 仅导出 `reviewStatus = approved` 的文献
|
||
- 表头双行:第一行中文名,第二行英文 JSON Key
|
||
- `GET /tasks/:taskId/export` 端点
|
||
|
||
**验收标准:**
|
||
- [ ] 导出的 Excel 列顺序正确(变量 + Quote 交替)
|
||
- [ ] 仅含 Approved 文献
|
||
- [ ] 双行表头
|
||
|
||
> 📖 宽表格式见架构总纲 Task 2.5
|
||
|
||
---
|
||
|
||
### M2-6:联调 + 集成测试(1 天)
|
||
|
||
**做什么:**
|
||
- Step 1 → Step 2 → Step 3 完整流程走通(含 MinerU + 审核抽屉 + Excel)
|
||
- fuzzyQuoteMatch 边界测试(连字符替换、空格差异、换行吞掉)
|
||
- 断点恢复测试(关闭浏览器 → 重新打开 → 恢复正确步骤)
|
||
- 散装 10 篇并发提取压力测试
|
||
|
||
**验收标准:**
|
||
- [ ] 8 篇测试 PDF 全链路跑通:PKB → MinerU + LLM → 抽屉审核 → Excel 导出
|
||
- [ ] 中途关闭浏览器后恢复正确
|
||
- [ ] 10 篇并发无数据丢失、无重复
|
||
|
||
---
|
||
|
||
## M2 结束时的状态
|
||
|
||
```
|
||
✅ M1 全部 +
|
||
✅ MinerU 表格引擎 + OSS 缓存(Worker 内部串行调用)
|
||
✅ XML 隔离 Prompt + 表格优先级
|
||
✅ fuzzyQuoteMatch 三级置信度验证
|
||
✅ SSE 终端日志(双轨制:React Query 轮询主驱 + SSE 本 Pod 日志增强)
|
||
✅ 完整审核抽屉(Collapse + Quote + HITL 解锁 + 签名 URL)
|
||
✅ Excel 宽表导出
|
||
⏳ [P2 可选] SSE NOTIFY/LISTEN 跨 Pod 广播(多 Pod 部署后按需实施)
|
||
❌ 无自定义字段(仅系统基座模板)
|
||
❌ 无 Prompt 注入防护(无用户输入,不需要)
|
||
❌ 无 E2E 自动化测试
|
||
```
|
||
|
||
> **M2 的核心价值:** 此时工具 3 已是一个"完全可用且高度可用"的产品。用标准 RCT 模板提取文献已经足够惊艳。如果项目赶进度,可以直接拿 M1+M2 给真实医生试用,M3 作为 v2.1 后续迭代。
|