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>
6.2 KiB
🎯 终极架构审查与研发红线规范:工具 3 全文智能提取 V2.0
文档性质: 架构定稿与研发执行标准
审查基准: 《Postgres-Only 异步任务处理指南 (v1.1)》、《通用能力层清单 (v2.4)》
适用对象: 后端研发、前端研发、测试工程师
核心宗旨: 确保工具 3 在分布式环境下的高可用性,彻底对齐平台现有的 Postgres-Only 规范,消除并发死锁、状态撕裂与算力浪费。
🚨 核心研发红线 (Sprint 1 强制执行)
在进入代码编写前,所有研发人员必须对齐以下三条底层红线:
- Payload 绝对轻量化: pg-boss 的 Job Data 中,绝对不允许传入 PDF 文件的 Base64 或 extractedText 全文。只能传递 { taskId, resultId, pkbDocumentId }。所需的大文本必须在 Worker 启动后,通过 ID 实时从 DB 或 OSS 拉取。
- 严格的计算卸载: Node.js 进程绝对不碰任何文档实体的解析计算(pymupdf4llm 或 MinerU)。所有解析动作必须通过 HTTP 路由给独立的 Python 微服务 (extraction_service) 执行。
- 过期时间兜底: 由于大模型提取长文本耗时较长,在推送 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 状态。
- ✅ 标准解法: 抽屉内的错误警告框必须配套两个处理按钮:
- [强制认可]:消除警告,在 payload 中标记 quote_force_accepted: true。
- [手动修改数值]:医生直接修改 Input 框,系统自动给旧的错误 Quote 画上删除线,并提示“已转为人工干预,原文引用取消强绑定”。
3. 规避签名 URL 过期导致的 403 报错
- 业务痛点: 医生复核 50 篇文献需要很长时间,预签名的 OSS PDF 链接容易过期。
- ✅ 标准解法: 绝对禁止在加载列表时批量生成并缓存签名 URL。采用懒加载:仅当医生点击某行文献的“复核提单”并展开右侧抽屉时,前端才实时请求获取一个有效期为 10 分钟的临时 URL 赋给 iframe。