From 8be741cd52c4b30bd044a8af448d0917d7c9c3e0 Mon Sep 17 00:00:00 2001 From: HaHafeng Date: Sat, 6 Dec 2025 11:00:44 +0800 Subject: [PATCH] docs(dc/tool-c): Complete Tool C MVP planning and TODO list Summary: - Update Tool C MVP Development Plan (V1.3) * Clarify Python execution as core feature * Add 15 real medical data cleaning scenarios (basic/medium/advanced) * Enhance System Prompt with 10 Few-shot examples * Discover existing Python service (extraction_service) * Update to extend existing service instead of rebuilding - Create Tool C MVP Development TODO List * 3-week plan with 30 tasks (Day 1-15) * 4 core milestones with clear acceptance criteria * Daily checklist and risk management * Detailed task breakdown for each day Key Changes: - Python service: Extend existing extraction_service instead of new setup - Test scenarios: 15 scenarios (5 basic + 5 medium + 5 advanced) - Success criteria: Basic >90%, Medium >80%, Advanced >60%, Total >80% - Development time: Reduced from 3 weeks to 2 weeks (reuse infrastructure) Status: Planning complete, ready to start Day 1 development --- DC模块代码恢复指南.md | 1 + backend/recover-code-from-cursor-db.js | 1 + backend/scripts/check-dc-tables.mjs | 1 + .../__tests__/api-integration-test.ts | 1 + .../__tests__/e2e-real-test-v2.ts | 1 + .../__tests__/fulltext-screening-api.http | 1 + .../services/ExcelExporter.ts | 1 + .../services/ConflictDetectionService.ts | 1 + .../dc/tool-b/services/TemplateService.ts | 1 + backend/sync-dc-database.ps1 | 1 + .../04-开发计划/05-全文复筛前端开发计划.md | 1 + .../05-开发记录/2025-01-23_全文复筛前端开发完成.md | 1 + .../05-开发记录/2025-01-23_全文复筛前端逻辑调整.md | 1 + .../05-开发记录/2025-11-23_Day5_全文复筛API开发.md | 1 + .../01-需求分析/PRD_工具C_科研数据编辑器_V6.md | 114 + .../PRD:Tool C - 科研数据编辑器 (MVP V1.1).md | 107 + ...设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md | 164 ++ .../03-UI设计/工具C_原型设计V6 .html | 479 ++++ .../04-开发计划/工具C_MVP开发_TODO清单.md | 620 +++++ .../04-开发计划/工具C_MVP开发计划_V1.0.md | 2061 +++++++++++++++++ .../06-开发记录/2025-12-02_工作总结.md | 1 + .../06-开发记录/DC模块重建完成总结-Day1.md | 1 + .../06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md | 1 + .../Phase2-ToolB-Step1-2开发完成-2025-12-03.md | 1 + .../06-开发记录/Portal页面UI优化-2025-12-02.md | 1 + .../06-开发记录/Tool-B-MVP完成总结-2025-12-03.md | 1 + .../06-开发记录/ToolB-UI优化-2025-12-03.md | 1 + .../06-开发记录/ToolB-UI优化-Round2-2025-12-03.md | 1 + .../06-开发记录/ToolB浏览器测试计划-2025-12-03.md | 1 + .../06-开发记录/后端API测试报告-2025-12-02.md | 1 + .../06-开发记录/待办事项-下一步工作.md | 1 + .../06-开发记录/数据库验证报告-2025-12-02.md | 1 + .../07-技术债务/Tool-B技术债务清单.md | 1 + .../asl/components/FulltextDetailDrawer.tsx | 1 + .../modules/asl/hooks/useFulltextResults.ts | 1 + .../src/modules/asl/hooks/useFulltextTask.ts | 1 + .../src/modules/asl/pages/FulltextResults.tsx | 1 + frontend-v2/src/modules/dc/hooks/useAssets.ts | 1 + .../src/modules/dc/hooks/useRecentTasks.ts | 1 + frontend-v2/src/modules/dc/types/portal.ts | 1 + recover_dc_code.py | 1 + run_recovery.ps1 | 1 + 42 files changed, 3581 insertions(+) create mode 100644 docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD_工具C_科研数据编辑器_V6.md create mode 100644 docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器 (MVP V1.1).md create mode 100644 docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md create mode 100644 docs/03-业务模块/DC-数据清洗整理/03-UI设计/工具C_原型设计V6 .html create mode 100644 docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发_TODO清单.md create mode 100644 docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md diff --git a/DC模块代码恢复指南.md b/DC模块代码恢复指南.md index 007f7496..566bce66 100644 --- a/DC模块代码恢复指南.md +++ b/DC模块代码恢复指南.md @@ -222,3 +222,4 @@ + diff --git a/backend/recover-code-from-cursor-db.js b/backend/recover-code-from-cursor-db.js index aa106518..829e2085 100644 --- a/backend/recover-code-from-cursor-db.js +++ b/backend/recover-code-from-cursor-db.js @@ -179,3 +179,4 @@ function extractCodeBlocks(obj, blocks = []) { + diff --git a/backend/scripts/check-dc-tables.mjs b/backend/scripts/check-dc-tables.mjs index a197dd41..5b2af70a 100644 --- a/backend/scripts/check-dc-tables.mjs +++ b/backend/scripts/check-dc-tables.mjs @@ -198,3 +198,4 @@ async function checkDCTables() { checkDCTables(); + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts b/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts index 5c571dde..f16232d8 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts +++ b/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts @@ -302,3 +302,4 @@ runTests().catch((error) => { + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts b/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts index 3d568284..026cbdaa 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts +++ b/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts @@ -243,3 +243,4 @@ runTest() + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http b/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http index 1ff705e6..ff1b6d79 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http +++ b/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http @@ -281,3 +281,4 @@ Content-Type: application/json + diff --git a/backend/src/modules/asl/fulltext-screening/services/ExcelExporter.ts b/backend/src/modules/asl/fulltext-screening/services/ExcelExporter.ts index 5f0ce885..da779300 100644 --- a/backend/src/modules/asl/fulltext-screening/services/ExcelExporter.ts +++ b/backend/src/modules/asl/fulltext-screening/services/ExcelExporter.ts @@ -360,3 +360,4 @@ export class ExcelExporter { + diff --git a/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts b/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts index 5eb7e21f..55c65304 100644 --- a/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts +++ b/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts @@ -217,3 +217,4 @@ export const conflictDetectionService = new ConflictDetectionService(); + diff --git a/backend/src/modules/dc/tool-b/services/TemplateService.ts b/backend/src/modules/dc/tool-b/services/TemplateService.ts index ac2065a2..8fdf2103 100644 --- a/backend/src/modules/dc/tool-b/services/TemplateService.ts +++ b/backend/src/modules/dc/tool-b/services/TemplateService.ts @@ -245,3 +245,4 @@ export const templateService = new TemplateService(); + diff --git a/backend/sync-dc-database.ps1 b/backend/sync-dc-database.ps1 index af38a3d2..5bb6dfb5 100644 --- a/backend/sync-dc-database.ps1 +++ b/backend/sync-dc-database.ps1 @@ -25,3 +25,4 @@ Write-Host "✅ 完成!" -ForegroundColor Green + diff --git a/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md b/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md index f996157f..fe031562 100644 --- a/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md +++ b/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md @@ -1242,3 +1242,4 @@ interface FulltextScreeningResult { + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md index 322436cb..1f88a2ff 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md @@ -356,3 +356,4 @@ GET /api/v1/asl/fulltext-screening/tasks/:taskId/export + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md index 26278967..15b7734f 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md @@ -299,3 +299,4 @@ Linter错误:0个 + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md index 5621af70..3fef2af8 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md @@ -458,3 +458,4 @@ Failed to open file '\\tmp\\extraction_service\\temp_10000_test.pdf' + diff --git a/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD_工具C_科研数据编辑器_V6.md b/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD_工具C_科研数据编辑器_V6.md new file mode 100644 index 00000000..1d5aa3aa --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD_工具C_科研数据编辑器_V6.md @@ -0,0 +1,114 @@ +# **PRD:Tool C \- 科研数据编辑器 (The Research Editor)** + +| 文档版本 | V6.0 (AI Code Interpreter 增强版) | +| :---- | :---- | +| **产品形态** | Web 端双核编辑器 (GUI 可视化操作 \+ LUI 自然语言交互) | +| **核心价值** | 将“Excel 的易用性”与“Python 的强大能力”结合。医生既可以通过鼠标点击完成微操,也可以通过自然语言指挥 AI 编写代码完成复杂的清洗任务(如长宽转换、多重插补)。 | +| **技术底座** | Node.js BFF \+ Python Server-side Sandbox \+ DeepSeek-V3 | + +## **一、 产品核心理念 (Core Philosophy)** + +### **1.1 双核驱动 (Dual-Core Interaction)** + +* **左脑 (GUI):** 提供类似 Excel 的网格和工具栏,适合“直觉式、原子化”的操作(如手动修改一个值、排序、筛选)。 +* **右脑 (AI Copilot):** 提供对话式代码解释器,适合“逻辑性、批量化”的操作(如“把年龄按60岁分箱”、“删除所有空行”、“计算生存时间”)。 + +### **1.2 可控黑盒 (Controllable Blackbox)** + +AI 不直接修改数据,而是**生成 Python 代码**。系统在执行前展示\*\*“预操作卡片”\*\*,由用户确认执行,确保科研数据的严谨性。 + +## **二、 核心业务流程 (User Flow)** + +数据导入 \-\> 双模式清洗 (点击工具栏 OR 对话 AI) \-\> 代码/操作执行 \-\> 实时预览更新 \-\> 版本快照 \-\> 导出结果 + +## **三、 功能模块详解 (Functional Requirements)** + +### **1\. 界面布局 (The Workspace)** + +* **P0:** **分栏布局 (Split View):** + * **左侧 (70%):** 超级网格 (The Grid),展示数据预览。 + * **右侧 (30%):** 智能侧边栏 (Smart Sidebar),包含 **\[统计概览\]** 和 **\[AI 助手\]** 两个 Tab。 +* **P0:** **全局状态指示:** + * 当 AI 正在思考或后端正在计算时,左侧网格显示 **“AI 处理中...”** 遮罩,并锁定编辑,防止双写冲突。 + +### **2\. 顶部扁平工具栏 (Flat Toolbar) —— GUI 核心** + +*保留高频、标准化的操作入口,作为 AI 的补充。* + +* **P0: 变量加工:** + * **生成新变量:** 弹窗公式构建器。 + * **计算时间差:** 内置医学常数 (年=365.25天)。 + * **生成哑变量:** 回归分析专用。 + * **横纵表转换 (Pivot):** \* *交互升级:* 点击后不再只是纯前端计算,而是调用后端 Python 逻辑,支持处理更复杂的转换。 +* **P0: 质量治理:** + * **查找重复值:** 按 ID 或全字段查重。 + * **多重插补 (MICE):** 全局入口,调用后端 sklearn 或 fancyimpute 库。 +* **P0: 样本筛选:** 构建入排标准。 + +### **3\. AI Copilot 智能助手 (The Brain) —— V6 核心升级** + +*位于右侧侧边栏的 \[AI 助手\] Tab。* + +#### **3.1 自然语言指令解析** + +* **P0:** **意图识别:** 支持模糊指令,如“洗一下数据”、“把男变成1”。 +* **P0:** **上下文感知:** AI 能够读取当前的列名 (Metadata) 和前 5 行数据示例,理解数据含义。 + +#### **3.2 代码解释器模式 (Code Interpreter)** + +* **P0:** **代码生成:** AI 针对用户需求,生成可执行的 **Python (Pandas)** 代码块。 +* **P0:** **预操作卡片 (Action Card):** + * AI 不直接执行代码。 + * 界面展示一个卡片:操作类型: 数据分箱 | 目标列: 年龄 | 代码预览。 + * 按钮:**\[运行代码\]** | **\[取消\]**。 +* **P0:** **执行反馈:** + * 执行成功:显示 ✅,左侧表格自动刷新。 + * 执行失败:AI 自动分析 Error Log,尝试自我修正代码并建议重试。 + +#### **3.3 典型 AI 场景支持** + +* **高级清洗:** “把所有列的异常值(\>3倍标准差)替换为缺失值”。 +* **复杂提取:** “从‘诊断’列中提取出由‘/’分隔的第二部分,生成新列”。 +* **批量处理:** “删除所有缺失率超过 50% 的列”。 + +### **4\. 智能统计面板 (Insight Panel)** + +*位于右侧侧边栏的 \[统计概览\] Tab。* + +* **P0:** **列联动:** 点击左侧网格某一列,右侧自动显示该列的分布图(直方图/频次图)。 +* **P0:** **快捷操作:** 图表下方直接提供“填补”、“分箱”、“映射”等快捷按钮。 + +### **5\. 导出与流转 (Export)** + +* **P0:** **结果导出:** 支持 Excel (.xlsx) 和 SPSS (.sav) 格式。 +* **P0:** **操作审计:** 导出的文件中,附带一份 **"清洗日志 (Cleaning Log)"**,记录了所有的 AI 代码和手动操作步骤(用于科研溯源)。 + +## **四、 数据与性能策略 (Data Strategy)** + +### **4.1 性能准入 (Guardrails)** + +* **文件大小限制:** 单个文件 **\< 20MB**。 +* **行数限制:** 建议 **\< 50,000 行** 以保证前端渲染流畅度。 + * *策略:* 后端 Python 可以处理百万行,但前端 AG Grid 仅加载前 100-1000 行作为预览(Preview Mode),导出时才生成全量文件。 + +### **4.2 安全与隐私** + +* **P0:** **沙箱隔离:** AI 生成的 Python 代码必须在服务端的安全沙箱(Docker/SAE)中运行,禁止访问外网和系统文件。 +* **P0:** **数据脱敏:** 确保输入编辑器的数据已在前置环节(工具 B)完成了 PII 脱敏。 + +## **五、 埋点与统计** + +* **AI 采纳率:** 展示 Action Card 后,用户点击“运行”的比例。 +* **代码报错率:** AI 生成的代码在后端执行失败的比例。 +* **常用指令 Top 10:** 统计医生最常对 AI 说的话。 + +## **六、 附录:AI 指令集示例 (Few-Shot Examples)** + +| 用户指令 | AI 动作 (Action) | 生成代码逻辑 (Python Pandas) | +| :---- | :---- | :---- | +| "把性别转为数字" | Recode | df\['sex'\] \= df\['sex'\].map({'男':1, '女':0}) | +| "年龄按60分两组" | Binning | df\['age\_group'\] \= pd.cut(df\['age'\], bins=\[0,60,150\], labels=\['0','1'\]) | +| "删除没有ID的行" | Filter | df \= df.dropna(subset=\['patient\_id'\]) | +| "计算BMI" | Formula | df\['bmi'\] \= df\['weight'\] / (df\['height'\]/100)\*\*2 | +| "把每个人的一行变成多行" | Pivot/Melt | df \= df.melt(id\_vars=\['id'\], ...) | + diff --git a/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器 (MVP V1.1).md b/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器 (MVP V1.1).md new file mode 100644 index 00000000..9d2e960b --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器 (MVP V1.1).md @@ -0,0 +1,107 @@ +# **PRD:Tool C \- 科研数据编辑器 (MVP V1.1)** + +| 文档版本 | V1.1 (工程细化版) | +| :---- | :---- | +| **产品形态** | **Web 端数据编辑器 (AG Grid \+ AI Chat)** | +| **核心策略** | **"AI-First" \+ "Server-side State"**。依靠 AI 生成 Python 代码完成清洗;以服务端 DataFrame 为单一数据源,前端负责渲染和轻量编辑。 | +| **技术底座** | React \+ AG Grid \+ Node.js BFF \+ Python Sandbox (FastAPI) \+ DeepSeek-V3 | +| **变更记录** | V1.1: 增加数据同步机制、撤销回滚策略、会话生命周期定义。 | + +## **一、 MVP 核心目标 (Objectives)** + +1. **验证闭环:** 跑通 “自然语言 \-\> Python 代码 \-\> 后端执行 \-\> 前端刷新” 的完整链路。 +2. **数据一致性:** 确保手动编辑与 AI 操作不冲突,状态可追溯。 +3. **可用性:** 解决中文乱码、会话超时等实际工程问题。 + +## **二、 详细功能需求 (Functional Requirements)** + +### **1\. 界面框架与会话 (Shell & Session)** + +* **P0: 左右分栏布局:** 左侧 AG Grid (70%),右侧 AI Chat (30%)。 +* **P0: 会话初始化 (Session Init):** + * 用户上传文件 \-\> 后端开启 Python 进程/容器 \-\> 加载 df \-\> 返回 sessionId。 + * **编码检测:** 后端必须尝试 utf-8 和 gbk 解码,防止中文乱码。 +* **P1: 心跳与保活 (Keep-alive):** + * 前端每 30s 发送心跳。 + * **超时策略:** 若超过 30min 无操作,后端释放内存。用户再次操作时,提示“会话已过期,请重新加载文件”。 + +### **2\. 超级网格 (The Grid) \- *前端交互*** + +* **P0: 数据展示 (View):** + * 通过 API 分页拉取数据(预览模式,仅取前 100-500 行)。 + * **列类型推断:** 前端根据后端返回的 dtypes 渲染列头图标(数值/文本/日期)。 +* **P0: 手动编辑 (Manual Edit):** + * 支持双击修改单元格。 + * **关键逻辑(脏数据标记):** 用户修改后,单元格右标红,数据暂存在前端 dirtyRows 队列中。 +* **P0: 状态锁 (UI Locking):** + * 当 AI 正在执行时,Grid 变为 **只读 (Read-only)**,显示 Loading 遮罩。 + +### **3\. AI Copilot 智能助手 (The Brain) \- *后端驱动*** + +#### **3.1 交互流程 (Chat Loop)** + +1. **用户输入:** “把年龄大于60的设为老年组”。 +2. **前置同步 (Auto-Sync):** **(V1.1 新增)** + * **判定:** 前端检查是否有未保存的手动修改 (dirtyRows). + * **动作:** 如果有,先静默发送 PATCH /api/data 将手动修改同步给后端 df。确保 AI 基于最新数据操作。 +3. **代码生成:** 后端调用 DeepSeek,生成 Pandas 代码。 +4. **预操作确认 (Action Card):** + * 展示:代码预览 \+ 摘要。 + * 按钮:\[运行\] | \[取消\]。 + +#### **3.2 执行与回滚 (Execute & Rollback) \- (V1.1 核心)** + +* **P0: 自动快照 (Auto-Checkpoint):** + * 在执行 exec(code) 之前,后端必须先对当前 df 进行内存快照(或序列化备份)。 + * 记录操作日志:ActionID: 101, Type: AI\_CODE, Code: "..."。 +* **P0: 执行反馈:** + * 成功:返回新的预览数据 \-\> 刷新 Grid \-\> 添加一条“操作成功”消息。 + * 失败:捕获 Python Traceback \-\> 让 AI 尝试自我修复 (Self-Correction) 一次 \-\> 若仍失败,向用户展示错误原因。 +* **P0: 撤销操作 (Undo):** + * 聊天框每条成功记录下显示 \[撤销\] 按钮。 + * 逻辑:调用后端 rollback(action\_id) \-\> 恢复到该操作前的快照 \-\> 刷新 Grid。 + +### **4\. 快捷指令 (Prompt Chips)** + +* **P0:** 顶部工具栏按钮(生成变量、长宽转换等),点击仅作为“快捷短语”填入输入框,不触发独立 UI 逻辑,保持架构统一。 + +### **5\. 导出 (Export)** + +* **P0:** 导出 Excel。 + * 逻辑:后端直接将当前的 df (包含所有 AI 修改和手动修改) 写入 Excel 流并返回。 + +## **三、 异常处理规范 (Error Handling)** + +| 异常场景 | 前端表现 | 后端处理 | +| :---- | :---- | :---- | +| **中文乱码** | 提示“编码格式识别失败,请手动选择” | 尝试 chardet 检测,失败则报错 | +| **AI 代码报错** | Chat 气泡变红,显示“AI 正在尝试修复...” | 捕获 stderr,将错误反喂给 AI 重试 (Max 1次) | +| **会话过期** | 全屏遮罩“会话已过期” | 清理 Redis/内存,返回 401/404 | +| **手动修改冲突** | 提示“正在同步数据...” | 优先处理手动 Patch,再执行 AI 任务 | + +## **四、 开发与测试重点 (QA Focus)** + +1. **一致性测试:** + * 先手动改一个值,再让 AI 删一行,确认那个值还在不在(或者是否正确被删)。 + * 先让 AI 改一列,再撤销,确认是否完全恢复。 +2. **边界测试:** + * 上传空文件。 + * 上传全中文列名的文件。 + * 让 AI 计算一个不存在的列。 +3. **性能测试:** + * 连续快速发送 5 条指令,确保后端是**串行队列**处理,而不是并发搞乱数据。 + +## **五、 附录:数据同步 API 定义 (简版)** + +* POST /api/sync: 前端 \-\> 后端。发送手动修改的 Diff。 + * Payload: \[{ rowId: "P001", col: "age", value: 66 }\] +* POST /api/run: 前端 \-\> 后端。发送 AI 代码执行请求。 + * Payload: { code: "df\['new'\] \= 1", snapshot: true } +* POST /api/undo: 前端 \-\> 后端。 + * Payload: { step: \-1 } + +\#\#\# 给开发团队的一句话总结 + +\*\*“V1.1 的核心在于‘状态管理’。请务必保证后端 Python 内存里的 \`DataFrame\` 是唯一的‘真理来源 (Source of Truth)’。前端的所有操作(无论是手改还是 AI 改),本质上都是在向这个真理层发送指令和同步状态。”\*\* + +现在,您可以拿着这份文档,很有底气地去和开发团队开工了。它已经堵上了最容易翻车的几个漏洞。 diff --git a/docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md b/docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md new file mode 100644 index 00000000..2cf27679 --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md @@ -0,0 +1,164 @@ +# **技术设计文档:工具 C \- 科研数据编辑器 (V7 云端沙箱抗风险版)** + +| 文档类型 | Technical Design Document (TDD) | +| :---- | :---- | +| **对应原型** | **工具C\_科研数据编辑器\_原型设计\_V6\_修复版.html** | +| **版本** | **V7.1** (完整收录架构决策 ADR 与红队风险对策) | +| **状态** | Final Standard | +| **核心目标** | 构建一个高可靠的云端 Python 数据清洗平台。在保障“数据不出域”的前提下,通过 Apache Arrow 和样式分离技术,解决服务端执行带来的延迟与格式丢失问题。 | + +## **1\. 总体架构设计 (System Architecture)** + +鉴于文件大小限制 (\<20MB) 和脱敏前提,采用 “Node.js BFF \+ Python Microservice” 架构。 +V7 核心升级: 引入 Apache Arrow 作为前后端数据交换标准,替代低效的 Excel 文件反复读写,将单次交互延迟从 8s 降低至 0.5s。 + +### **1.1 架构拓扑图 (V7 优化版)** + +graph TD + subgraph Client\_Layer \[用户端\] + ReactApp\[React 19 \+ AG Grid\] + ArrowClient\[Apache Arrow JS\] + end + + subgraph Aliyun\_SAE \[阿里云 Serverless 应用引擎\] + BFF\[Node.js Web 服务 (Fastify)\] + PythonService\[Python 计算微服务 (FastAPI)\] + end + + subgraph Cache\_Layer \[高速缓存层\] + Redis\_Session\[Redis (存 DataFrame Arrow 序列化)\] + end + + subgraph AI\_PaaS \[AI 能力层\] + Dify\[Dify 编排引擎\] + DeepSeek\[DeepSeek-V3 模型\] + end + + subgraph Cloud\_Infra \[持久化层\] + OSS\[对象存储 (存 Excel 底板)\] + RDS\[RDS PostgreSQL (存元数据)\] + end + + %% 交互流 + User\[用户\] \--\>|1. 上传| BFF + BFF \--\>|2. 存原始Excel| OSS + BFF \--\>|3. 预热 Session| PythonService + PythonService \--\>|4. 加载并转为 Arrow| Redis\_Session + + User \--\>|5. AI 指令| Dify + Dify \--\>|6. Python 代码| BFF + BFF \--\>|7. 发送代码| PythonService + + PythonService \--\>|8. 读取 Arrow (内存)| Redis\_Session + PythonService \--\>|9. Pandas 执行| PythonService + PythonService \--\>|10. 写回 Arrow| Redis\_Session + + PythonService \--\>|11. 返回 Preview 数据 (JSON/Arrow)| ReactApp + + User \--\>|12. 导出/保存| BFF + BFF \--\>|13. 触发合并| PythonService + PythonService \--\>|14. 读OSS底板 \+ 填入数据| OSS + +## **2\. 关键架构决策记录 (ADR)** + +本节记录了为何从“前端 Pyodide”转向“后端沙箱”的决策过程,供团队参考。 + +### **决策点:前端运行 (WASM) vs 后端运行 (Server-side)** + +| 维度 | 方案 A:前端 Pyodide (WASM) | 方案 B:后端 Python (本方案) | 决策结论 | +| :---- | :---- | :---- | :---- | +| **启动延迟** | **极慢 (15s+)**。需下载 \~20MB 引擎包,用户体验极差。 | **秒开**。环境在服务器预热,即开即用。 | **后端胜** | +| **交互延迟** | **极快 (\< 0.1s)**。本地内存操作,无网络开销。 | **中等 (0.5s)**。通过 Apache Arrow 优化后可接受。 | 前端胜 | +| **稳定性** | **高风险**。浏览器 Tab 内存有限,易 OOM 崩溃。 | **高稳定**。服务器内存充足,容器隔离,崩溃不影响前端。 | **后端胜** | +| **库支持** | **有限**。不支持部分 C 扩展库 (如复杂统计库)。 | **无限**。标准 Linux 环境,生态完整。 | **后端胜** | +| **开发难度** | **极高**。需处理 JS-Python 通信、内存管理。 | **低**。标准 Web API 开发。 | **后端胜** | + +**结论:** 鉴于“数据已脱敏”且“文件较小”,**后端执行方案** 在稳定性、兼容性和开发成本上全面胜出。 + +## **3\. 技术选型与融合 (Tech Stack Fusion)** + +### **3.1 核心组件更新** + +| 领域 | 选型 | V7 新增理由 | +| :---- | :---- | :---- | +| **数据交换** | **Apache Arrow** | **关键升级**。用于 Python 和 Node.js/前端之间的高性能数据传输,避免 JSON 序列化开销,**解决 IO 延迟核心**。 | +| **Excel 处理** | **openpyxl** | 替代纯 Pandas。用于在导出时**保留原始 Excel 样式**(如颜色、边框),**解决格式丢失核心**。 | +| **会话缓存** | **Redis** | 用于暂存用户的 DataFrame (序列化为 Parquet/Arrow),避免每次操作都去 OSS 读文件。 | +| **计算服务** | **FastAPI \+ Celery** | 引入 Celery 处理异步任务,防止长计算阻塞 HTTP 线程。 | + +## **4\. 逆向风险评估与对策 (Red Teaming Analysis)** + +本节详细记录了“红队测试”中发现的潜在致命风险及其工程化解决方案。 + +### **风险一:交互延迟的“体感崩塌”** + +* **逆向拷问:** 每次 AI 操作都走 OSS 下载 \-\> Pandas 读取 \-\> 计算 \-\> 上传,单次耗时可能超过 8秒,用户无法忍受。 +* **V7 解决方案:** **Session 驻留模式 (Memory-Resident)** + 1. **初始化:** 用户上传 Excel 后,后端将其加载为 DataFrame,并序列化为 **Arrow** 格式存入 Redis (TTL 30min)。 + 2. **增量交互:** 前端发送指令,Python 从 Redis 读取 Arrow 数据(毫秒级),执行 Pandas 计算,将结果写回 Redis。 + 3. **轻量反馈:** 计算完成后,只返回 **前 100 行预览数据** 给前端 AG Grid 渲染。 + 4. **效果:** 耗时缩短至 **0.5s \- 1s**。 + +### **风险二:Excel 格式丢失 (The Format Loss)** + +* **逆向拷问:** Pandas 的 to\_excel 会重置所有单元格样式,医生标注的颜色和批注会丢失。 +* **V7 解决方案:** **底板分离策略 (Template Separation)** + 1. **上传时:** 将原始 Excel 标记为 **"Style Template" (样式底板)**,永久保存在 OSS。 + 2. **计算时:** 只在内存/Redis 中处理纯数据 (Values),不关心样式。 + 3. **导出时:** 使用 openpyxl 加载“样式底板”,将内存中的新数据**填入**到底板的对应坐标中,保留未修改区域的背景色和批注。 + +### **风险三:状态同步的“双写冲突”** + +* **逆向拷问:** 用户手动修改了第 5 行,同时 AI 删除了第 5 行,导致数据状态不一致。 +* **V7 解决方案:** **UI 互斥锁 (UI Locking)** + * 当 AI 正在生成代码或后端正在计算时,AG Grid 强制进入 **readOnly** 模式,并在界面显示 "AI 正在处理..." 遮罩,**物理层面上禁止并发操作**。 + +### **风险四:安全沙箱逃逸** + +* **逆向拷问:** AI 生成了 import os; os.system('rm \-rf /')。 +* **V7 解决方案:** **AST 静态分析 \+ 容器隔离** + * **预检:** 在执行 exec() 前,使用 Python ast 模块扫描代码树,检测到 import os 等关键词直接抛出异常。 + * **资源限额:** 使用 Python resource 模块限制单次执行的 CPU 时间 (10s) 和 内存 (1GB)。 + +## **5\. 数据库设计 (元数据层)** + +新增 TaskAudit 表用于记录每一次 AI 操作的上下文,便于回滚和审计。 + +model TaskAudit { + id String @id @default(uuid()) + datasetId String + version Int // 操作后的版本号 + + actionType String // "AI\_CODE" or "MANUAL\_EDIT" + prompt String? // 用户的自然语言指令 + code String? // AI 生成的 Python 代码 + + executionTime Int // 执行耗时 (ms) + status String // SUCCESS / FAILED + + createdAt DateTime @default(now()) +} + +## **6\. API 接口定义 (V7 优化)** + +* POST /api/session/init: 上传文件,初始化 Redis Session,返回 sessionId。 +* POST /api/session/execute: + * **Input:** { sessionId, code, version } + * **Output:** { previewData: ArrowBase64, newVersion: int, logs: string } + * **说明:** 仅返回预览数据,不生成 Excel 文件。 +* POST /api/session/save: + * **Input:** { sessionId } + * **Output:** { downloadUrl } + * **说明:** 触发 openpyxl 合并逻辑,生成最终 Excel 并上传 OSS。 + +## **7\. 开发分工建议** + +* **Python 组 (重中之重):** + * 实现 Arrow \<-\> Pandas 的序列化逻辑。 + * 封装 openpyxl 的样式回填逻辑。 + * 搭建 FastAPI \+ Redis 环境。 +* **Node.js 组:** + * 负责 Dify 转发和鉴权。 +* **前端组:** + * 集成 apache-arrow JS 库,解析后端返回的二进制流并在 AG Grid 展示。 + * 实现“AI 处理中”的全屏锁定交互。 \ No newline at end of file diff --git a/docs/03-业务模块/DC-数据清洗整理/03-UI设计/工具C_原型设计V6 .html b/docs/03-业务模块/DC-数据清洗整理/03-UI设计/工具C_原型设计V6 .html new file mode 100644 index 00000000..b559d742 --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/03-UI设计/工具C_原型设计V6 .html @@ -0,0 +1,479 @@ + + + + + + 工具C - 科研数据编辑器 V6 (修复版) + + + + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发_TODO清单.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发_TODO清单.md new file mode 100644 index 00000000..6c8c8fe7 --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发_TODO清单.md @@ -0,0 +1,620 @@ +# 工具C MVP开发 - To-do List + +> **文档版本**:v1.0 +> **创建日期**:2025-12-06 +> **预计工期**:3周(15个工作日) +> **参考文档**:[工具C_MVP开发计划_V1.0.md](./工具C_MVP开发计划_V1.0.md) + +--- + +## 📊 整体进度概览 + +| 阶段 | 任务数 | 已完成 | 进行中 | 待开始 | 完成率 | +|------|-------|-------|-------|-------|--------| +| **Week 1: 基础架构** | 12 | 0 | 0 | 12 | 0% | +| **Week 2: 核心功能** | 10 | 0 | 0 | 10 | 0% | +| **Week 3: 测试优化** | 8 | 0 | 0 | 8 | 0% | +| **总计** | **30** | **0** | **0** | **30** | **0%** | + +--- + +## 🎯 核心里程碑(必须完成) + +- [ ] **M1**:Python代码执行环境搭建完成(Day 1) +- [ ] **M2**:AI生成代码能力验证通过(Day 7) +- [ ] **M3**:基础场景成功率 > 90%(Day 12) +- [ ] **M4**:总体成功率 > 80%(Day 15) + +--- + +## 📅 Week 1:基础架构搭建(Day 1-5) + +### Day 1:Python服务扩展 + 环境验证 ⭐ + +#### Python微服务扩展 +- [ ] **P0** 创建 `extraction_service/services/dc_executor.py` + - [ ] 实现 `validate_code(code)` - AST静态检查 + - [ ] 实现 `execute_pandas_code(data, code)` - 代码执行 + - [ ] 添加危险模块黑名单(os、sys、subprocess等) + - [ ] 添加超时保护(30秒) + - [ ] 添加异常捕获和错误消息 + +- [ ] **P0** 扩展 `extraction_service/main.py` + - [ ] 添加 `POST /api/dc/execute` 端点 + - [ ] 添加 `POST /api/dc/validate` 端点 + - [ ] 添加请求日志记录 + - [ ] 添加错误处理中间件 + +- [ ] **P0** 测试Python服务 + - [ ] 启动服务:`cd extraction_service && venv\Scripts\activate && uvicorn main:app --reload` + - [ ] 测试健康检查:`GET http://localhost:8000/api/health` + - [ ] 测试代码验证:发送简单Pandas代码 + - [ ] 测试代码执行:发送真实数据+代码 + - [ ] 验证AST拦截:发送危险代码(如 `import os`) + +#### Node.js后端集成 +- [ ] **P0** 创建后端文件夹结构 + ``` + backend/src/modules/dc/tool-c/ + ├── services/ + │ ├── SessionService.ts # Session管理 + │ ├── AICodeService.ts # AI代码生成 + │ ├── PythonExecutorService.ts # Python执行(新增) + │ └── DataProcessService.ts # 数据处理 + ├── controllers/ + │ └── ToolCController.ts # HTTP控制器 + ├── routes/ + │ └── index.ts # 路由定义 + └── utils/ + └── codeValidator.ts # 前置验证 + ``` + +- [ ] **P0** 实现 `PythonExecutorService.ts` + - [ ] 实现 `executeCode(data, code)` 方法 + - [ ] 实现 `validateCode(code)` 方法 + - [ ] 添加超时控制(30秒) + - [ ] 添加日志记录(复用 `@/common/logging`) + - [ ] 添加错误处理和重试机制 + +- [ ] **P1** 环境变量配置 + - [ ] 添加 `EXTRACTION_SERVICE_URL=http://localhost:8000` 到 `.env` + - [ ] 验证环境变量加载 + +#### 验收标准 +- [ ] Python服务能成功执行简单Pandas代码(如 `df['age'] > 60`) +- [ ] AST检查能拦截危险代码(如 `import os`) +- [ ] Node.js能成功调用Python服务并获取结果 +- [ ] 所有日志正常输出到控制台 + +--- + +### Day 2:数据库 + Session管理 + +#### 数据库Schema设计 +- [ ] **P0** 创建Prisma Schema(`prisma/schema.prisma`) + ```prisma + // @@schema("dc") + model DcToolCSession { + id String @id @default(uuid()) + userId String + fileName String + fileKey String // OSS存储key + totalRows Int + totalCols Int + columns Json // 列名数组 + encoding String? // 编码格式 + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([userId]) + @@map("dc_tool_c_sessions") + } + ``` + +- [ ] **P0** 执行数据库迁移 + ```bash + npx prisma db push + npx prisma generate + ``` + +- [ ] **P1** 验证表创建成功 + ```sql + SELECT * FROM dc.dc_tool_c_sessions LIMIT 1; + ``` + +#### SessionService实现 +- [ ] **P0** 实现 `SessionService.ts` + - [ ] `createSession(userId, fileName, fileBuffer)` - 创建会话 + - [ ] 上传Excel到OSS(复用 `storage.uploadBuffer`) + - [ ] 解析Excel到JSON(使用xlsx库) + - [ ] 保存元数据到数据库(prisma) + - [ ] `getSession(sessionId)` - 获取会话 + - [ ] 从数据库读取元数据 + - [ ] 从OSS下载数据(如需要) + - [ ] `deleteSession(sessionId)` - 删除会话 + - [ ] 删除OSS文件 + - [ ] 删除数据库记录 + +- [ ] **P1** 添加Excel解析逻辑 + - [ ] 使用 `xlsx` 库读取Excel + - [ ] 转换为JSON格式(数组对象) + - [ ] 检测编码(中文支持) + - [ ] 提取列名和数据类型 + +#### 验收标准 +- [ ] 能成功上传10MB以内的Excel文件 +- [ ] 数据正确保存到OSS(零落盘) +- [ ] Session元数据正确存储到数据库 +- [ ] 能通过sessionId检索到完整数据 + +--- + +### Day 3:AI代码生成服务 + +#### AICodeService实现 +- [ ] **P0** 实现 `AICodeService.ts` + - [ ] 集成LLMFactory(复用 `@/common/llm`) + - [ ] 实现 `generateCode(prompt, dataContext)` 方法 + - [ ] 构建System Prompt(包含10个Few-shot示例) + - [ ] 注入数据上下文(行数、列名、样本数据) + - [ ] 调用LLM生成代码 + - [ ] 提取纯代码(去除Markdown格式) + - [ ] 实现 `fixCode(originalCode, errorMsg, dataContext)` 方法 + - [ ] AI自我修复逻辑 + - [ ] 最多重试1次 + +- [ ] **P0** System Prompt设计 + - [ ] 基础场景示例(5个) + - [ ] 中等场景示例(3个) + - [ ] 高级场景示例(2个) + - [ ] 安全规范说明 + - [ ] 输出格式要求 + +- [ ] **P1** 代码提取逻辑 + - [ ] 识别 ` ```python ... ``` ` 格式 + - [ ] 识别纯代码格式 + - [ ] 去除注释和说明 + +#### 验收标准 +- [ ] AI能生成正确的Pandas代码(基础场景) +- [ ] 生成的代码符合安全规范(无危险导入) +- [ ] 能正确处理中文列名 +- [ ] 代码提取准确率 > 95% + +--- + +### Day 4:前端基础框架 + +#### 前端文件夹结构 +- [ ] **P0** 创建前端目录 + ``` + frontend-v2/src/modules/dc/pages/tool-c/ + ├── index.tsx # 主页面入口 + ├── components/ + │ ├── DataTable.tsx # AG Grid数据表格 + │ ├── AICopilot.tsx # AI对话侧边栏 + │ ├── FileUploader.tsx # 文件上传 + │ ├── Toolbar.tsx # 顶部工具栏 + │ └── ChatMessage.tsx # 对话消息组件 + ├── hooks/ + │ ├── useSession.ts # Session管理 + │ └── useAIChat.ts # AI对话 + ├── types.ts # TypeScript类型定义 + └── api.ts # API封装 + ``` + +- [ ] **P0** 安装依赖 + ```bash + cd frontend-v2 + npm install ag-grid-react ag-grid-community xlsx + ``` + +#### 主页面布局 +- [ ] **P0** 实现 `index.tsx`(主布局) + - [ ] 左侧:数据表格区域(70%宽度) + - [ ] 右侧:AI Copilot侧边栏(30%宽度) + - [ ] 顶部:扁平工具栏(文件上传、导出、撤销) + - [ ] 状态管理:useState/useReducer + +- [ ] **P1** 实现 `FileUploader.tsx` + - [ ] 拖拽上传支持 + - [ ] 文件类型验证(仅Excel) + - [ ] 文件大小限制(10MB) + - [ ] 上传进度显示 + +#### 验收标准 +- [ ] 页面布局正确(左表格右AI) +- [ ] 能成功上传Excel文件 +- [ ] 上传后能看到加载状态 +- [ ] 响应式布局(最小宽度1280px) + +--- + +### Day 5:数据表格实现(AG Grid) + +#### DataTable组件 +- [ ] **P0** 实现 `DataTable.tsx` + - [ ] 集成AG Grid + - [ ] 动态列定义(根据Excel自动生成) + - [ ] 单元格编辑功能 + - [ ] 脏数据标记(黄色高亮) + - [ ] 分页支持(每页100行) + +- [ ] **P1** 配置AG Grid主题 + - [ ] 使用 `ag-theme-alpine` + - [ ] 自定义样式(Ant Design风格) + - [ ] 列宽自适应 + +- [ ] **P1** 表格功能 + - [ ] 列排序 + - [ ] 列筛选 + - [ ] 行选择(多选) + - [ ] 导出CSV(AG Grid内置) + +#### 验收标准 +- [ ] 能正确显示Excel数据(100行+) +- [ ] 列宽自适应且可手动调整 +- [ ] 单元格编辑后有黄色标记 +- [ ] 表格性能流畅(1000行不卡顿) + +--- + +## 📅 Week 2:核心功能实现(Day 6-10) + +### Day 6:AI对话UI + +#### AICopilot组件 +- [ ] **P0** 实现 `AICopilot.tsx` + - [ ] 对话消息列表(滚动) + - [ ] 输入框(多行,支持Enter发送) + - [ ] 发送按钮 + - [ ] 加载状态(AI思考中...) + +- [ ] **P0** 实现 `ChatMessage.tsx` + - [ ] 用户消息(右对齐,蓝色) + - [ ] AI消息(左对齐,灰色) + - [ ] 代码块高亮(使用 `react-syntax-highlighter`) + - [ ] 时间戳显示 + +- [ ] **P1** 消息历史管理 + - [ ] 保存到localStorage + - [ ] 最多保存50条 + - [ ] 清空历史按钮 + +#### 验收标准 +- [ ] 对话界面美观(参考原型设计) +- [ ] 消息发送/接收流畅 +- [ ] 代码块正确高亮 +- [ ] 滚动到最新消息 + +--- + +### Day 7:AI生成代码集成 ⭐ + +#### API集成 +- [ ] **P0** 实现 `api.ts` + - [ ] `uploadFile(file)` - 上传Excel + - [ ] `sendMessage(sessionId, message)` - 发送AI消息 + - [ ] `executeCode(sessionId, code)` - 执行代码 + - [ ] `getSessionData(sessionId)` - 获取数据 + +- [ ] **P0** 后端API实现(`ToolCController.ts`) + - [ ] `POST /api/v1/dc/tool-c/upload` - 文件上传 + - [ ] `POST /api/v1/dc/tool-c/chat` - AI对话 + - [ ] `POST /api/v1/dc/tool-c/execute` - 执行代码 + - [ ] `GET /api/v1/dc/tool-c/sessions/:id` - 获取会话 + +#### 业务逻辑实现 +- [ ] **P0** 实现完整流程 + 1. [ ] 用户发送消息 → AICodeService生成代码 + 2. [ ] 前端展示生成的代码(Markdown格式) + 3. [ ] 用户点击"执行"按钮 → 调用Python服务 + 4. [ ] 执行成功 → 刷新表格数据 + 5. [ ] 执行失败 → AI自我修复 → 重试 + +- [ ] **P1** 错误处理 + - [ ] AST检查失败 → 提示用户 + - [ ] 执行超时(30秒) → 提示用户 + - [ ] AI生成失败 → 重试机制 + +#### 验收标准 +- [ ] **基础场景测试(5个)成功率 > 90%** + - [ ] "把年龄大于60的标记为老年组" + - [ ] "删除所有患者ID为空的行" + - [ ] "把性别转为数字,男1女0" + - [ ] "计算BMI = 体重 / (身高/100)^2" + - [ ] "删除缺失率超过50%的列" + +--- + +### Day 8:UI锁定机制 + +#### 互斥锁实现 +- [ ] **P0** 前端状态管理 + - [ ] 添加 `isAIProcessing` 状态 + - [ ] AI对话中 → 锁定表格编辑 + - [ ] 显示友好提示:"AI正在处理,请稍候..." + +- [ ] **P0** 表格锁定逻辑 + - [ ] `isAIProcessing=true` → AG Grid设置为只读 + - [ ] 禁用工具栏按钮(导出除外) + - [ ] 显示半透明蒙层 + +- [ ] **P1** 视觉反馈 + - [ ] 表格半透明(opacity: 0.6) + - [ ] 显示加载动画 + - [ ] 顶部显示进度条 + +#### 验收标准 +- [ ] AI处理时,表格无法编辑 +- [ ] 锁定状态有明显的视觉反馈 +- [ ] AI完成后,表格自动解锁 +- [ ] 用户体验流畅(无卡顿) + +--- + +### Day 9:自动检查点(Checkpoint) + +#### 数据快照管理 +- [ ] **P0** 实现检查点逻辑 + - [ ] 每次AI执行成功 → 自动保存快照 + - [ ] 最多保存10个检查点 + - [ ] 快照数据存储到OSS(压缩JSON) + +- [ ] **P0** 回滚功能 + - [ ] 工具栏添加"撤销"按钮 + - [ ] 点击撤销 → 恢复到上一个检查点 + - [ ] 最多支持10次撤销 + +- [ ] **P1** 检查点列表UI + - [ ] 侧边栏显示检查点列表 + - [ ] 每个检查点显示:时间、操作描述 + - [ ] 点击检查点 → 恢复到该状态 + +#### 验收标准 +- [ ] 每次AI操作后自动保存检查点 +- [ ] 撤销功能正常工作 +- [ ] 快照数据正确存储到OSS +- [ ] 10个检查点后,自动删除最旧的 + +--- + +### Day 10:Excel导出 + +#### 导出功能实现 +- [ ] **P0** 后端导出API + - [ ] `POST /api/v1/dc/tool-c/export/:sessionId` + - [ ] 使用 `openpyxl`(Python)或 `xlsx`(Node.js) + - [ ] 保留原始Excel格式(可选,MVP可跳过) + +- [ ] **P0** 前端导出按钮 + - [ ] 工具栏添加"导出"按钮 + - [ ] 点击 → 下载Excel文件 + - [ ] 文件名:`原文件名_cleaned_YYYYMMDD.xlsx` + +- [ ] **P1** 导出选项(可选) + - [ ] 仅导出修改的行 + - [ ] 保留样式(复杂,可延后) + +#### 验收标准 +- [ ] 能成功导出Excel文件 +- [ ] 导出的数据与表格一致 +- [ ] 文件名正确 +- [ ] 下载速度快(< 3秒) + +--- + +## 📅 Week 3:测试优化(Day 11-15) + +### Day 11:中等场景测试 🟡 + +#### 测试用例执行 +- [ ] **P0** 准备测试数据 + - [ ] 创建测试Excel文件(包含多个列) + - [ ] 包含真实医疗数据示例 + +- [ ] **P0** 中等场景测试(5个) + 1. [ ] "把诊断日期和出院日期计算天数差,如果出院日期早于诊断日期则标记为异常" + 2. [ ] "根据白细胞、中性粒细胞、淋巴细胞三个指标,计算NLR,并按2.5分为高低两组" + 3. [ ] "从病理报告列中提取TNM分期,生成新列" + 4. [ ] "把血压列的'120/80'格式拆分成收缩压和舒张压,并判断是否高血压" + 5. [ ] "删除重复的患者ID,保留最新的一条记录(根据就诊日期)" + +#### 优化AI Prompt +- [ ] **P1** 根据失败案例优化Prompt + - [ ] 增加错误处理示例 + - [ ] 强化中文列名处理 + - [ ] 增加边界情况说明 + +#### 验收标准 +- [ ] **中等场景成功率 > 80%(4/5成功)** + +--- + +### Day 12:高级场景测试 🔴 + +#### 高级场景测试(5个) +- [ ] **P0** 复杂场景测试 + 1. [ ] "对于每个患者,找出第一次化疗日期和最后一次化疗日期,计算化疗持续时间" + 2. [ ] "生成生存状态和生存时间"(复杂条件逻辑) + 3. [ ] "根据ALT、AST、ALP、TBIL判断肝功能分级" + 4. [ ] "按患者ID分组,计算每次随访相比上次的肿瘤大小变化率" + 5. [ ] "根据入院时间,计算季节变量,统计不同季节的发病人数" + +#### Prompt深度优化 +- [ ] **P1** 针对失败场景优化 + - [ ] 增加分组聚合示例 + - [ ] 增加时间序列示例 + - [ ] 增加医学规则示例 + +#### 验收标准 +- [ ] **高级场景成功率 > 60%(3/5成功)** +- [ ] **总体成功率 > 80%(12/15成功)** + +--- + +### Day 13:性能优化 + +#### 性能测试 +- [ ] **P1** 测试性能指标 + - [ ] 文件上传速度(10MB文件 < 5秒) + - [ ] AI代码生成速度(< 10秒) + - [ ] Python执行速度(< 5秒) + - [ ] 表格刷新速度(< 2秒) + - [ ] 端到端流程(< 20秒) + +#### 优化措施 +- [ ] **P1** 前端优化 + - [ ] AG Grid虚拟滚动 + - [ ] React.memo优化渲染 + - [ ] 防抖输入(debounce 300ms) + +- [ ] **P1** 后端优化 + - [ ] 数据压缩(OSS上传前) + - [ ] 缓存会话数据(Redis可选) + - [ ] 并发控制(限制同时执行数) + +#### 验收标准 +- [ ] 端到端操作 < 20秒 +- [ ] 1000行数据表格不卡顿 +- [ ] 无内存泄漏 + +--- + +### Day 14:集成测试 + +#### 端到端测试 +- [ ] **P0** 完整流程测试(10次) + 1. [ ] 上传Excel文件 + 2. [ ] 查看数据表格 + 3. [ ] 发送AI指令 + 4. [ ] 执行生成的代码 + 5. [ ] 验证表格刷新 + 6. [ ] 测试撤销功能 + 7. [ ] 导出Excel文件 + 8. [ ] 验证导出数据正确 + +- [ ] **P1** 异常场景测试 + - [ ] 上传损坏的Excel文件 + - [ ] 发送空消息 + - [ ] 执行危险代码(应被拦截) + - [ ] 网络断开恢复 + +#### Bug修复 +- [ ] **P1** 修复测试中发现的问题 + - [ ] 记录所有bug到GitHub Issues + - [ ] 按优先级修复(P0 > P1 > P2) + +#### 验收标准 +- [ ] 端到端流程100%通过 +- [ ] 无P0级别bug +- [ ] P1级别bug < 3个 + +--- + +### Day 15:MVP验收 🎉 + +#### 最终验收测试 +- [ ] **P0** 15个场景全覆盖测试 + - [ ] 基础场景:5/5 ✅ + - [ ] 中等场景:4/5 ✅ + - [ ] 高级场景:3/5 ✅ + - [ ] **总体成功率 > 80%** + +- [ ] **P0** 非功能性验收 + - [ ] 性能:端到端 < 20秒 ✅ + - [ ] 安全:AST拦截危险代码 ✅ + - [ ] 稳定:10次测试无崩溃 ✅ + - [ ] 易用:用户能独立完成任务 ✅ + +#### 文档完善 +- [ ] **P1** 更新文档 + - [ ] 用户使用手册 + - [ ] API接口文档 + - [ ] 部署指南 + - [ ] 已知问题清单 + +#### 验收决策 +- [ ] ✅ **通过**:总体成功率 > 80%,进入下一阶段 +- [ ] ❌ **失败**:总体成功率 < 60%,需要Pivot到模板库模式 + +--- + +## 🔥 每日站会检查清单 + +### 每天工作开始前 +- [ ] 查看昨日遗留问题 +- [ ] 确认今日任务清单 +- [ ] 检查环境是否正常(Python服务、数据库、前端dev server) + +### 每天工作结束前 +- [ ] 更新To-do List进度 +- [ ] 提交代码到Git(✅ 重要!防止丢失) +- [ ] 记录遇到的问题和解决方案 +- [ ] 规划明日任务 + +--- + +## ⚠️ 风险提示 + +### 技术风险 +1. **AI生成质量不稳定** + - 缓解:多轮测试,优化Prompt + - 备选:失败后人工编写模板 + +2. **Python执行安全问题** + - 缓解:AST静态检查 + 超时控制 + - 备选:Docker沙箱隔离(Phase 2) + +3. **性能不达标** + - 缓解:分步优化,设定性能基准 + - 备选:降低数据量要求(10MB → 5MB) + +### 进度风险 +1. **AI生成成功率 < 60%** + - 应对:紧急会议,决定是否Pivot + +2. **前端开发延期** + - 应对:简化UI,聚焦核心功能 + +--- + +## 📊 成功标准(最终验收) + +| 指标 | 目标 | 当前 | 状态 | +|------|------|------|------| +| **基础场景成功率** | > 90% | 0% | 🔴 | +| **中等场景成功率** | > 80% | 0% | 🔴 | +| **高级场景成功率** | > 60% | 0% | 🔴 | +| **总体成功率** | > 80% | 0% | 🔴 | +| **端到端性能** | < 20秒 | 0秒 | 🔴 | +| **代码安全性** | 100%拦截 | 0% | 🔴 | + +--- + +## 🎯 下一步行动(启动开发) + +### 立即开始(Day 1) +1. **扩展Python服务**(2小时) + ```bash + cd AIclinicalresearch/extraction_service + # 创建dc_executor.py + # 扩展main.py添加2个端点 + # 测试服务 + ``` + +2. **创建后端文件夹**(1小时) + ```bash + cd AIclinicalresearch/backend/src/modules/dc + mkdir -p tool-c/services tool-c/controllers tool-c/routes tool-c/utils + ``` + +3. **实现PythonExecutorService**(2小时) + - 编写代码 + - 单元测试 + - 集成测试 + +--- + +**准备好了吗?让我们开始Day 1的开发!** 🚀 + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md new file mode 100644 index 00000000..bd63e2f5 --- /dev/null +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md @@ -0,0 +1,2061 @@ +# 工具C - 科研数据编辑器 MVP开发计划 + +> **文档版本:** V1.0 (务实快速验证版) +> **创建日期:** 2025-12-06 +> **计划周期:** 3周(15个工作日) +> **核心策略:** 用最小成本验证核心假设,快速失败优于完美计划 +> **状态:** 待启动 + +--- + +## ⚠️ 开发前必读:严格遵守现有架构与规范 + +### 🔴 不要重复造轮子!复用平台能力 + +**本项目已有完整的3层架构体系和平台基础设施,所有代码必须复用现有能力:** + +#### 平台基础层(✅ 已完成,直接使用) + +| 服务 | 导入方式 | 用途 | 文档 | +|------|---------|------|------| +| **存储服务** | `import { storage } from '@/common/storage'` | 文件上传下载 | ✅ 必须使用 | +| **日志系统** | `import { logger } from '@/common/logging'` | 标准化日志 | ✅ 必须使用 | +| **缓存服务** | `import { cache } from '@/common/cache'` | 分布式缓存 | ✅ 必须使用 | +| **异步任务** | `import { jobQueue } from '@/common/jobs'` | 长时间任务 | ✅ 必须使用 | +| **数据库** | `import { prisma } from '@/config/database'` | 数据库操作 | ✅ 必须使用 | +| **LLM能力** | `import { LLMFactory } from '@/common/llm'` | LLM调用 | ✅ 必须使用 | + +#### 云原生开发规范(✅ 强制执行) + +**详细文档**:`docs/04-开发规范/08-云原生开发规范.md` + +**核心要求:** +- ✅ **文件存储**:使用`storage.upload()`,不要用`fs.writeFile()` +- ✅ **Session管理**:存数据库,不要用`Map` +- ✅ **日志输出**:使用`logger.info()`,不要用`console.log()` +- ✅ **数据库连接**:使用全局`prisma`实例,不要`new PrismaClient()` +- ✅ **LLM调用**:使用`LLMFactory.getLLM()`,不要自己集成 +- ❌ **禁止本地文件存储**:Excel直接从内存解析 +- ❌ **禁止内存缓存Map**:用数据库或cache服务 +- ❌ **禁止硬编码配置**:使用环境变量 + +#### 代码文件夹结构(✅ 参考tool-b) + +**后端**: +``` +backend/src/modules/dc/tool-c/ ← 已存在 +├── services/ ← 业务逻辑 +├── controllers/ ← HTTP控制器 +├── routes/ ← 路由定义 +└── utils/ ← 工具函数 +``` + +**前端**: +``` +frontend-v2/src/modules/dc/pages/tool-c/ ← 已存在 +``` + +#### 常见错误示例(❌ 严禁) + +```typescript +// ❌ 错误1:自己实现Session管理 +const sessions = new Map(); // 违反规范! + +// ❌ 错误2:本地文件存储 +fs.writeFileSync('./uploads/file.xlsx', buffer); // 违反规范! + +// ❌ 错误3:不用logger +console.log('User uploaded file'); // 违反规范! + +// ❌ 错误4:新建Prisma实例 +const prisma = new PrismaClient(); // 违反规范! + +// ❌ 错误5:自己集成LLM +import Anthropic from '@anthropic-ai/sdk'; // 违反规范! +``` + +**正确示例(✅ 必须)**:参考本文档Day 1-5的代码示例 + +--- + +## 📋 目录 + +- [开发前必读](#开发前必读严格遵守现有架构与规范) +- [一、MVP核心目标](#一mvp核心目标) +- [二、技术架构方案(务实版)](#二技术架构方案务实版) +- [三、功能优先级矩阵](#三功能优先级矩阵) +- [四、3周详细开发计划](#四3周详细开发计划) +- [五、风险应对策略](#五风险应对策略) +- [六、验收标准](#六验收标准) +- [七、快速失败机制](#七快速失败机制) + +--- + +## 一、MVP核心目标 + +### 1.1 核心假设验证 + +**我们需要验证的3个核心假设:** + +| 假设 | 验证方式 | 成功标准 | 失败后果 | +|------|---------|---------|---------| +| **H1: AI能生成高质量Pandas代码并成功执行** | 15个真实场景测试 | 总体成功率 > 80% | MVP失败,改用代码模板库 | +| **H2: Python代码执行环境稳定可靠** | 复杂场景测试 | 高级场景成功率 > 60% | 简化为批处理模式 | +| **H3: 左表格+右AI的交互模式好用** | 用户体验测试 | 用户能独立完成任务 | 重新设计UI交互 | +| **H4: 性能可接受(含Python执行)** | 性能测试 | 端到端 < 20秒 | 优化Python执行或改批处理 | + +**⚠️ 核心价值主张:** +- ✅ **AI生成代码 + 真实执行 + 表格刷新**是工具C的差异化价值 +- ✅ 如果只生成代码不执行,用户还不如直接用ChatGPT +- ✅ Python执行环境是MVP的**技术核心**,不是可选项 + +### 1.2 MVP功能范围 + +**✅ MVP必须有的(P0):** +1. 文件上传(10MB限制) +2. 表格展示(AG Grid,100行预览) +3. AI对话界面(右侧侧边栏) +4. AI代码生成(DeepSeek-V3) +5. 代码执行(Python沙箱) +6. UI锁定机制(AI处理时表格只读) +7. AST安全检查 +8. 表格自动刷新 +9. 导出Excel + +**❌ MVP明确不做的:** +- 手动编辑单元格(专注AI能力) +- 撤销/回滚(节省内存) +- Apache Arrow(先用JSON) +- Redis会话(用进程内存) +- 样式保留(直接覆盖) +- 操作审计日志 +- 协同编辑 + +### 1.3 MVP交付物 + +**最终演示场景:** +``` +用户上传 "lung_cancer_patients.xlsx" (5000行 × 20列) + ↓ +左侧显示数据表格,右侧AI助手准备就绪 + ↓ +用户对AI说:"把年龄大于60的患者标记为老年组" + ↓ +AI生成代码 → 展示预操作卡片 → 用户确认 → 执行成功 + ↓ +表格自动刷新,新增"age_group"列 + ↓ +用户继续说:"删除所有缺失患者ID的行" + ↓ +AI执行 → 表格刷新,显示删除了23行 + ↓ +用户点击"导出",下载处理后的Excel +``` + +**成功标准:** +- ✅ 整个流程 < 2分钟完成 +- ✅ AI代码一次性执行成功 +- ✅ 用户无需看文档就能操作 + +--- + +## 二、技术架构方案(务实版) + +### 2.1 总体架构(简化版)- ⚠️ 重要:复用平台能力 + +``` +┌─────────────────────────────────────────────────────────┐ +│ 前端 (React) │ +│ frontend-v2/src/modules/dc/pages/tool-c/ │ +│ ┌──────────────────────┐ ┌────────────────────────┐ │ +│ │ AG Grid (70%) │ │ AI Chat Sidebar (30%) │ │ +│ │ • 展示100行数据 │ │ • 自然语言输入 │ │ +│ │ • 只读模式切换 │ │ • 预操作卡片 │ │ +│ │ • 锁定遮罩 │ │ • 消息历史 │ │ +│ └──────────────────────┘ └────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + ↕ REST API (JSON) +┌─────────────────────────────────────────────────────────┐ +│ Node.js后端 (backend/src/modules/dc/tool-c/) │ +│ • 文件上传(复用storage服务)✅ │ +│ • 会话管理(存数据库,不用内存Map)✅ │ +│ • LLM集成(复用LLMFactory)✅ │ +│ • 日志记录(复用logger)✅ │ +│ • 异步任务(复用jobQueue)✅ │ +└─────────────────────────────────────────────────────────┘ + ↕ HTTP(如需Python执行) +┌─────────────────────────────────────────────────────────┐ +│ Python微服务(可选,扩展文档处理引擎) │ +│ • DataFrame管理(存数据库,不用内存) │ +│ • exec()代码执行 │ +│ • AST静态安全检查 │ +│ • JSON序列化返回 │ +└─────────────────────────────────────────────────────────┘ +``` + +**⚠️ 云原生架构强制要求:** +- ✅ **复用storage服务**:`import { storage } from '@/common/storage'` +- ✅ **复用logger服务**:`import { logger } from '@/common/logging'` +- ✅ **复用cache服务**:`import { cache } from '@/common/cache'` +- ✅ **复用LLMFactory**:`import { LLMFactory } from '@/common/llm'` +- ✅ **复用prisma实例**:`import { prisma } from '@/config/database'` +- ✅ **Session存数据库**:不用内存Map(违反规范) +- ❌ **禁止本地文件存储**:Excel直接从内存解析 +- ❌ **禁止新建Prisma实例**:使用全局实例 + +**关键决策:** +- ✅ **不用Apache Arrow**:JSON够快(100行 ≈ 20KB) +- ✅ **Session存数据库**:符合云原生规范,支持多实例 +- ✅ **不做撤销**:节省开发时间,用户重新上传即可 +- ✅ **不保留样式**:直接生成新Excel + +### 2.2 技术栈选型(✅ 复用现有技术栈) + +#### 前端(frontend-v2) +```json +{ + "framework": "React 19 + TypeScript 5 (已有)", + "table": "AG Grid Community (免费版)", + "ui": "Ant Design 5 (已有)", + "state": "React Query v5 (已有)", + "http": "axios (已有)", + "routing": "React Router DOM v6 (已有)" +} +``` + +#### Node.js后端(backend) +```json +{ + "framework": "Fastify v4 (已有)", + "llm": "✅ 复用 LLMFactory (平台通用能力层)", + "storage": "✅ 复用 storage服务 (平台基础层)", + "logging": "✅ 复用 logger服务 (平台基础层)", + "cache": "✅ 复用 cache服务 (平台基础层)", + "database": "✅ 复用 prisma全局实例 (平台基础层)", + "session": "✅ PostgreSQL (dc_tool_c_sessions表)", + "excel": "xlsx 库(内存解析)", + "validation": "Joi" +} +``` + +#### Python微服务(⚠️ 核心功能,扩展现有服务)⭐⭐⭐⭐⭐ +**决策**:✅ **系统已有Python微服务(FastAPI),需扩展代码执行功能** + +**📦 现有Python服务(已完成):** +- ✅ **extraction_service**:FastAPI + PyMuPDF + Pandas + openpyxl +- ✅ **端口**:8000(已运行) +- ✅ **功能**:PDF/Docx/Txt文档提取、语言检测 +- ✅ **集成**:Node.js通过ExtractionClient调用 +- ✅ **依赖**:Pandas、openpyxl、chardet、langdetect(已安装) + +**🔧 工具C需要的新功能:** +| 功能 | 现有服务 | 需求 | 方案 | +|------|---------|------|------| +| **Pandas代码执行** | ❌ 不支持 | ✅ 核心 | **新增API端点** `/api/dc/execute` | +| **Excel上传** | ❌ 不支持 | ✅ 需要 | 复用MultiPart上传 | +| **DataFrame管理** | ❌ 不支持 | ✅ 会话 | 新增Session管理 | +| **AST代码检查** | ❌ 不支持 | ✅ 必须 | 新增AST模块 | +| **Excel导出** | ❌ 不支持 | ✅ 需要 | 使用openpyxl(已安装) | + +**⚠️ 不需要重复开发:** +- ❌ 不需要新建Python项目 +- ❌ 不需要重新安装Pandas/openpyxl(已安装) +- ❌ 不需要重新配置FastAPI(已运行) +- ❌ 不需要重新写Node.js调用逻辑(ExtractionClient已存在) + +**✅ MVP扩展方案:** +```python +# extraction_service/services/dc_executor.py(新增) +import pandas as pd +import ast +from typing import Dict, Any + +def validate_code(code: str) -> Dict[str, Any]: + """AST静态检查(安全验证)""" + try: + tree = ast.parse(code) + # 禁止import os、sys、subprocess等 + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name in ['os', 'sys', 'subprocess', 'socket']: + return {'valid': False, 'error': f'禁止导入{alias.name}'} + return {'valid': True} + except Exception as e: + return {'valid': False, 'error': str(e)} + +def execute_pandas_code(data: list, code: str) -> Dict[str, Any]: + """执行Pandas代码(⭐核心功能)""" + try: + # 1. 安全检查 + validation = validate_code(code) + if not validation['valid']: + return {'success': False, 'error': validation['error']} + + # 2. 加载数据 + df = pd.DataFrame(data) + + # 3. 执行代码(注入df和pd) + exec(code, {'df': df, 'pd': pd}) + + # 4. 返回结果(前100行) + return { + 'success': True, + 'data': df.head(100).to_dict('records'), + 'totalRows': len(df), + 'totalCols': len(df.columns), + 'columns': df.columns.tolist() + } + except Exception as e: + return {'success': False, 'error': str(e)} +``` + +```python +# extraction_service/main.py(扩展) +from services.dc_executor import execute_pandas_code, validate_code + +@app.post("/api/dc/execute") +async def execute_code( + data: List[Dict], + code: str +): + """工具C:执行Pandas代码""" + result = execute_pandas_code(data, code) + return JSONResponse(result) + +@app.post("/api/dc/validate") +async def validate_code_endpoint(code: str): + """工具C:AST代码检查""" + result = validate_code(code) + return JSONResponse(result) +``` + +**✅ Node.js调用(复用ExtractionClient模式):** +```typescript +// backend/src/modules/dc/tool-c/services/PythonExecutorService.ts +import axios from 'axios'; + +const EXTRACTION_SERVICE_URL = process.env.EXTRACTION_SERVICE_URL || 'http://localhost:8000'; + +export class PythonExecutorService { + async executeCode(data: any[], code: string) { + const response = await axios.post(`${EXTRACTION_SERVICE_URL}/api/dc/execute`, { + data, + code + }); + return response.data; + } +} +``` + +### 2.3 数据流设计 + +**会话生命周期:** +```python +# 1. 用户上传文件 +POST /api/tool-c/session/init +{ + file: File, + userId: string +} + +# Node.js: 转发到Python +# Python: +# - 读取Excel → DataFrame +# - 存入内存:sessions[sessionId] = df +# - 返回:sessionId + 数据概览 + +# 2. 用户发送AI指令 +POST /api/tool-c/ai/execute +{ + sessionId: string, + prompt: string +} + +# Node.js: +# - 构建System Prompt(包含数据上下文) +# - 调用LLM生成代码 +# - 转发代码到Python + +# Python: +# - AST检查 +# - exec(code)修改df +# - 返回前100行JSON + +# 3. 用户导出 +GET /api/tool-c/session/export/{sessionId} + +# Python: +# - df.to_excel(buffer) +# - 返回二进制流 +``` + +--- + +## 三、功能优先级矩阵 + +### 3.1 P0级:必须开发(核心闭环)⭐⭐⭐⭐⭐ + +| ID | 功能 | 描述 | 验证目标 | 工时 | 负责人 | +|----|------|------|---------|------|--------| +| **P0-001** | 文件上传 | 前端Upload组件 + 10MB限制 | 能否解析Excel | 0.5天 | 前端 | +| **P0-002** | Session初始化 | 后端接收文件,Python加载DataFrame | 中文列名、GBK编码 | 1天 | 后端 | +| **P0-003** | 表格展示 | AG Grid展示100行数据 | 列类型识别、空值高亮 | 1天 | 前端 | +| **P0-004** | AI对话UI | 右侧侧边栏,消息列表 | 聊天交互流畅 | 0.5天 | 前端 | +| **P0-005** | System Prompt构建 | 包含数据上下文的Prompt | AI能理解数据结构 | 1天 | 后端 | +| **P0-006** | AI代码生成 | 调用DeepSeek-V3生成Pandas代码 | **成功率>80%** | 2天 | 后端 | +| **P0-007** | AST安全检查 | 拦截危险代码 | `import os`被拦截 | 1天 | Python | +| **P0-008** | 代码执行 | exec()在沙箱中运行代码 | 修改DataFrame成功 | 1天 | Python | +| **P0-009** | 预操作卡片 | 展示代码,用户确认后执行 | 用户能看懂代码意图 | 0.5天 | 前端 | +| **P0-010** | 表格刷新 | 执行后自动拉取新数据 | 前端实时更新 | 0.5天 | 前端 | +| **P0-011** | UI锁定机制 | AI处理时表格变灰+遮罩 | 物理禁止并发操作 | 0.5天 | 前端 | +| **P0-012** | 导出Excel | 下载处理后的文件 | 文件完整性 | 1天 | Python | + +**P0小计:11.5天** + +### 3.2 P1级:必须验证,可简化实现 ⭐⭐⭐⭐ + +| ID | 功能 | MVP简化方案 | 完整版 | 工时 | +|----|------|------------|--------|------| +| **P1-001** | 会话管理 | 进程内存Map(单实例) | Redis分布式 | 0.5天 | +| **P1-002** | 心跳保活 | 固定10分钟过期,不续期 | 前端心跳续期 | 0.5天 | +| **P1-003** | 编码检测 | chardet自动检测+友好报错 | 自动转换 | 1天 | +| **P1-004** | AI自我修复 | 失败后重试1次 | 多次重试+学习 | 1天 | +| **P1-005** | 快捷模板 | 3个常用模板(年龄分组等) | 10+模板库 | 0.5天 | + +**P1小计:3.5天** + +### 3.3 P2级:延后或不做 ⭐⭐ + +| 功能 | 延后原因 | 何时做 | +|------|---------|--------| +| 手动编辑单元格 | MVP专注AI | P2阶段 | +| 撤销/回滚 | 节省内存 | P2阶段 | +| Apache Arrow | JSON够用 | 性能不达标时 | +| Redis分布式 | 单实例够用 | 横向扩展时 | +| 样式保留 | 复杂度高 | P3阶段 | +| 操作审计 | 非核心 | P3阶段 | +| 多Sheet支持 | 简化逻辑 | P3阶段 | + +--- + +## 四、3周详细开发计划 + +### Week 1:基础架构搭建(5天) + +#### Day 1:环境搭建 + 代码结构(⚠️ 参考tool-b结构 + Python环境) + +**任务清单:** +- [ ] 创建项目目录结构(**参考tool-b**) + ``` + backend/src/modules/dc/tool-c/ ← 主目录 + ├── services/ ← 业务逻辑层 + │ ├── SessionService.ts ← Session管理(存数据库) + │ ├── AICodeService.ts ← AI代码生成 + │ ├── PythonExecutorService.ts ← ⭐ Python代码执行(核心) + │ └── DataProcessService.ts ← 数据处理逻辑 + ├── controllers/ ← 控制器层 + │ └── ToolCController.ts ← HTTP请求处理 + ├── routes/ ← 路由层 + │ └── index.ts ← 路由定义 + └── utils/ ← 工具函数 + ├── codeValidator.ts ← AST代码检查 + └── pythonScripts/ ← ⭐ Python执行脚本 + └── executor.py ← Pandas代码执行器 + + frontend-v2/src/modules/dc/pages/tool-c/ ← 前端(已存在) + ``` + +- [ ] **⭐ 扩展现有Python服务(核心功能)** + ```bash + # 系统已有Python微服务(extraction_service),只需扩展功能 + cd extraction_service + + # 1. 创建DC执行器模块 + cat > services/dc_executor.py << 'EOF' + import pandas as pd + import ast + from typing import Dict, Any, List + + def validate_code(code: str) -> Dict[str, Any]: + """AST静态检查(安全验证)""" + try: + tree = ast.parse(code) + # 禁止危险导入 + forbidden_modules = ['os', 'sys', 'subprocess', 'socket', 'shutil', 'requests'] + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name in forbidden_modules: + return {'valid': False, 'error': f'禁止导入{alias.name}模块'} + elif isinstance(node, ast.ImportFrom): + if node.module in forbidden_modules: + return {'valid': False, 'error': f'禁止导入{node.module}模块'} + return {'valid': True} + except Exception as e: + return {'valid': False, 'error': f'代码语法错误: {str(e)}'} + + def execute_pandas_code(data: List[Dict], code: str) -> Dict[str, Any]: + """执行Pandas代码(⭐核心功能)""" + try: + # 1. 安全检查 + validation = validate_code(code) + if not validation['valid']: + return {'success': False, 'error': validation['error']} + + # 2. 加载数据到DataFrame + df = pd.DataFrame(data) + + # 3. 执行代码(注入df和pd,隔离环境) + local_env = {'df': df, 'pd': pd} + exec(code, {'__builtins__': {}}, local_env) + + # 4. 获取执行后的df + df_result = local_env.get('df', df) + + # 5. 返回结果(前100行,避免数据量过大) + return { + 'success': True, + 'data': df_result.head(100).to_dict('records'), + 'totalRows': len(df_result), + 'totalCols': len(df_result.columns), + 'columns': df_result.columns.tolist() + } + except Exception as e: + return { + 'success': False, + 'error': f'代码执行失败: {str(e)}' + } + EOF + + # 2. 扩展main.py,添加DC端点 + # 在main.py末尾添加: + cat >> main.py << 'EOF' + + # ==================== DC工具C端点 ==================== + from services.dc_executor import execute_pandas_code, validate_code + from pydantic import BaseModel + + class ExecuteRequest(BaseModel): + data: List[Dict] + code: str + + class ValidateRequest(BaseModel): + code: str + + @app.post("/api/dc/execute") + async def dc_execute_code(request: ExecuteRequest): + """工具C:执行Pandas代码""" + logger.info(f"DC Execute: code length={len(request.code)}, data rows={len(request.data)}") + result = execute_pandas_code(request.data, request.code) + return JSONResponse(result) + + @app.post("/api/dc/validate") + async def dc_validate_code(request: ValidateRequest): + """工具C:AST代码检查""" + logger.info(f"DC Validate: code length={len(request.code)}") + result = validate_code(request.code) + return JSONResponse(result) + EOF + ``` + +- [ ] **Node.js调用Python服务(PythonExecutorService)** + ```typescript + // backend/src/modules/dc/tool-c/services/PythonExecutorService.ts + import axios from 'axios'; + import { logger } from '@/common/logging'; + + const EXTRACTION_SERVICE_URL = process.env.EXTRACTION_SERVICE_URL || 'http://localhost:8000'; + + export interface ExecuteResult { + success: boolean; + data?: any[]; + totalRows?: number; + totalCols?: number; + columns?: string[]; + error?: string; + } + + export class PythonExecutorService { + private baseUrl: string; + + constructor() { + this.baseUrl = EXTRACTION_SERVICE_URL; + } + + /** + * 执行Pandas代码(⭐ 核心功能) + * 复用现有Python微服务,添加新端点 + */ + async executeCode(data: any[], code: string): Promise { + try { + logger.info('Calling Python executor', { + dataRows: data.length, + codeLength: code.length + }); + + const response = await axios.post( + `${this.baseUrl}/api/dc/execute`, + { data, code }, + { timeout: 30000 } // 30秒超时 + ); + + logger.info('Python execution success', { + totalRows: response.data.totalRows + }); + + return response.data; + } catch (error) { + logger.error('Python execution failed', { error }); + + if (axios.isAxiosError(error) && error.response) { + throw new Error(error.response.data.error || 'Python execution failed'); + } + + throw new Error('无法连接到Python执行服务'); + } + } + + /** + * AST代码检查(执行前验证) + */ + async validateCode(code: string): Promise<{ valid: boolean; error?: string }> { + try { + const response = await axios.post( + `${this.baseUrl}/api/dc/validate`, + { code }, + { timeout: 5000 } + ); + + return response.data; + } catch (error) { + logger.error('Code validation failed', { error }); + throw new Error('代码验证失败'); + } + } + } + + export const pythonExecutorService = new PythonExecutorService(); + ``` + +- [ ] **强制检查**:确保复用平台服务 + ```typescript + // backend/src/modules/dc/tool-c/services/SessionService.ts + + // ✅ 必须导入这些平台服务 + import { storage } from '@/common/storage'; + import { logger } from '@/common/logging'; + import { cache } from '@/common/cache'; + import { prisma } from '@/config/database'; + + // ❌ 禁止自己实现 + // const sessions = new Map() ← 违反规范! + ``` + +- [ ] 数据库Schema设计(dc_schema) + ```prisma + // prisma/schema.prisma(添加到dc_schema) + model DcToolCSession { + id String @id @default(uuid()) + userId String + sessionId String @unique + fileName String + dataSnapshot Json // 存储100行预览数据 + totalRows Int + totalCols Int + columns Json // 列信息 + expiresAt DateTime // 10分钟过期 + createdAt DateTime @default(now()) + + @@schema("dc_schema") + @@map("dc_tool_c_sessions") + } + ``` + +**验收标准:** +- ✅ 文件夹结构与tool-b一致 +- ✅ 导入了所有必须的平台服务 +- ✅ 没有内存Map、没有本地文件存储 + +**负责人:** 后端开发 + +--- + +#### Day 2:Session管理 + 数据加载(⚠️ 存数据库,不用内存) + +**任务清单:** +- [ ] 实现SessionService(**存数据库,符合云原生规范**) + ```typescript + // backend/src/modules/dc/tool-c/services/SessionService.ts + import { storage } from '@/common/storage'; + import { logger } from '@/common/logging'; + import { prisma } from '@/config/database'; + import * as xlsx from 'xlsx'; + import chardet from 'chardet'; + + export class SessionService { + /** + * 创建Session(✅ 存数据库,不用内存Map) + */ + async createSession(userId: string, fileBuffer: Buffer, fileName: string): Promise { + // 1. 检测编码 + const detected = chardet.detect(fileBuffer); + if (detected.encoding.toLowerCase() !== 'utf-8') { + throw new Error(`文件编码为${detected.encoding},请转换为UTF-8`); + } + + // 2. 内存解析Excel(✅ 云原生:不落盘) + const workbook = xlsx.read(fileBuffer, { type: 'buffer' }); + const sheet = workbook.Sheets[workbook.SheetNames[0]]; + const data = xlsx.utils.sheet_to_json(sheet); + + // 3. 提取列信息 + const columns = Object.keys(data[0] || {}).map(col => ({ + name: col, + type: typeof data[0][col] + })); + + // 4. Session存数据库(✅ 符合云原生规范) + const sessionId = `session_${Date.now()}_${Math.random().toString(36)}`; + await prisma.dcToolCSession.create({ + data: { + sessionId, + userId, + fileName, + dataSnapshot: data.slice(0, 100), // 只存前100行预览 + totalRows: data.length, + totalCols: columns.length, + columns, + expiresAt: new Date(Date.now() + 10 * 60 * 1000) // 10分钟过期 + } + }); + + // 5. 完整数据上传到OSS(✅ 云原生:持久化存储) + const dataKey = `dc/tool-c/${sessionId}/full-data.json`; + await storage.uploadBuffer(dataKey, Buffer.from(JSON.stringify(data))); + + logger.info('Session created', { sessionId, totalRows: data.length }); + + return sessionId; + } + + /** + * 获取Session(从数据库读取) + */ + async getSession(sessionId: string) { + const session = await prisma.dcToolCSession.findUnique({ + where: { sessionId } + }); + + if (!session || new Date() > session.expiresAt) { + throw new Error('Session已过期'); + } + + return session; + } + } + ``` + +**测试用例:** +- [ ] 上传UTF-8编码的Excel,正常解析 +- [ ] 上传GBK编码的Excel,被拦截并提示 +- [ ] 上传中文列名的Excel,无乱码 +- [ ] Session存入数据库,可查询 +- [ ] 10分钟后Session自动过期 + +**验收标准:** +- ✅ Session存储在数据库(不是内存Map) +- ✅ 完整数据存储在OSS +- ✅ 符合云原生开发规范 + +**负责人:** Node.js后端 + +--- + +#### Day 3:Node.js BFF + 文件上传 + +**任务清单:** +- [ ] 创建Fastify路由 + ```typescript + // backend/src/modules/dc/tool-c/routes/index.ts + import { FastifyInstance } from 'fastify'; + + export async function registerToolCRoutes(fastify: FastifyInstance) { + // 文件上传 + fastify.post('/api/v1/dc/tool-c/session/init', async (req, reply) => { + const file = await req.file(); + + // 1. 文件大小检查 + if (file.file.bytesRead > 10 * 1024 * 1024) { + return reply.code(413).send({ + success: false, + error: '文件过大,请压缩后重试(限10MB)' + }); + } + + // 2. 转发到Python服务 + const formData = new FormData(); + formData.append('file', file.file, file.filename); + + const response = await axios.post('http://localhost:8001/api/python/init', formData); + + return { + success: true, + sessionId: response.data.sessionId, + data: response.data + }; + }); + } + ``` +- [ ] 集成到主应用 + ```typescript + // backend/src/modules/dc/index.ts + import { registerToolCRoutes } from './tool-c/routes'; + + export async function registerDCRoutes(fastify: FastifyInstance) { + await registerToolBRoutes(fastify); + await registerToolCRoutes(fastify); // 新增 + } + ``` + +**测试用例:** +- [ ] 上传9MB文件,成功 +- [ ] 上传11MB文件,被拦截 +- [ ] 并发上传2个文件,sessionId不冲突 + +**验收标准:** +- ✅ Node.js能正确转发文件到Python +- ✅ 文件大小限制生效 + +**负责人:** Node.js后端 + +--- + +#### Day 4:前端框架搭建 + +**任务清单:** +- [ ] 创建Tool C页面 + ```typescript + // frontend-v2/src/modules/dc/pages/tool-c/index.tsx + import { AgGridReact } from 'ag-grid-react'; + import { Upload, Spin } from 'antd'; + + export default function ToolCEditor() { + const [sessionId, setSessionId] = useState(); + const [data, setData] = useState([]); + const [columns, setColumns] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const handleUpload = async (file: File) => { + setIsLoading(true); + const formData = new FormData(); + formData.append('file', file); + + const response = await api.post('/api/v1/dc/tool-c/session/init', formData); + + setSessionId(response.data.sessionId); + setData(response.data.data.preview); + setColumns(response.data.data.columns); + setIsLoading(false); + }; + + return ( +
+ {/* 左侧:表格区域 */} +
+ {!sessionId ? ( + + + + ) : ( + ({ + field: col.name, + headerName: col.name + }))} + /> + )} +
+ + {/* 右侧:AI侧边栏(占位) */} +
+

AI助手

+
+
+ ); + } + ``` +- [ ] 集成AG Grid + ```bash + cd frontend-v2 + npm install ag-grid-react ag-grid-community + ``` +- [ ] 路由配置 + ```typescript + // frontend-v2/src/modules/dc/routes.tsx + { + path: 'tool-c', + element: + } + ``` + +**测试用例:** +- [ ] 上传文件,表格正确显示 +- [ ] 中文列名显示正常 +- [ ] 空值单元格有视觉提示 + +**验收标准:** +- ✅ 用户能看到左右分栏界面 +- ✅ 表格能展示100行数据 + +**负责人:** 前端开发 + +--- + +#### Day 5:System Prompt构建 + +**任务清单:** +- [ ] 创建Prompt模板 + ```typescript + // backend/src/modules/dc/tool-c/prompts/system-prompt.ts + export function buildSystemPrompt(dataContext: DataContext): string { + return `# AI医疗数据清洗助手 - 系统角色 + +## 当前数据结构 +- 总行数:${dataContext.totalRows} 行 +- 总列数:${dataContext.totalCols} 列 +- 列名:${dataContext.columns.map(c => c.name).join(', ')} + +### 前5行数据示例 +${JSON.stringify(dataContext.headData, null, 2)} + +## 严格规则 +1. 只能使用pandas操作(已预导入为pd) +2. 变量名必须是df(不要用其他名字) +3. 就地修改:df['new'] = ... 或 df.drop(...) +4. 不要print()、display()等输出 +5. 禁止import os、sys、requests等 + +## Few-shot示例(分层难度:基础→中等→高级) + +### 🟢 基础场景(单步骤操作) +#### 场景1:年龄分组 +用户:"把年龄大于60的标记为老年组" +代码: +df['age_group'] = df['age'].apply(lambda x: '老年组' if pd.notna(x) and x > 60 else '非老年组') + +#### 场景2:删除缺失 +用户:"删除没有患者ID的行" +代码: +df.dropna(subset=['patient_id'], inplace=True) + +#### 场景3:性别编码 +用户:"把性别转为数字" +代码: +df['gender_code'] = df['gender'].map({'男': 1, '女': 0}) + +### 🟡 中等场景(多步骤或跨列逻辑) +#### 场景4:计算NLR并分组 +用户:"计算中性粒细胞淋巴细胞比值NLR,并按2.5分为高低两组" +代码: +df['NLR'] = df.apply(lambda row: row['neutrophil'] / row['lymphocyte'] if pd.notna(row['neutrophil']) and pd.notna(row['lymphocyte']) and row['lymphocyte'] > 0 else None, axis=1) +df['NLR_group'] = df['NLR'].apply(lambda x: 'High' if pd.notna(x) and x > 2.5 else 'Low') + +#### 场景5:字符串拆分(血压) +用户:"把血压列的'120/80'格式拆分成收缩压和舒张压,并判断是否高血压" +代码: +df[['systolic_bp', 'diastolic_bp']] = df['blood_pressure'].str.split('/', expand=True) +df['systolic_bp'] = pd.to_numeric(df['systolic_bp'], errors='coerce') +df['diastolic_bp'] = pd.to_numeric(df['diastolic_bp'], errors='coerce') +df['is_hypertension'] = ((df['systolic_bp'] > 140) | (df['diastolic_bp'] > 90)).astype(int) + +#### 场景6:时间差计算(逻辑验证) +用户:"计算住院天数,如果出院日期早于入院日期则标记为异常" +代码: +df['admission_date'] = pd.to_datetime(df['admission_date'], errors='coerce') +df['discharge_date'] = pd.to_datetime(df['discharge_date'], errors='coerce') +df['hospital_days'] = (df['discharge_date'] - df['admission_date']).dt.days +df['date_error'] = df['hospital_days'] < 0 +df.loc[df['date_error'], 'hospital_days'] = None + +### 🔴 高级场景(分组聚合、时间序列、医学规则) +#### 场景7:生存时间计算(复杂条件逻辑) +用户:"生成生存状态和生存时间,如果死亡日期存在则状态为1,时间为死亡日期减诊断日期,否则状态为0,时间为随访截止日期减诊断日期" +代码: +df['diagnosis_date'] = pd.to_datetime(df['diagnosis_date'], errors='coerce') +df['death_date'] = pd.to_datetime(df['death_date'], errors='coerce') +df['followup_end_date'] = pd.to_datetime(df['followup_end_date'], errors='coerce') +df['vital_status'] = df['death_date'].notna().astype(int) +df['survival_days'] = df.apply(lambda row: (row['death_date'] - row['diagnosis_date']).days if pd.notna(row['death_date']) else (row['followup_end_date'] - row['diagnosis_date']).days, axis=1) +df['survival_months'] = (df['survival_days'] / 30.44).round(1) + +#### 场景8:分组聚合(首末记录) +用户:"对每个患者找出第一次化疗日期和最后一次化疗日期,计算持续时间" +代码: +df['chemo_date'] = pd.to_datetime(df['chemo_date'], errors='coerce') +patient_chemo = df.groupby('patient_id')['chemo_date'].agg(['min', 'max']).reset_index() +patient_chemo.columns = ['patient_id', 'first_chemo', 'last_chemo'] +patient_chemo['chemo_duration_days'] = (patient_chemo['last_chemo'] - patient_chemo['first_chemo']).dt.days +df = df.merge(patient_chemo[['patient_id', 'first_chemo', 'last_chemo', 'chemo_duration_days']], on='patient_id', how='left') + +#### 场景9:时间序列变化率 +用户:"按患者ID分组,计算每次随访相比上次的肿瘤大小变化率" +代码: +df = df.sort_values(['patient_id', 'followup_date']) +df['prev_tumor_size'] = df.groupby('patient_id')['tumor_size'].shift(1) +df['tumor_change_rate'] = ((df['tumor_size'] - df['prev_tumor_size']) / df['prev_tumor_size'] * 100).round(2) +df['tumor_change_rate'] = df['tumor_change_rate'].fillna(0) + +#### 场景10:医学规则引擎(肝功能分级) +用户:"根据ALT、AST、ALP、TBIL判断肝功能分级" +代码: +def classify_liver_function(row): + abnormal_count = 0 + if pd.notna(row.get('ALT')) and row['ALT'] > 40: abnormal_count += 1 + if pd.notna(row.get('AST')) and row['AST'] > 40: abnormal_count += 1 + if pd.notna(row.get('ALP')) and row['ALP'] > 125: abnormal_count += 1 + if pd.notna(row.get('TBIL')) and row['TBIL'] > 20: abnormal_count += 1 + if abnormal_count == 0: return '正常' + elif abnormal_count == 1: return '轻度异常' + elif abnormal_count == 2: return '中度异常' + else: return '重度异常' +df['liver_function_grade'] = df.apply(classify_liver_function, axis=1) + +现在,请准备好接收用户的指令。记住:代码要安全、可靠、易懂,能处理从基础到高级的复杂医疗场景。 +`; + } + ``` +- [ ] 集成LLMFactory(✅ 复用平台通用能力层) + ```typescript + // backend/src/modules/dc/tool-c/services/AICodeService.ts + import { LLMFactory } from '@/common/llm'; // ✅ 复用平台LLM能力 + import { logger } from '@/common/logging'; // ✅ 复用日志 + + export class AICodeService { + /** + * 生成Pandas代码(✅ 复用LLMFactory) + */ + async generateCode(prompt: string, dataContext: DataContext): Promise { + logger.info('Generating Pandas code', { prompt, dataContext }); + + // ✅ 复用平台LLM服务 + const llm = LLMFactory.getLLM('deepseek-v3'); + + const systemPrompt = buildSystemPrompt(dataContext); + + const response = await llm.chat([ + { role: 'system', content: systemPrompt }, + { role: 'user', content: prompt } + ]); + + // 提取纯代码(去除Markdown格式) + const code = this.extractCode(response.content); + + logger.info('Code generated successfully', { codeLength: code.length }); + + return code; + } + + /** + * AI自我修复(失败后重试1次) + */ + async fixCode(originalCode: string, errorMsg: string, dataContext: DataContext): Promise { + logger.warn('Code execution failed, attempting self-repair', { errorMsg }); + + const llm = LLMFactory.getLLM('deepseek-v3'); + + const fixPrompt = `以下代码执行失败,请修复: + +错误信息: +${errorMsg} + +原始代码: +${originalCode} + +数据结构: +${JSON.stringify(dataContext)} + +请生成修复后的代码(不要有Markdown格式):`; + + const response = await llm.chat([{ role: 'user', content: fixPrompt }]); + + return this.extractCode(response.content); + } + + private extractCode(text: string): string { + // 去除```python```或```标记 + const match = text.match(/```(?:python)?\n([\s\S]*?)\n```/); + return match ? match[1] : text; + } + } + ``` + +**测试用例:** +- [ ] 输入"年龄分组",生成正确代码 +- [ ] 输入"删除空行",生成正确代码 +- [ ] 检查代码中是否包含危险语句 + +**验收标准:** +- ✅ AI能生成可执行的Pandas代码 +- ✅ 代码符合规范(无print、无import) + +**负责人:** Node.js后端 + +--- + +### Week 2:核心功能开发(5天) + +#### Day 6-7:AI代码生成 + AST检查 + +**Day 6任务:** +- [ ] 实现AST静态检查 + ```python + # python-service/code_validator.py + import ast + + DANGEROUS_IMPORTS = {'os', 'sys', 'subprocess', 'requests', 'urllib'} + DANGEROUS_BUILTINS = {'eval', 'exec', 'compile', 'open', '__import__'} + + def validate_code_safety(code: str) -> Tuple[bool, str]: + try: + tree = ast.parse(code) + + for node in ast.walk(tree): + # 检查import + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name in DANGEROUS_IMPORTS: + return False, f"禁止导入:{alias.name}" + + # 检查函数调用 + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + if node.func.id in DANGEROUS_BUILTINS: + return False, f"禁止使用:{node.func.id}" + + return True, "验证通过" + except SyntaxError as e: + return False, f"语法错误:{str(e)}" + ``` + +**Day 7任务:** +- [ ] 实现AI代码生成API + ```typescript + // Node.js + fastify.post('/api/v1/dc/tool-c/ai/generate', async (req, reply) => { + const { sessionId, prompt } = req.body; + + // 1. 获取数据上下文 + const context = await pythonService.getDataContext(sessionId); + + // 2. 调用AI生成代码 + const code = await aiService.generateCode(prompt, context); + + return { + success: true, + code, + summary: `将执行以下操作:${extractSummary(code)}` + }; + }); + ``` + +**测试场景(15个真实医疗数据清洗场景):** + +**🟢 基础场景(5个)- 单步骤操作:** +1. [ ] "把年龄大于60的标记为老年组" - 简单条件判断 +2. [ ] "删除所有患者ID为空的行" - 数据完整性清洗 +3. [ ] "把性别转为数字,男1女0" - 分类变量编码 +4. [ ] "计算BMI = 体重 / (身高/100)^2" - 简单公式计算 +5. [ ] "删除缺失率超过50%的列" - 列级数据质量控制 + +**🟡 中等场景(5个)- 多步骤或跨列逻辑:** +6. [ ] "把诊断日期和出院日期计算天数差,如果出院日期早于诊断日期则标记为异常" - 逻辑验证 +7. [ ] "根据白细胞、中性粒细胞、淋巴细胞三个指标,计算NLR(中性粒细胞/淋巴细胞),并按2.5分为高低两组" - 多步骤计算 +8. [ ] "从病理报告列中提取TNM分期,生成新列,如果没有提取到则标记为'未分期'" - 文本提取(可用正则) +9. [ ] "把血压列中的'120/80'格式拆分成收缩压和舒张压两列,并判断是否高血压(收缩压>140 or 舒张压>90)" - 字符串处理+逻辑判断 +10. [ ] "删除重复的患者ID,保留最新的一条记录(根据就诊日期)" - 去重+排序 + +**🔴 高级场景(5个)- 复杂分组、时间序列、医学规则:** +11. [ ] "对于每个患者,找出第一次化疗日期和最后一次化疗日期,计算化疗持续时间" - 分组聚合 +12. [ ] "生成生存状态变量:如果死亡日期存在则为1,否则为0;生成生存时间:如果死亡则为(死亡日期-诊断日期),否则为(随访截止日期-诊断日期)" - 复杂条件逻辑 +13. [ ] "根据多个实验室指标(ALT、AST、ALP、TBIL)判断肝功能分级(正常、轻度异常、中度异常、重度异常)" - 医学规则引擎 +14. [ ] "按患者ID分组,对每个患者的多次随访记录,计算相邻两次之间的指标变化率(如肿瘤大小变化率)" - 时间序列分析 +15. [ ] "根据入院时间,计算患者的季节变量(春夏秋冬),然后统计不同季节的发病人数" - 时间特征提取+统计分析 + +**验收标准(分层要求):** +- ✅ **基础场景成功率 > 90%**(5/5或4/5成功) +- ✅ **中等场景成功率 > 80%**(4/5成功) +- ✅ **高级场景成功率 > 60%**(3/5成功) +- ✅ **总体成功率 > 80%**(12/15场景成功) +- ✅ AST能拦截`import os`等危险代码 +- ❌ 如果总体成功率 < 60%(9/15失败),**MVP失败,需要Pivot到模板库模式** + +**负责人:** Node.js后端 + Python开发 + +--- + +#### Day 8:代码执行 + 表格刷新 + +**任务清单:** +- [ ] 实现代码执行API + ```python + # Python + @app.post("/api/python/execute") + async def execute_code(request: ExecuteRequest): + # 1. 获取Session + df = session_manager.get(request.sessionId) + + # 2. AST检查 + is_safe, error_msg = validate_code_safety(request.code) + if not is_safe: + return {"success": False, "error": error_msg} + + # 3. 执行代码 + try: + exec(request.code, {'df': df, 'pd': pd, 'np': np}) + + # 4. 返回预览数据 + preview = df.head(100).to_dict('records') + + return { + "success": True, + "preview": preview, + "stats": { + "totalRows": len(df), + "totalCols": len(df.columns) + } + } + except Exception as e: + return { + "success": False, + "error": str(e), + "traceback": traceback.format_exc() + } + ``` +- [ ] 前端执行流程 + ```typescript + async function executeAICode(code: string) { + // 1. 展示预操作卡片 + const confirmed = await showActionCard(code); + if (!confirmed) return; + + // 2. 锁定表格 + setIsLocked(true); + + try { + // 3. 执行代码 + const response = await api.post('/api/v1/dc/tool-c/ai/execute', { + sessionId, + code + }); + + if (response.data.success) { + // 4. 刷新表格 + setData(response.data.preview); + message.success('执行成功!'); + } else { + message.error(`执行失败:${response.data.error}`); + } + } finally { + // 5. 解锁表格 + setIsLocked(false); + } + } + ``` + +**测试用例:** +- [ ] 执行成功,表格正确刷新 +- [ ] 执行失败,显示错误信息 +- [ ] 执行期间,表格处于锁定状态 + +**验收标准:** +- ✅ 代码能正确修改DataFrame +- ✅ 前端能实时看到变化 + +**负责人:** Python开发 + 前端 + +--- + +#### Day 9:UI锁定 + 预操作卡片 + +**任务清单:** +- [ ] 实现AI对话UI + ```typescript + function AIChatPanel({ sessionId }: Props) { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [isProcessing, setIsProcessing] = useState(false); + + const handleSend = async () => { + if (!input.trim()) return; + + // 1. 添加用户消息 + setMessages(prev => [...prev, { + role: 'user', + content: input + }]); + + setIsProcessing(true); + + // 2. 调用AI生成代码 + const response = await api.post('/api/v1/dc/tool-c/ai/generate', { + sessionId, + prompt: input + }); + + // 3. 添加AI消息(包含代码) + setMessages(prev => [...prev, { + role: 'assistant', + content: response.data.summary, + code: response.data.code + }]); + + setIsProcessing(false); + setInput(''); + }; + + return ( +
+ {/* 消息列表 */} +
+ {messages.map((msg, idx) => ( + + ))} +
+ + {/* 输入框 */} +
+ setInput(e.target.value)} + placeholder="输入指令,例如:把年龄大于60的标记为老年组" + disabled={isProcessing} + /> + +
+
+ ); + } + ``` +- [ ] 实现预操作卡片 + ```typescript + function ActionCard({ code, onConfirm, onCancel }: Props) { + return ( + +
+ AI生成的代码 - 请确认后执行 +
+
+          {code}
+        
+
+ + +
+
+ ); + } + ``` +- [ ] 实现UI锁定 + ```typescript + + + {isLocked && ( +
+ +
+ )} + ``` + +**验收标准:** +- ✅ 用户能发送消息 +- ✅ AI返回代码时展示预操作卡片 +- ✅ 执行期间表格锁定 + +**负责人:** 前端 + +--- + +#### Day 10:导出 + 端到端测试 + +**任务清单:** +- [ ] 实现导出API + ```python + @app.get("/api/python/export/{session_id}") + async def export_excel(session_id: str): + df = session_manager.get(session_id) + + # 生成Excel + output = io.BytesIO() + df.to_excel(output, index=False, engine='openpyxl') + output.seek(0) + + return StreamingResponse( + output, + media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + headers={ + 'Content-Disposition': f'attachment; filename=cleaned_data_{session_id}.xlsx' + } + ) + ``` +- [ ] 前端导出按钮 + ```typescript + const handleExport = async () => { + const response = await api.get(`/api/v1/dc/tool-c/session/export/${sessionId}`, { + responseType: 'blob' + }); + + const url = window.URL.createObjectURL(response.data); + const a = document.createElement('a'); + a.href = url; + a.download = 'cleaned_data.xlsx'; + a.click(); + }; + ``` +- [ ] **端到端测试(完整流程)** + ``` + 1. 上传 test_data.xlsx (5000行 × 20列) + 2. AI指令:"把年龄大于60的标记为老年组" + 3. 确认 → 执行 → 表格刷新(验证新增列) + 4. AI指令:"删除患者ID为空的行" + 5. 确认 → 执行 → 表格刷新(验证行数减少) + 6. 点击"导出" → 下载文件 + 7. 打开下载的文件,验证数据正确 + ``` + +**验收标准:** +- ✅ 整个流程 < 2分钟 +- ✅ 导出的Excel数据正确 +- ✅ AI代码执行成功 + +**负责人:** 全员 + +--- + +### Week 3:优化与测试(5天) + +#### Day 11-12:错误处理 + AI自我修复 + +**任务清单:** +- [ ] 实现AI自我修复 + ```typescript + async function executeWithRetry(code: string): Promise { + // 第一次执行 + let result = await pythonService.execute(sessionId, code); + + if (!result.success) { + // 失败,让AI修复 + const fixedCode = await aiService.fixCode(code, result.error); + + // 重试1次 + result = await pythonService.execute(sessionId, fixedCode); + + if (!result.success) { + // 彻底失败 + return { + success: false, + error: '代码执行失败,请尝试更详细地描述您的需求', + originalError: result.error + }; + } + } + + return result; + } + ``` +- [ ] 优化错误提示 + ```typescript + function showErrorDetail(error: string) { + const errorMap = { + "KeyError: 'age'": "列名'age'不存在,请检查列名是否正确", + "ValueError": "数据类型不匹配,请检查数据格式", + "MemoryError": "数据量过大,请减少数据行数" + }; + + const friendlyMsg = errorMap[error] || error; + + Modal.error({ + title: '执行失败', + content: friendlyMsg + }); + } + ``` + +**测试场景(边界情况):** +- [ ] 列名不存在:`df['nonexistent']` +- [ ] 语法错误:`df[df['age'] > 60 and df['age'] < 80]` +- [ ] 数据类型错误:`df['age'] + '字符串'` +- [ ] 内存溢出:处理超大数据 + +**验收标准:** +- ✅ 常见错误能自我修复 +- ✅ 无法修复时有友好提示 + +**负责人:** Node.js后端 + +--- + +#### Day 13:快捷模板 + 编码检测 + +**任务清单:** +- [ ] 实现快捷模板 + ```typescript + const QUICK_TEMPLATES = { + '年龄分组': { + label: '年龄分组(60岁分界)', + code: `df['age_group'] = pd.cut(df['age'], bins=[0,60,150], labels=['非老年','老年'])` + }, + '删除空行': { + label: '删除空行', + code: `df.dropna(how='all', inplace=True)` + }, + '性别编码': { + label: '性别编码(男1女0)', + code: `df['gender_code'] = df['gender'].map({'男':1, '女':0})` + } + }; + + function QuickActions() { + return ( +
+
快捷操作
+ {Object.entries(QUICK_TEMPLATES).map(([key, template]) => ( + + ))} +
+ ); + } + ``` +- [ ] 实现编码检测 + ```python + import chardet + + def detect_encoding(file_bytes: bytes) -> str: + result = chardet.detect(file_bytes) + encoding = result['encoding'] + confidence = result['confidence'] + + if confidence < 0.8: + raise ValueError(f"无法确定文件编码(置信度{confidence:.0%})") + + return encoding + + @app.post("/api/python/init") + async def init_session(file: UploadFile): + content = await file.read() + + # 检测编码 + encoding = detect_encoding(content) + + if encoding.lower() not in ['utf-8', 'utf-8-sig']: + return { + "success": False, + "error": f"文件编码为{encoding},请转换为UTF-8后重新上传", + "suggestion": "在Excel中另存为CSV UTF-8格式" + } + + # 正常加载 + # ... + ``` + +**验收标准:** +- ✅ 快捷模板一键执行 +- ✅ GBK文件被友好拦截 + +**负责人:** 前端 + Python开发 + +--- + +#### Day 14:性能测试 + 压力测试 + +**测试场景:** +- [ ] **性能测试** + ``` + 测试数据:5000行 × 20列(约5MB) + + 场景1:上传文件 + - 目标:< 5秒 + - 实测:____秒 + + 场景2:AI生成代码 + - 目标:< 5秒 + - 实测:____秒 + + 场景3:代码执行+刷新 + - 目标:< 3秒 + - 实测:____秒 + + 场景4:导出Excel + - 目标:< 3秒 + - 实测:____秒 + + 总计:< 16秒 + ``` +- [ ] **压力测试** + ``` + 场景1:并发用户 + - 5个用户同时上传文件 + - 验证:Session隔离,互不影响 + + 场景2:大数据量 + - 上传50000行 × 50列(约10MB) + - 验证:不崩溃,响应时间可接受 + + 场景3:连续操作 + - 连续执行10次AI指令 + - 验证:内存不泄漏 + ``` + +**性能优化(如果不达标):** +- [ ] 前端:AG Grid虚拟滚动配置 +- [ ] 后端:DataFrame操作改为惰性计算 +- [ ] Python:使用Polars替代Pandas(如果需要) + +**验收标准:** +- ✅ 单次操作 < 16秒 +- ✅ 5个并发用户正常工作 +- ✅ 无内存泄漏 + +**负责人:** 全员 + +--- + +#### Day 15:文档 + 演示准备 + +**任务清单:** +- [ ] 编写用户文档 + ```markdown + # 工具C使用指南 + + ## 快速开始 + 1. 点击"上传文件",选择Excel(限10MB) + 2. 等待3-5秒,表格加载完成 + 3. 在右侧AI助手输入指令,例如:"把年龄大于60的标记为老年组" + 4. 点击"运行代码"确认 + 5. 查看表格自动刷新 + 6. 完成所有操作后,点击"导出"下载结果 + + ## 常见问题 + Q: 文件上传失败,提示文件过大? + A: 请将Excel文件压缩到10MB以内,或删除不需要的列。 + + Q: 列名显示乱码? + A: 请在Excel中另存为"CSV UTF-8"格式后重新上传。 + ``` +- [ ] 准备演示数据 + ``` + 创建 demo_data.xlsx: + - 100行患者数据 + - 包含:patient_id, name, age, gender, admission_date, bmi + - 故意制造一些问题:空值、异常值、需要清洗的数据 + ``` +- [ ] 准备演示脚本 + ``` + 演示流程(5分钟): + 1. 上传demo_data.xlsx + 2. AI指令:"把年龄大于60的标记为老年组" + 3. AI指令:"删除患者ID为空的行" + 4. AI指令:"把性别转为数字,男1女0" + 5. 导出结果 + 6. 展示导出的Excel文件 + ``` + +**验收标准:** +- ✅ 文档清晰易懂 +- ✅ 演示流畅无卡顿 +- ✅ 演示数据能体现核心功能 + +**负责人:** 全员 + +--- + +## 五、风险应对策略 + +### 5.1 风险清单与应对 + +| 风险 | 概率 | 影响 | 应对策略 | 降级方案 | +|------|------|------|---------|---------| +| **AI代码质量低(成功率<60%)** | 中 | 致命 | 1. 优化Prompt工程
2. 增加Few-shot示例
3. 实现自我修复 | **Pivot:改用代码模板库** | +| **Apache Arrow集成困难** | 高 | 高 | 1. MVP不用Arrow
2. 直接用JSON | **已降级:MVP用JSON** | +| **Redis内存成本高** | 中 | 中 | 1. MVP用进程内存
2. 激进的Session过期 | **已降级:用Map缓存** | +| **Python内存泄漏** | 中 | 高 | 1. 不做历史快照
2. 定期重启进程 | **SAE自动重启** | +| **中文Excel乱码** | 高 | 中 | 1. chardet自动检测
2. 友好报错提示 | **文档说明转UTF-8** | +| **SAE冷启动慢** | 高 | 中 | 1. 最小实例数=1
2. 异步初始化 | **Loading优化** | +| **前端AG Grid性能差** | 低 | 中 | 1. 只展示100行
2. 虚拟滚动 | **已优化** | + +### 5.2 快速失败触发器 + +**立即停止开发的条件:** + +1. **Day 7结束,AI代码成功率 < 60%** + - 决策:放弃AI Code Interpreter路线 + - Pivot:改为"代码模板库 + 参数化配置" + +2. **Day 10结束,端到端时间 > 30秒** + - 决策:性能无法接受 + - Pivot:改为批处理模式(上传 → 后台处理 → 下载) + +3. **Day 14结束,3个用户测试,都无法独立完成任务** + - 决策:交互设计失败 + - Pivot:重新设计UI,增加新手引导 + +--- + +## 六、验收标准 + +### 6.1 功能验收清单 + +| 编号 | 验收项 | 验收标准 | 验收方式 | +|------|--------|---------|---------| +| **F-001** | 文件上传 | 能上传10MB以内的Excel | 手动测试 | +| **F-002** | 编码检测 | GBK文件被友好拦截 | 手动测试 | +| **F-003** | 表格展示 | 100行数据正确显示,中文无乱码 | 手动测试 | +| **F-004** | AI对话 | 能发送消息,接收回复 | 手动测试 | +| **F-005** | 代码生成 | **10个场景,8个成功(80%)** | 自动化测试 | +| **F-006** | AST检查 | 拦截`import os`等危险代码 | 单元测试 | +| **F-007** | 代码执行 | 能正确修改DataFrame | 集成测试 | +| **F-008** | 表格刷新 | 执行后自动更新 | 手动测试 | +| **F-009** | UI锁定 | AI处理时表格只读+遮罩 | 手动测试 | +| **F-010** | 导出Excel | 下载的文件数据正确 | 手动测试 | + +### 6.2 性能验收标准 + +| 指标 | 目标值 | 验收方式 | +|------|--------|---------| +| 文件上传到表格显示 | < 5秒 | 性能测试 | +| AI生成代码 | < 5秒 | 性能测试 | +| 代码执行+刷新 | < 3秒 | 性能测试 | +| 导出Excel | < 3秒 | 性能测试 | +| **端到端总时间** | **< 16秒** | 性能测试 | +| 并发用户数 | ≥ 5人 | 压力测试 | +| 内存占用 | < 2GB(5用户) | 压力测试 | + +### 6.3 安全验收标准 + +| 指标 | 验收标准 | 验收方式 | +|------|---------|---------| +| 代码沙箱 | 拦截所有危险代码 | 渗透测试 | +| Session隔离 | 用户A看不到用户B数据 | 并发测试 | +| Session过期 | 10分钟后自动清理 | 时间测试 | + +--- + +## 七、快速失败机制 + +### 7.1 关键检查点 + +``` +Checkpoint 1 (Day 5结束): +├─ System Prompt能否让AI理解数据? +├─ AI能生成Pandas代码吗? +└─ 决策:继续 or 调整Prompt策略 + +Checkpoint 2 (Day 7结束): +├─ AI代码成功率 ≥ 80%? +├─ 如果 < 60%:立即停止,改用模板库 +└─ 如果 60-80%:继续优化Prompt + +Checkpoint 3 (Day 10结束): +├─ 端到端流程能跑通? +├─ 性能 < 16秒? +└─ 决策:继续 or 性能优化 + +Checkpoint 4 (Day 14结束): +├─ 用户测试通过? +├─ 无重大Bug? +└─ 决策:发布 or 修复Bug +``` + +### 7.2 Pivot决策树 + +``` +如果 AI代码成功率 < 60%: + ├─ Pivot 1:改用"代码模板库 + 参数化配置" + │ └─ 用户选择模板 → 填写参数 → 生成代码 + │ + ├─ Pivot 2:AI变成"辅助建议"角色 + │ └─ 用户手动写代码,AI提供建议 + │ + └─ Pivot 3:简化为"批处理模式" + └─ 上传 → 选择预设操作 → 后台处理 → 下载 + +如果 性能无法接受(> 30秒): + ├─ 方案1:引入Apache Arrow + ├─ 方案2:使用Polars替代Pandas + └─ 方案3:改为批处理+异步通知 + +如果 用户无法独立完成任务: + ├─ 方案1:增加交互式新手引导 + ├─ 方案2:提供示例数据集 + └─ 方案3:简化UI,减少选项 +``` + +--- + +## 八、附录 + +### 8.1 测试数据准备 + +**创建标准测试数据集:** +```python +# create_test_data.py +import pandas as pd +import numpy as np + +# 生成5000行测试数据 +data = { + 'patient_id': [f'P{i:04d}' for i in range(1, 5001)], + 'name': [f'患者{i}' for i in range(1, 5001)], + 'age': np.random.randint(18, 90, 5000), + 'gender': np.random.choice(['男', '女'], 5000), + 'admission_date': pd.date_range('2023-01-01', periods=5000, freq='H'), + 'weight': np.random.uniform(45, 95, 5000).round(1), + 'height': np.random.uniform(150, 190, 5000).round(0), + 'diagnosis': np.random.choice(['肺癌', '糖尿病', '高血压'], 5000) +} + +df = pd.DataFrame(data) + +# 故意制造问题 +df.loc[np.random.choice(5000, 100, replace=False), 'age'] = None # 100个缺失 +df.loc[np.random.choice(5000, 10, replace=False), 'age'] = 150 # 10个异常值 +df.loc[np.random.choice(5000, 50, replace=False), 'patient_id'] = None # 50个空ID + +df.to_excel('test_data.xlsx', index=False) +``` + +### 8.2 10个典型场景测试用例 + +```python +# test_ai_scenarios.py +test_cases = [ + { + "id": "TC-001", + "prompt": "把年龄大于60的患者标记为老年组", + "expected_code": "df['age_group'] = df['age'].apply(lambda x: '老年组' if pd.notna(x) and x > 60 else '非老年组')", + "verify": lambda df: 'age_group' in df.columns + }, + { + "id": "TC-002", + "prompt": "删除所有患者ID为空的行", + "expected_code": "df.dropna(subset=['patient_id'], inplace=True)", + "verify": lambda df: df['patient_id'].isnull().sum() == 0 + }, + # ... 其余8个场景 +] +``` + +### 8.3 环境配置清单 + +**Node.js依赖:** +```json +{ + "dependencies": { + "fastify": "^4.0.0", + "axios": "^1.6.0", + "joi": "^17.11.0", + "@fastify/multipart": "^8.0.0" + } +} +``` + +**Python依赖:** +```txt +fastapi==0.115.0 +uvicorn==0.30.0 +pandas==2.2.0 +openpyxl==3.1.0 +chardet==5.2.0 +python-multipart==0.0.9 +``` + +--- + +## 九、项目管理 + +### 9.1 团队配置 + +| 角色 | 人数 | 职责 | +|------|------|------| +| 前端开发 | 1人 | React + AG Grid + UI | +| Node.js后端 | 1人 | BFF + LLM集成 + Prompt工程 | +| Python开发 | 1人 | FastAPI + DataFrame + AST检查 | +| **总计** | **3人** | **3周(15天)** | + +### 9.2 沟通机制 + +- **每日站会**:15分钟,同步进度和问题 +- **关键检查点**:Day 5、7、10、14 +- **问题升级**:阻塞问题1小时内升级 +- **文档更新**:每日更新开发记录 + +### 9.3 代码管理 + +**分支策略:** +``` +main (稳定版) + └── develop (开发版) + ├── feature/tool-c-frontend + ├── feature/tool-c-backend + └── feature/tool-c-python +``` + +**提交规范:** +``` +feat: 新增AI代码生成功能 +fix: 修复表格刷新Bug +test: 增加AST检查测试用例 +docs: 更新开发文档 +``` + +--- + +## 十、总结 + +### 核心成功要素 +1. ✅ **AI代码质量**:成功率 > 80%(核心假设) +2. ✅ **性能可接受**:端到端 < 16秒 +3. ✅ **交互简单**:用户无需文档就能用 + +### 风险控制 +1. ✅ **快速失败**:7天验证AI能力,不行立即Pivot +2. ✅ **务实技术**:JSON不用Arrow,内存不用Redis +3. ✅ **降级方案**:准备好模板库、批处理等Plan B + +### 时间节点 +- **Week 1**:基础架构(5天) +- **Week 2**:核心功能(5天) +- **Week 3**:优化测试(5天) +- **总计**:15个工作日 + +--- + +## 📝 文档修订记录 + +### V1.3(2025-12-06)- 发现现有Python服务 ⭐ 重大发现 + +**修正内容**:检查系统发现已有Python微服务,不需要重复开发: + +#### 修正1:复用现有Python服务,不重复造轮 ⭐⭐⭐⭐⭐ +- **重大发现**:系统已有 `extraction_service/`(FastAPI + Pandas + openpyxl) +- **现有功能**: + - ✅ PDF/Docx/Txt文档提取(PyMuPDF + Mammoth) + - ✅ Pandas、openpyxl、chardet、langdetect已安装 + - ✅ FastAPI框架已运行(端口8000) + - ✅ Node.js已有ExtractionClient集成 +- **新增方案**: + - ✅ 扩展现有服务,添加 `/api/dc/execute` 端点 + - ✅ 新增 `dc_executor.py` 模块(AST检查 + 代码执行) + - ✅ 复用ExtractionClient模式调用 +- **避免重复**: + - ❌ 不需要新建Python项目 + - ❌ 不需要重新安装依赖 + - ❌ 不需要重新写HTTP调用逻辑 + +--- + +### V1.2(2025-12-06)- Python执行环境 + 复杂场景 ⚠️ 核心功能 + +**修正内容**:根据用户反馈,明确Python代码执行是核心功能: + +#### 修正1:Python执行是核心,不是可选 ⭐⭐⭐⭐⭐ +- **原错误**:说"MVP阶段可能不需要Python微服务" +- **已修正**:**Python代码执行是工具C的核心价值,必须实现** +- **新增内容**: + - Day 1增加Python环境搭建 + - 新增PythonExecutorService(Node.js ↔ Python通信) + - 新增executor.py(Pandas代码执行器) + - 技术方案:Node.js child_process调用Python脚本 + +#### 修正2:测试场景从简单到复杂 ✅ +- **原问题**:10个测试场景过于简单 +- **已修正**:15个真实医疗数据清洗场景 + - 🟢 基础场景5个:单步骤操作 + - 🟡 中等场景5个:多步骤、跨列逻辑 + - 🔴 高级场景5个:分组聚合、时间序列、医学规则 +- **新增场景**: + - 复杂条件逻辑(生存时间计算) + - 医学规则引擎(肝功能分级) + - 时间序列分析(指标变化率) + - 文本提取(TNM分期) + - 字符串处理(血压拆分) + +#### 修正3:验收标准分层 ✅ +- **原标准**:总体成功率 > 80% +- **新标准**: + - 基础场景成功率 > 90% + - 中等场景成功率 > 80% + - 高级场景成功率 > 60% + - 总体成功率 > 80%(12/15场景) + +#### 修正4:核心假设验证 ✅ +- **新增H2**:Python代码执行环境稳定可靠 +- **修改H1**:从"生成代码"改为"生成代码并成功执行" +- **强调**:AI生成代码 + 真实执行 + 表格刷新是差异化价值 + +--- + +### V1.1(2025-12-06)- 架构合规性修正 ⚠️ 重要 + +**修正内容**:根据用户严肃提醒,修正以下违反规范的问题: + +#### 修正1:强制复用平台能力 ✅ +- **原错误**:建议自己实现Session管理、存储、日志 +- **已修正**:强制使用`storage`, `logger`, `cache`, `prisma`, `LLMFactory` +- **影响章节**:所有Day 1-15的代码示例 + +#### 修正2:Session存储方式 ✅ +- **原错误**:建议用`Map`内存缓存 +- **已修正**:Session存数据库(`dc_tool_c_sessions`表) +- **原因**:违反云原生规范第2条(禁止内存缓存) + +#### 修正3:文件夹结构 ✅ +- **原错误**:建议创建`python-service/`、`node-service/`子文件夹 +- **已修正**:遵循tool-b结构(`services/`, `controllers/`, `routes/`) +- **参考**:`backend/src/modules/dc/tool-b/` + +#### 修正4:技术栈说明 ✅ +- **原错误**:未强调复用现有能力 +- **已修正**:明确标注"已有"、"复用平台能力" +- **新增**:云原生开发规范检查清单 + +#### 修正5:代码示例 ✅ +- **原错误**:Day 1-5的代码示例未体现平台服务 +- **已修正**:所有代码示例都使用`import { storage } from '@/common/storage'`等 +- **新增**:常见错误示例(❌ 严禁) + +#### 新增章节 ✅ +- **开发前必读**:强调不要重复造轮子 +- **云原生规范强制要求**:列出6条核心规范 +- **常见错误示例**:展示5个严禁的错误写法 + +--- + +**文档状态:** ✅ 已完成(V1.1 架构合规性修正版) +**下一步:** 团队Review → 严格按规范开发 +**负责人:** 项目经理 +**创建日期:** 2025-12-06 +**修订日期:** 2025-12-06(架构合规性修正) + +**⚠️ 重要提醒**: +1. 开发前必须阅读:`docs/04-开发规范/08-云原生开发规范.md` +2. 参考现有实现:`backend/src/modules/dc/tool-b/` +3. 禁止违反规范:内存缓存、本地文件存储、重复实现平台能力 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md index 5a5f38ad..8335f21d 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md @@ -295,3 +295,4 @@ Changes: **版本**: V1.0 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md index ac9442ec..def027a8 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md @@ -385,3 +385,4 @@ Docs: docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md index 080899bb..03d6cced 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md @@ -360,3 +360,4 @@ const mockAssets: Asset[] = [ **文档版本**: V1.0 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md index 70b8a9be..1f37201b 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md @@ -344,3 +344,4 @@ frontend-v2/src/modules/dc/ **UI质量**: ⭐⭐⭐⭐⭐ **状态**: ✅ 已完成,待测试 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md index b506fb9b..e2579462 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md @@ -304,3 +304,4 @@ **文档版本**: V1.0 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md index 6f613a2c..5983637b 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md @@ -258,3 +258,4 @@ ConflictDetectionService // 冲突检测(字段级对比) **文档创建时间:** 2025-12-03 **维护者:** DC模块开发团队 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md index 350e9247..6f3e7b0e 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md @@ -307,3 +307,4 @@ **UI质量**: ⭐⭐⭐⭐⭐ **用户体验**: ⭐⭐⭐⭐⭐ + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md index 5041a550..f236ea65 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md @@ -270,3 +270,4 @@ **测试状态**: ✅ 待用户验证 **代码质量**: ⭐⭐⭐⭐⭐ + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md index 5fca2c2a..6a405c6c 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md @@ -334,3 +334,4 @@ **测试完成后,请更新此文档并标记所有测试点的完成状态!** + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md index e3769019..bb5bea30 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md @@ -422,3 +422,4 @@ Tool B后端代码**100%复用**了平台通用能力层,无任何重复开发 *本报告将持续更新,随着测试进展补充更多测试结果* + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md index e796282a..716acced 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md @@ -268,3 +268,4 @@ **更新人**: AI Assistant **下次更新**: 测试完成后 + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md index dce50cec..42082ee7 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md @@ -199,3 +199,4 @@ $ node scripts/check-dc-tables.mjs **下次验证**: 不需要(除非重建数据库) + diff --git a/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md b/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md index 96647022..2c4f24b0 100644 --- a/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md +++ b/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md @@ -432,3 +432,4 @@ ${fields.map((f, i) => `${i + 1}. ${f.name}:${f.desc}`).join('\n')} **文档维护:** 每次处理技术债务时更新此文档 + diff --git a/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx b/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx index ed254c6c..ce5f4f69 100644 --- a/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx +++ b/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx @@ -511,3 +511,4 @@ export default FulltextDetailDrawer; + diff --git a/frontend-v2/src/modules/asl/hooks/useFulltextResults.ts b/frontend-v2/src/modules/asl/hooks/useFulltextResults.ts index be8855f3..b4a96dc8 100644 --- a/frontend-v2/src/modules/asl/hooks/useFulltextResults.ts +++ b/frontend-v2/src/modules/asl/hooks/useFulltextResults.ts @@ -110,3 +110,4 @@ export function useFulltextResults({ + diff --git a/frontend-v2/src/modules/asl/hooks/useFulltextTask.ts b/frontend-v2/src/modules/asl/hooks/useFulltextTask.ts index fe6e8cc6..ddda6250 100644 --- a/frontend-v2/src/modules/asl/hooks/useFulltextTask.ts +++ b/frontend-v2/src/modules/asl/hooks/useFulltextTask.ts @@ -73,3 +73,4 @@ export function useFulltextTask({ + diff --git a/frontend-v2/src/modules/asl/pages/FulltextResults.tsx b/frontend-v2/src/modules/asl/pages/FulltextResults.tsx index d1d2975b..851d6c7a 100644 --- a/frontend-v2/src/modules/asl/pages/FulltextResults.tsx +++ b/frontend-v2/src/modules/asl/pages/FulltextResults.tsx @@ -464,3 +464,4 @@ export default FulltextResults; + diff --git a/frontend-v2/src/modules/dc/hooks/useAssets.ts b/frontend-v2/src/modules/dc/hooks/useAssets.ts index bacce2e4..a733d643 100644 --- a/frontend-v2/src/modules/dc/hooks/useAssets.ts +++ b/frontend-v2/src/modules/dc/hooks/useAssets.ts @@ -104,3 +104,4 @@ export const useAssets = (activeTab: AssetTabType) => { }; + diff --git a/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts b/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts index 7d01a294..49211dd7 100644 --- a/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts +++ b/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts @@ -94,3 +94,4 @@ export const useRecentTasks = () => { }; + diff --git a/frontend-v2/src/modules/dc/types/portal.ts b/frontend-v2/src/modules/dc/types/portal.ts index 21169196..5b3dd0cd 100644 --- a/frontend-v2/src/modules/dc/types/portal.ts +++ b/frontend-v2/src/modules/dc/types/portal.ts @@ -52,3 +52,4 @@ export interface Asset { export type AssetTabType = 'all' | 'processed' | 'raw'; + diff --git a/recover_dc_code.py b/recover_dc_code.py index d10a9468..1429bb58 100644 --- a/recover_dc_code.py +++ b/recover_dc_code.py @@ -216,3 +216,4 @@ if __name__ == "__main__": + diff --git a/run_recovery.ps1 b/run_recovery.ps1 index 67065829..7075eedb 100644 --- a/run_recovery.ps1 +++ b/run_recovery.ps1 @@ -40,3 +40,4 @@ Write-Host "==================================================================== +