Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/06-技术文档/工具3终极架构审查与研发规范.md
HaHafeng dc6b292308 docs(asl): Complete Tool 3 extraction workbench V2.0 development plan (v1.5)
ASL Tool 3 Development Plan:
- Architecture blueprint v1.5 (6 rounds of architecture review, 13 red lines)
- M1/M2/M3 sprint checklists (Skeleton Pipeline / HITL Workbench / Dynamic Template Engine)
- Code patterns cookbook (9 chapters: Fan-out, Prompt engineering, ACL, SSE dual-track, etc.)
- Key patterns: Fan-out with Last Child Wins, Optimistic Locking, teamConcurrency throttling
- PKB ACL integration (anti-corruption layer), MinerU Cache-Aside, NOTIFY/LISTEN cross-pod SSE
- Data consistency snapshot for long-running extraction tasks

Platform capability:
- Add distributed Fan-out task pattern development guide (7 patterns + 10 anti-patterns)
- Add system-level async architecture risk analysis blueprint
- Add PDF table extraction engine design and usage guide (MinerU integration)
- Add table extraction source code (TableExtractionManager + MinerU engine)

Documentation updates:
- Update ASL module status with Tool 3 V2.0 plan readiness
- Update system status document (v6.2) with latest milestones
- Add V2.0 product requirements, prototypes, and data dictionary specs
- Add architecture review documents (4 rounds of review feedback)
- Add test PDF files for extraction validation

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-23 22:49:16 +08:00

6.2 KiB
Raw Permalink Blame History

🎯 终极架构审查与研发红线规范:工具 3 全文智能提取 V2.0

文档性质: 架构定稿与研发执行标准

审查基准: 《Postgres-Only 异步任务处理指南 (v1.1)》、《通用能力层清单 (v2.4)》

适用对象: 后端研发、前端研发、测试工程师

核心宗旨: 确保工具 3 在分布式环境下的高可用性,彻底对齐平台现有的 Postgres-Only 规范,消除并发死锁、状态撕裂与算力浪费。

🚨 核心研发红线 (Sprint 1 强制执行)

在进入代码编写前,所有研发人员必须对齐以下三条底层红线:

  1. Payload 绝对轻量化: pg-boss 的 Job Data 中,绝对不允许传入 PDF 文件的 Base64 或 extractedText 全文。只能传递 { taskId, resultId, pkbDocumentId }。所需的大文本必须在 Worker 启动后,通过 ID 实时从 DB 或 OSS 拉取。
  2. 严格的计算卸载: Node.js 进程绝对不碰任何文档实体的解析计算pymupdf4llm 或 MinerU。所有解析动作必须通过 HTTP 路由给独立的 Python 微服务 (extraction_service) 执行。
  3. 过期时间兜底: 由于大模型提取长文本耗时较长,在推送 asl_extraction_child 任务时expireInMinutes 强制设置为 30 分钟,防止任务被系统意外判死。

🛠️ 后端架构排雷与对齐方案

1. 废弃单机并发控制,拥抱全局队列限流

  • 错误做法: 在 ExtractionService 内部使用 P-Queue 控制 MinerU 并发。在多实例Pods部署下这会导致真实的 API 请求量翻倍,瞬间引发 429 熔断和重试风暴。
  • 标准解法: 把并发控制权交还给数据库。针对昂贵的 MinerU 解析,单独拆分一个子队列 asl_mineru_extract配置严格的全局并发数
    // 全局只允许同时有 2 个 MinerU 解析任务在跑,跨所有 Node.js 实例生效
    jobQueue.process('asl_mineru_extract', { teamConcurrency: 2 }, async (job) => { ... })

