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>
This commit is contained in:
2026-02-23 22:49:16 +08:00
parent 8f06d4f929
commit dc6b292308
42 changed files with 16615 additions and 41 deletions

View File

@@ -0,0 +1,96 @@
# **🎯 终极架构审查与研发红线规范:工具 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。