feat(ssa): Complete Phase V-A editable analysis plan variables

Features:
- Add editable variable selection in workflow plan (SingleVarSelect + MultiVarTags)
- Implement 3-layer flexible interception (warning bar + icon + blocking dialog)
- Add tool_param_constraints.json for 12 statistical tools parameter validation
- Add PATCH /workflow/:id/params API with Zod structural validation
- Implement synchronous parameter sync before execution (Promise chaining)
- Fix LLM hallucination by strict system prompt constraints
- Fix DynamicReport object-based rows compatibility (R baseline_table)
- Fix Word export row.map error with same normalization logic
- Restore inferGroupingVar for smart default variable selection
- Add ReactMarkdown rendering in SSAChatPane
- Update SSA module status document to v3.5

Modified files:
- backend: workflow.routes, ChatHandlerService, SystemPromptService, FlowTemplateService
- frontend: WorkflowTimeline, SSAWorkspacePane, DynamicReport, SSAChatPane, ssaStore, ssa.css
- config: tool_param_constraints.json (new)
- docs: SSA status doc, team review reports

Tested: Cohort study end-to-end execution + report export verified
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-24 13:08:29 +08:00
parent dc6b292308
commit 85fda830c2
27 changed files with 2732 additions and 154 deletions

View File

@@ -1,13 +1,24 @@
# SSA智能统计分析模块 - 当前状态与开发指南
> **文档版本:** v3.4
> **文档版本:** v3.5
> **创建日期:** 2026-02-18
> **最后更新:** 2026-02-22
> **最后更新:** 2026-02-23
> **维护者:** 开发团队
> **当前状态:** 🎉 **QPER 主线闭环 + Phase I + Phase II + Phase III + Phase IV对话驱动分析 + QPER 集成)开发完成**
> **当前状态:** 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A分析方案变量可编辑化)开发完成**
> **文档目的:** 快速了解SSA模块状态为新AI助手提供上下文
>
> **最新进展2026-02-22 Phase IV 完成):**
> **最新进展2026-02-23 Phase V-A 变量可编辑化完成):**
> - ✅ **分析方案变量可编辑化** — 系统默认帮选变量,医生可在方案审查阶段修改/调整变量选择
> - ✅ **三层柔性拦截** — Layer 1 即时黄条警告 + Layer 2 步骤警告图标 + Layer 3 执行前阻断确认弹窗Informed Consent
> - ✅ **变量选择器 UI** — 单选下拉(按类型分组)+ 多选标签(分类=紫色 / 连续=蓝色)+ 全选分类/连续快捷按钮 + 不适配变量 ⚠️ 标记
> - ✅ **tool_param_constraints 配置** — 12 个统计工具参数约束表,前后端共用单一事实来源
> - ✅ **后端 PATCH API + Zod 防火墙** — PATCH /workflow/:id/params + 结构校验400 Bad Request/ 统计学校验交给 R 引擎
> - ✅ **同步阻塞执行** — 执行按钮 Promise Chainingawait PATCH -> 再触发执行 + loading 防连点
> - ✅ **inferGroupingVar 恢复** — LLM 未识别分组变量时,自动推断二分类变量填入默认值
> - ✅ **DynamicReport 增强** — 兼容 R 基线表对象格式 rowsWord 导出同步修复
> - ✅ **前后端集成测试通过** — 队列研究完整执行 + 报告导出验证
>
> **此前进展2026-02-22 Phase IV 完成):**
> - ✅ **Phase IV 全 5 批次完成** — ToolOrchestratorServicePICO hint 三层降级)+ handleAnalyze 重写plan→analysis_plan SSE→LLM 方案说明→ask_user 确认)+ AVAILABLE_TOOLS 配置化11 处改 toolRegistryService+ 前端 SSE 对接analysis_plan + plan_confirmed
> - ✅ **团队审查 H1-H3+B1-B2 全部落地** — H1 PICO hint 注入 / H2 幽灵卡片清除 / H3 SSE 严格串行 / B1 修改建议循环 / B2 旧 API 兼容
> - ✅ **SSA_ANALYZE_PLAN Prompt 入库** — 指导 LLM 用自然语言解释分析方案(步骤/理由/注意事项)
@@ -57,7 +68,7 @@
| **前端状态模型** | **Unified Record Architecture — 一次分析 = 一个 Record = N 个 Steps** |
| **商业价值** | ⭐⭐⭐⭐⭐ 极高 |
| **目标用户** | 临床研究人员、生物统计师 |
| **开发状态** | 🎉 **QPER 主线闭环 + 智能对话架构设计完成Phase Deploy 待启动** |
| **开发状态** | 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A变量可编辑化完成** |
### 核心目标
@@ -159,7 +170,8 @@ AnalysisRecord {
| **Phase II** | **对话层 LLM + 意图路由器 + 统一对话入口** | **35h** | ✅ **已完成4 批次, 12 文件, E2E 38/38, H1-H4 落地)** | 2026-02-22 |
| **Phase III** | **method_consult + ask_user 标准化** | **20h** | ✅ **已完成5 批次, 12 文件, E2E 13/13+4skip, H1-H3+P1 落地)** | 2026-02-22 |
| **Phase IV** | **对话驱动分析 + QPER 集成** | **14h** | ✅ **已完成5 批次, 11 文件, E2E 25/25, H1-H3+B1-B2 落地)** | 2026-02-22 |
| **Phase V** | **反思编排 + 高级特性** | **18h** | 📋 待开始 | - |
| **Phase V-A** | **分析方案变量可编辑化** | **~6h** | **已完成9 文件, 团队双视角审查 V2, 三层柔性拦截)** | 2026-02-23 |
| **Phase V-B** | **反思编排 + 高级特性** | **18h** | 📋 待开始 | - |
| **Phase VI** | **集成测试 + 可观测性** | **10h** | 📋 待开始 | - |
### 已完成核心功能
@@ -181,7 +193,9 @@ AnalysisRecord {
| **Phase III 前端** | AskUserCard4 inputType + H1 跳过按钮)+ useSSAChat 扩展pendingQuestion + respondToQuestion + skipQuestion | ✅ |
| **Phase IV 后端** | ToolOrchestratorServiceplan+PICO hint 三层降级+formatPlanForLLM+ ChatHandlerService 重写handleAnalyze: plan→analysis_plan SSE→LLM 说明→ask_user 确认; handleAskUserResponse: confirm_plan/change_method+ AVAILABLE_TOOLS 配置化11 处→toolRegistryService+ ToolRegistryService+getVisibleTools+ AskUserService+metadata+ SSA_ANALYZE_PLAN Prompt 入库 | ✅ |
| **Phase IV 前端** | useSSAChatanalysis_plan+plan_confirmed SSE 处理+pendingPlanConfirm→executeWorkflow+ SSAChatPaneAskUserCard 渲染+幽灵卡片清除 H2 | ✅ |
| **测试** | QPER 端到端 40/40 + 集成测试 7 Bug 修复 + Phase I E2E 31/31 + Phase II E2E 38/38 + Phase III E2E 13/13+4skip + Phase IV E2E 25/25 | ✅ |
| **Phase V-A 后端** | PATCH /workflow/:id/paramsZod 结构校验防火墙)+ tool_param_constraints.json12 工具参数约束)+ inferGroupingVar 恢复(默认填充分组变量) | ✅ |
| **Phase V-A 前端** | WorkflowTimeline 可编辑化SingleVarSelect + MultiVarTags + 三层柔性拦截)+ ssaStore updateStepParams + SSAWorkspacePane 同步阻塞执行 + DynamicReport 对象 rows 兼容 + Word 导出修复 | ✅ |
| **测试** | QPER 端到端 40/40 + 集成测试 7 Bug 修复 + Phase I E2E 31/31 + Phase II E2E 38/38 + Phase III E2E 13/13+4skip + Phase IV E2E 25/25 + Phase V-A 前后端集成测试通过 | ✅ |
---
@@ -210,7 +224,8 @@ backend/src/modules/ssa/
│ ├── ConfigLoader.ts # 通用 JSON 加载 + Zod 校验
│ ├── tools_registry.json # R 工具注册表
│ ├── decision_tables.json # 四维匹配规则
── flow_templates.json # 流程模板
── flow_templates.json # 流程模板
│ └── tool_param_constraints.json # Phase V-A12 工具参数类型约束
├── types/
│ ├── query.types.ts # Q 层接口
│ ├── reflection.types.ts # R 层接口
@@ -324,7 +339,7 @@ npx tsx scripts/seed-ssa-phase4-prompts.ts # Phase IV: SSA_ANALYZE_PLAN
### 近期(优先级高)
1. **Phase V — 反思编排 + 高级特性18h / 3 天)**
1. **Phase V-B — 反思编排 + 高级特性18h / 3 天)**
- 错误分类器实现(可自愈 vs 不可自愈)
- 自动反思静默重试MAX 2 次)+ 手动反思用户驱动feedback 意图)
- write_report interpret 模式 + discuss 意图处理(深度解读已有结果)
@@ -335,7 +350,7 @@ npx tsx scripts/seed-ssa-phase4-prompts.ts # Phase IV: SSA_ANALYZE_PLAN
3. **Phase VI10h** — 集成测试 + 可观测性(含 QPER 透明化)
**详细计划:** `04-开发计划/11-智能对话与工具体系开发计划.md`v1.8Phase I-IV 完成,含架构约束 C1-C8 + 全部团队审查落地记录)
**详细计划:** `04-开发计划/11-智能对话与工具体系开发计划.md`v1.8Phase I-IV + Phase V-A 完成,含架构约束 C1-C8 + 全部团队审查落地记录)
---
@@ -380,7 +395,7 @@ npx tsx scripts/seed-ssa-phase4-prompts.ts # Phase IV: SSA_ANALYZE_PLAN
---
**文档版本:** v3.4
**最后更新:** 2026-02-22
**当前状态:** 🎉 QPER 主线闭环 + Phase I + Phase II + Phase III + Phase IV 已完成
**下一步:** Phase V反思编排 + 高级特性18h/3 天)
**文档版本:** v3.5
**最后更新:** 2026-02-23
**当前状态:** 🎉 QPER 主线闭环 + Phase I-IV + Phase V-A变量可编辑化已完成
**下一步:** Phase V-B(反思编排 + 高级特性18h/3 天)