2. Fan-out 扇出模式下的并发写入安全

  • 错误做法: 查询父任务 -> successCount + 1 -> 更新父任务。100 个子任务并发完成时会导致严重的计数丢失Race Condition

  • 标准解法 (原子递增) 所有聚合数据的回写,必须 100% 使用 Prisma 的原子操作,并结合幂等性检查:
    // 1. 幂等性检查
    const existing = await prisma.aslExtractionResult.findUnique({ where: { id: resultId }});
    if (existing.status === 'completed') return { success: true };

    // 2. 事务内的原子递增
    await prisma.$transaction([
    prisma.aslExtractionResult.update({ where: { id: resultId }, data: { status: 'completed' } }),
    prisma.aslExtractionTask.update({
    where: { id: taskId },
    data: { successCount: { increment: 1 }, totalTokens: { increment: tokens } }
    })
    ]);

3. 错误处理边界:永久失败 vs 自动重试

  • 业务痛点: 严格遵循《Postgres-Only 指南》规范1直接 throw error会导致在遇到“PKB源文件被删除”等不可逆错误时pg-boss 盲目重试 3 次,白白消耗资源。
  • 标准解法 (异常分级路由) 在 Worker 中必须明确区分“可恢复错误”与“致命错误”:
    try {
    await doExtraction();
    } catch (error) {
    if (error instanceof PkbDocumentNotFoundError || error.name === 'PdfCorruptedError') {
    // 致命错误:更新业务状态为 error直接 return success 欺骗 pg-boss 停止重试
    await prisma.aslExtractionResult.update({
    where: { id: resultId }, data: { status: 'error', errorMessage: error.message }
    });
    return { success: false, reason: 'Permanent Failure, aborted retry.' };
    }
    // 临时错误 (429/网络抖动):直接 throw让 pg-boss 自动指数退避重试
    throw error;
    }

4. 极致落实 Clean Data 缓存机制

  • 业务痛点: MinerU 表格解析极度昂贵且耗时。
  • 标准解法: 必须前置检查 OSS 缓存,避免重复计算。
    const cleanDataKey = `pkb/${kbId}/${docId}_mineru_clean.html`;
    try {
    const html = await storage.download(cleanDataKey); // 优先读取缓存 (<1秒)
    return html;
    } catch (e) {
    const html = await callPythonMinerUService(pdfKey); // Fallback: 真正调用
    await storage.upload(cleanDataKey, Buffer.from(html)); // 同步存入 Clean Data
    return html;
    }

💻 前端架构排雷与对齐方案

1. 通信机制的优雅混合 (React Query + SSE)

  • 业务痛点: 前端到底是采用 SSE 维持连接,还是用 React Query 轮询?混用会导致状态撕裂。
  • 标准解法:
    • 主业务流控制 (Step 进度、成功/失败跳页) 严格遵守《Postgres-Only 指南》步骤5。使用 useTaskStatus (React Query) 的 refetchInterval 进行串行稳健轮询。
    • 视觉反馈增强 (终端日志流) 引入 SSE 单向通道,仅用于给 <ProcessingTerminal /> 组件灌入实时的打字机日志流。即使 SSE 意外断开,也不会阻断主线业务流。

2. 人机协作 (HITL) 抽屉的死锁解套

  • 业务痛点: 若 AI 提取的 Quote 模糊匹配失败(置信度 < 0.8),前端标红警告。但如果医生强行认为 AI 提取的没错,系统没有提供放行的交互,导致数据卡在 Pending 状态。
  • 标准解法: 抽屉内的错误警告框必须配套两个处理按钮:
    1. [强制认可]:消除警告,在 payload 中标记 quote_force_accepted: true。
    2. [手动修改数值]:医生直接修改 Input 框,系统自动给旧的错误 Quote 画上删除线,并提示“已转为人工干预,原文引用取消强绑定”。

3. 规避签名 URL 过期导致的 403 报错

  • 业务痛点: 医生复核 50 篇文献需要很长时间,预签名的 OSS PDF 链接容易过期。
  • 标准解法: 绝对禁止在加载列表时批量生成并缓存签名 URL。采用懒加载:仅当医生点击某行文献的“复核提单”并展开右侧抽屉时,前端才实时请求获取一个有效期为 10 分钟的临时 URL 赋给 iframe。