View File

@@ -0,0 +1,91 @@
# **架构与统计双重视角审查报告:分析方案变量可编辑化**
**审查对象:** plan\_editable\_variables\_27d3a9fd.plan.md
**审查时间:** 2026-02-23
**总体评级:** 🌟 **A 级 (方向极其正确,但存在隐性逻辑冲突需补强)**
**核心裁决:** 批准开发。但在前端变量过滤逻辑和后端校验机制上,必须引入“基于方法 Schema 的强约束”,否则极易导致下游 R 引擎大面积崩溃。
## **一、 视角一:资深统计学专家的评估**
**“不要给用户犯错的自由。医学统计的容错率是 0。”**
### **1\. 极度认可的改进**
* **尊重临床逻辑**AI 经常会把“住院天数”和“年龄”搞混或者漏掉医生特别关心的某个协变量。允许医生在执行前把遗漏的变量Tag加回来这才是真正懂临床的工具。
* **按类型分组展示**:下拉面板将连续变量和分类变量分开展示,极大地降低了医生寻找变量的认知负荷。
### **2\. 统计学视角的致命盲区 (The Statistical Blind Spot)**
正如您在提问中敏锐指出的,仅仅区分“分类 (Categorical)”和“连续 (Continuous)”是远远不够的。
* **二元 Logistic 回归陷阱**该方法要求结局指标Y**必须且只能**是二分类变量(如:死/活0/1。如果用户的下拉列表里显示了所有“分类变量”包含了 3 分类的“血型”),一旦用户手抖选了“血型”,后端的 R 代码执行时将 100% 报错崩溃。
* **T 检验陷阱**:独立样本 T 检验的分组变量X**必须**是二分类变量。如果是 3 分类变量,必须用 ANOVA。
* **生存分析陷阱**:它需要两个 YTime 是连续Status 是二分类 0/1
### **🛠️ 统计专家的修正建议:引入“细粒度统计类型过滤”**
前端的下拉框候选项Options不能仅仅根据 type \=== 'categorical' 来过滤,必须**与当前 Step 绑定的统计方法强关联**。
* **建议实现**:前端在渲染下拉框时,必须读取该统计工具的 params\_schema在 Phase III / IV 中已定义)。
* **UI 约束逻辑**
* 如果当前是 ST\_LOGISTIC结局变量的下拉框**只能**展示 DataProfile 中推断为 categorical\_2 (唯一值为2) 的变量。
* 对于不符合当前统计方法要求的变量,在下拉框中将其 disabled (置灰),并 hover 提示:“该变量为多分类,二元逻辑回归仅支持二分类变量”。
## **二、 视角二:资深架构师的评估**
**“用户的每一次修改,都可能打破系统原本自洽的状态机。”**
### **1\. 架构师高度赞赏的决策 (Brilliant Architectural Choices)**
* **后端 PATCH API 的设计 (方案 A)**
这是非常正统的 RESTful 设计。用户修改参数后,先 PATCH /params 更新数据库实体,然后再触发 executeWorkflow。这保证了数据库中的 Plan 永远是 Single Source of Truth单一事实来源避免了前端传来“幽灵参数”导致历史记录无法复现的灾难。
### **2\. 工程视角的潜在风险 (Engineering Risks)**
#### **🚨 风险 1UI 状态与执行动作的竞态条件 (Race Condition)**
* **场景**:用户在前端刚从下拉框里切换了变量,还没等组件把 state 同步完毕,或者还没等 PATCH 接口返回 200 OK用户就光速点击了“开始执行分析”按钮。
* **后果**executeWorkflow 可能会拿着数据库里**旧的参数**去执行。
* **修正建议**
“开始执行分析”按钮必须绑定一个复合动作Promise Chaining
async function handleExecute() {
setExecuting(true);
// 1\. 必须先 await 等待 PATCH 成功
if (hasUnsavedChanges) {
await api.patchWorkflowParams(workflowId, modifiedSteps);
}
// 2\. 然后再触发执行
startSseExecution(workflowId);
}
#### **🚨 风险 2级联失效与重新规划的边界 (Cascading Invalidation)**
* **场景**AI 原本规划了 \[描述统计 \-\> T检验 \-\> Mann-Whitney\]。此时,用户在“描述统计”那一步,把 analyze\_vars 里的变量全删了,换成了一批全新的变量。
* **架构思考**:此时,下游的 T检验 步骤的参数是否还有效?
* **修正建议 (MVP 阶段的防御性降维)**
在当前计划中,请严格限制:**参数的可编辑性仅限于“同类替换”**。
如果用户想要推翻整个研究假设(比如把 Y 变量从“血压”改成了“有效/无效”系统不应该允许他们通过修改参数来完成因为这会触发统计方法的变更T检验 变 卡方)。
* **前端提示**:在卡片顶部加一行提示:“如需更改核心分析目标(如改变数据类型),请在对话框告诉 AI 重新生成方案。”
#### **🚨 风险 3Zod Schema 的后端防御 (Backend Defense)**
* **场景**:前端即便做了限制,但网络请求是可以被篡改的,或者存在前端 Bug 漏传了非法参数。
* **修正建议**
新增的 PATCH /api/v1/ssa/workflow/:workflowId/params 接口,**绝对不能盲目接收数据**。它必须使用对应 R 工具的 Zod Schema 进行强校验。如果在 T 检验的 group\_var 里接收到了一个在 DataContext 中被标记为连续数值的变量,后端必须拦截并返回 400 Bad Request。
## **三、 终极结论与实施调整指南 (Actionable Summary)**
您的计划大体方向非常优秀,不仅提升了可用性,还大幅缓解了 AI 的幻觉焦虑。为了让它完美落地,请在您的开发计划中追加以下 **3 个微小但致命的补丁**
1. **细化前端过滤条件 (UI Filter Patch)**
* 在 WorkflowTimeline.tsx 渲染下拉框时,利用 VariableDictionary 中更精细的属性(如 unique\_values\_count来约束选项。
* 例如:如果是分组变量下拉框,仅高亮展示 type \=== 'categorical' && levels \<= 5 的变量。
2. **同步阻塞执行 (Sync Block Patch)**
* 确保“执行按钮”的 onClick 事件中,严格包含 await patchApi(),并在进行网络请求时将按钮置为 loading 状态,防止连点。
3. **后端的参数防火墙 (Backend Firewall Patch)**
* 在开发 PATCH API 时,务必对传入的 params 进行统计学常识级别的 Zod 校验,防止将脏参数写入数据库,导致后续 R 引擎因 Fatal Error 宕机。
**批示:完全批准按照此计划及上述修正建议执行开发!这会让 SSA-Pro 的专业度再上一个大台阶。**

View File

@@ -0,0 +1,84 @@
# **架构与统计双重视角审查报告:分析方案变量可编辑化** V2 修订版
**审查对象:** plan\_editable\_variables\_27d3a9fd.plan.md 及 团队 UX 修正反馈 **文档状态:** V2 修订版 (采纳“柔性拦截”方案) **审查时间:** 2026-02-23
**总体评级:** 🌟 **A+ 级 (方向极其正确,兼顾了学术严谨与用户掌控感)** **核心裁决:** 批准开发。团队提出的“软提示 \+ 强引导”完美解决了级联失效的体验问题,但必须配合后端的“强防火墙”才能安全落地。
## **一、 视角一:资深统计学专家的评估**
**“不要替医生做决定,但要给医生最专业的警告。”**
### **1\. 极度认可的改进**
* **尊重临床逻辑**AI 经常会把“住院天数”和“年龄”搞混或者漏掉医生特别关心的某个协变量。允许医生在执行前把遗漏的变量Tag加回来这才是真正懂临床的工具。
* **按类型分组展示**:下拉面板将连续变量和分类变量分开展示,极大地降低了医生寻找变量的认知负荷。
### **2\. 统计学视角的隐形陷阱 (The Statistical Blind Spot)**
仅仅区分“分类 (Categorical)”和“连续 (Continuous)”是远远不够的。不同的统计方法对变量有着极其严苛的专属要求:
* **二元 Logistic 回归陷阱**该方法要求结局指标Y**必须且只能**是二分类变量(如:死/活0/1。如果用户选了 3 分类的“血型”,后端的 R 代码将无法计算。
* **T 检验陷阱**:独立样本 T 检验的分组变量X**必须**是二分类变量。如果是 3 分类变量,必须用 ANOVA。
### **🛠️ 统计专家的修正建议:引入“细粒度统计类型过滤” (Soft Filtering)**
前端的下拉框候选项Options不能仅仅根据 type \=== 'categorical' 来过滤,必须**与当前 Step 绑定的统计方法建立映射提示**。
* **建议实现**:前端在渲染下拉框时,读取该统计工具的 params\_schema。对于不完全符合最佳统计条件的变量**不要禁用 (Do not disable)**,但可以在该选项旁打上一个 ⚠️ 标记,提示其可能不适配当前方法。
## **二、 视角二:资深架构师的评估**
**“前端可以极致柔性,后端必须绝对刚性。”**
### **1\. 架构师高度赞赏的决策 (Brilliant Architectural Choices)**
* **后端 PATCH API 的设计 (方案 A)**
这是非常正统的 RESTful 设计。用户修改参数后,先 PATCH /params 更新数据库实体,然后再触发 executeWorkflow。这保证了数据库中的 Plan 永远是 Single Source of Truth单一事实来源避免了前端传来“幽灵参数”导致历史记录无法复现的灾难。
### **2\. 工程视角的潜在风险与柔性化解 (Engineering Risks & Solutions)**
#### **🚨 风险 1UI 状态与执行动作的竞态条件 (Race Condition)**
* **场景**:用户在前端刚从下拉框里切换了变量,还没等组件把 state 同步完毕,或者还没等 PATCH 接口返回 200 OK用户就光速点击了“开始执行分析”按钮。
* **后果**executeWorkflow 可能会拿着数据库里**旧的参数**去执行。
* **修正建议**
“开始执行分析”按钮必须绑定一个复合动作Promise Chaining
async function handleExecute() {
setExecuting(true);
// 1\. 必须先 await 等待 PATCH 成功
if (hasUnsavedChanges) {
await api.patchWorkflowParams(workflowId, modifiedSteps);
}
// 2\. 然后再触发执行
startSseExecution(workflowId);
}
#### **🚨 风险 2级联失效与重新规划的边界 (Cascading Invalidation)**
* **场景**AI 原本规划了 \[描述统计 \-\> T检验 \-\> Mann-Whitney\]。此时,用户在“描述统计”那一步,强行把结局变量从“连续数值”换成了“分类文本”。此时下游的 T检验 已经彻底失去了统计学意义。
* **团队极佳的破局方案(柔性拦截与知情同意)** 绝对**不采用**“硬限制”锁定下拉框(这会引发极大的用户反感)。采用团队设计的\*\*“软提示 \+ 重新规划引导”\*\*机制:
1. **即时反馈**当检测到用户的修改导致变量类型与当前统计方法params\_schema失配时在 StepCard 顶部即时显示黄色警告条:*“⚠️ 当前变量类型已变更,可能导致当前统计方法失效。”*
2. **视觉打标**:在该步骤的卡片右上角亮起一个红/黄警告图标。
3. **阻断与授权弹窗 (Informed Consent)**:如果用户无视警告,强行点击【开始执行分析】,系统**拦截并弹窗***"检测到您修改的变量类型分类变量与当前统计方法T检验不匹配强制执行可能导致报错或结论无效。建议您在对话框告诉 AI 重新生成方案。是否仍要强行执行?"* \[ 取消并重新对话 \] \[ 强行执行 \]
* **架构师点评**:这种设计堪称完美。把控制权给用户,把免责声明做足。
#### **🚨 风险 3Zod Schema 的后端防御底线 (Backend Defense) \- 生死线**
* **场景**:既然前端允许用户点击“强行执行”,那么非法的参数就一定会穿透到后端。
* **架构底线**
新增的 PATCH /api/v1/ssa/workflow/:workflowId/params 接口,**绝对不能因为接收到了脏数据而导致 Node.js 崩溃**。 必须使用对应 R 工具的 Zod Schema 进行校验。如果接收到了离谱的参数(比如把一个字符串数组传给了要求 Boolean 的字段),后端必须捕获并转化为优雅的 400 Bad Request如果参数类型合法但统计学不合法放行给 R 引擎,由 R 引擎内部的 tryCatch 捕获并返回给前端清晰的 Error Log 即可。
## **三、 终极结论与实施调整指南 (Actionable Summary)**
团队对于 UX 交互边界的把握非常高级,"软提示+强引导"方案完美化解了系统的刻板印象。
为了让计划完美落地,请在开发中落实以下 3 个关键动作:
1. **前端交互柔性化 (UI Soft Filter)**
* 实现黄条警告和“强行执行确认弹窗”。这需要前端在渲染时,将当前选择的变量类型与工具的 params\_schema 需求类型进行实时比对计算。
2. **同步阻塞执行 (Sync Block Patch)**
* 确保“执行按钮”的 onClick 事件中,严格包含 await patchApi(),并在进行网络请求时将按钮置为 loading 状态。
3. **后端的参数防火墙 (Backend Firewall Patch)**
* 为 PATCH API 建立坚固的 Zod 校验,确保前端传来的强行覆盖数据,最多只会导致 R 引擎的“业务计算报错”,而绝对不会导致 Node.js 服务的“系统级崩溃”。
**批示:完全批准按照此修订计划执行开发!在赋予用户自由的同时守住后端的安全底线,这套交互将成为医疗 SaaS 的标杆。**