# SSA-Pro Q-P-E-R 架构开发计划 — 智能化主线 > **文档版本:** v7.1 > **创建日期:** 2026-02-20 > **最后更新:** 2026-02-21(v7.1 — 新增核心原则"领域知识可配置化" + Phase P 配置化基础设施:Zod 校验 / 热更新 API / 领域文件拆分) > **架构模式:** Query → Planner → Execute → Reflection > **目标:** 让不懂统计的医生完成专业级的统计分析 > **本文档定位:** **替代旧 MVP 开发计划总览**,成为 SSA 开发的主线指南 --- ## 1. 架构总览 ### 1.1 Q-P-E-R 四层架构 ``` 用户:"我有 200 个患者数据,想看看新药有没有效" │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Q · Query Layer (理解层) │ │ "用户到底想要什么?数据长什么样?" │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ IntentParser │→ │ DataProfiler │→ │ Clarifier │ │ │ │ LLM 意图 │ │ 数据诊断 │ │ 追问澄清 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ 输出:ParsedQuery { goal, y, x, design, dataProfile } │ └──────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ P · Planner Layer (规划层) │ │ "该用什么方法?按什么顺序?" │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │DecisionTable │→ │FlowTemplate │→ │ SAP Builder │ │ │ │ 四维匹配 │ │ 流程模板 │ │ 计划生成 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ 输出:AnalysisPlan { steps[], methodology, rationale } │ └──────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ E · Execute Layer (执行层) ✅ 已完成 │ │ "调用 R 引擎,跑出数字" │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Orchestrator│→ │ R Client │→ │ SSE Stream │ │ │ │ 步骤编排 │ │ 调用 R 引擎 │ │ 实时进度 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ 输出:StepResult[] { stats, tables, plots, code } │ └──────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ R · Reflection Layer (审视层) │ │ "结果说明了什么?可以发论文吗?" │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Integrator │→ │ LLM Critic │→ │ReportBuilder │ │ │ │ 结果整合 │ │ 论文级结论 │ │ 报告生成 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ 输出:Report { conclusion, methodology, limitations } │ └─────────────────────────────────────────────────────────┘ ``` ### 1.2 与现有代码的映射 | Q-P-E-R 层 | 现有代码 | 现状 | 目标 | |------------|---------|------|------| | **Q · Query** | `WorkflowPlannerService.parseUserIntent()` | 正则匹配,无 LLM | LLM 四维提取 + 追问 | | **Q · Query** | `DataProfileService` + `DataParserService` | 基础统计 | 增加诊断项 + 传递给 P 层 | | **P · Planner** | `WorkflowPlannerService.generateSteps()` | 硬编码 if/else | 决策表 + 流程模板 | | **E · Execute** | `WorkflowExecutorService` + `RClientService` | **✅ 完成** | 保持不变 | | **E · Execute** | 7 个 R 工具 + SSE + 前端展示 | **✅ 完成** | Block-based 标准化输出 | | **R · Reflection** | `ConclusionGeneratorService` | 规则拼接,无 LLM | LLM 论文级结论 | ### 1.3 核心原则 1. **Execute 层不动** — 这是花了最多时间搭建的基础,完整保留 2. **前后加层** — 在 Execute 前面加更好的 Q+P,后面加 R 3. **LLM 只在 Q 和 R 层使用** — P 层用确定性的决策表,E 层用确定性的 R 引擎 4. **渐进式替换** — 每层完成后立即可用,不需要全部完成才能看到效果 5. **复用平台通用能力层,禁止重复造轮** — - LLM 调用 **必须** 通过 `common/llm/LLMFactory` + `common/streaming/StreamingService` - Prompt 管理 **必须** 使用 `common/prompt/PromptService`(数据库版本化 `capability_schema.prompt_templates`),禁止在代码中硬编码 Prompt 字符串 - 缓存使用 `common/cache/CacheService`(Postgres-Only),日志使用 `common/logging/` - 参考 `docs/04-开发规范/08-云原生开发规范.md` 和 `docs/02-通用能力层/00-通用能力层清单.md` 6. **⭐ 领域知识可配置化 — QPER 四层的所有"领域知识"由统计学方法学团队配置,不写死在代码中** — > IT 团队负责搭建引擎和管道,统计学方法学团队负责往管道里填充内容。"可配置"是贯穿所有 Phase 的架构约束。 **约束:一切业务逻辑靠读 JSON/数据库驱动,绝不写死在 TypeScript 的 if-else 和常量对象中。** | QPER 层 | 方法学团队可配置的内容 | 配置载体 | |---------|----------------------|---------| | **Q 层** | Intent Prompt(Few-Shot 示例、Confidence Rubric、Goal 类型枚举) | `PromptService` 数据库(✅ 已实现) | | **P 层** | 决策表规则、流程模板、Switch Condition、EPV 阈值 | JSON 文件 → Repository 接口(Phase P) | | **E 层** | R 工具注册表(100 个工具的参数定义、前置条件、护栏规则) | JSON 文件 `tools_registry.json`(Phase P 创建) | | **R 层** | 结论模板、方法学说明模板、统计量阈值定义 | JSON 文件 `narrative_templates.json`(Phase R 创建) | **配置化三道防线(Phase P 落地):** 1. **Zod Schema 严格校验** — 方法学团队编辑 JSON 时的拼写错误、结构错误在加载时立即拦截,不传播到运行时 2. **热更新 API** — 方法学团队修改 JSON 后无需 IT 团队重启服务,调用 `POST /reload-config` 即可生效(校验失败则保留旧配置) 3. **领域文件物理拆分** — 按职责拆为独立 JSON 文件,避免版本冲突,每个文件有明确的所有者 ``` backend/src/modules/ssa/config/ ├── tools_registry.json # E 层:100 个工具的注册定义(方法学团队维护) ├── decision_tables.json # P 层:四维匹配规则 + 降级触发条件(方法学团队维护) ├── flow_templates.json # P 层:标准步骤流组合(方法学团队维护) └── narrative_templates.json # R 层:结论生成和理由说明的文案模板(Phase R 创建) ``` ### 1.4 架构红线(不可为清单) > **来源:** 四份外部调研报告 + 架构审查共识。以下三条为强制约束,任何 Phase 均不可违反。 | # | 红线 | 理由 | |---|------|------| | 🚫 1 | **禁止引入 LangGraph / AutoGen 等编排框架** | QPER 是线性流水线 + 重试,Node.js 原生 `while` + `try-catch` 足够编排,框架只增加复杂度和调试成本 | | 🚫 2 | **禁止 LLM 生成并执行 R 代码** | 只允许 LLM 修改**参数 JSON** 重新请求固定 R 脚本(`r-statistics-service/tools/`),杜绝远程代码执行风险。R 工具是参数化封装的固定脚本,不是 LLM 的画布 | | 🚫 3 | **禁止多智能体辩论(Multi-Agent Debate)** | 统计学有硬性定理(正态性 P<0.05 就是不满足),用决策表规则判定即可,不需要两个 LLM 互相消耗 Token | --- ## 2. 当前系统资产盘点 ### 2.1 可直接复用(不改动) | 资产 | 文件 | 状态 | |------|------|------| | R 统计引擎 | `r-statistics-service/` (7 个工具) | ✅ | | R 调用客户端 | `RClientService.ts` | ✅ | | 工作流执行器 | `WorkflowExecutorService.ts` | ✅ | | SSE 实时通信 | `workflow.routes.ts` | ✅ | | 数据上传 + OSS | 会话管理 + 文件上传路由 | ✅ | | 前端 V11 UI | SSAWorkspace 全套组件 | ✅ | | 前端执行日志 | terminal-box + TraceLogItem | ✅ | | 前端结果展示 | 统计量/表格/图表渲染 | ✅ | | R 代码导出 | SSACodeModal (多步骤聚合) | ✅ | | Word 报告导出 | useAnalysis.exportWorkflowReport | ✅ | | Python 数据质量 | DataProfileService + extraction_service | ✅ | ### 2.2 需要拆分重构 | 现有代码 | 拆分为 | 层级 | |---------|--------|------| | `WorkflowPlannerService.parseUserIntent()` | `QueryService.parseIntent()` | Q | | `WorkflowPlannerService.generateSteps()` | `PlannerService.plan()` | P | | `AVAILABLE_TOOLS` 硬编码对象 | `DecisionTable` 配置文件 | P | | `ConclusionGeneratorService`(已有,规则拼接) | `ReflectionService`(LLM 升级) | R | > **迁移策略:** `WorkflowPlannerService` 不立即删除,而是在内部委托给新的 QueryService / PlannerService,保留旧入口作为兼容层,待 Q+P 层稳定后再移除。 ### 2.3 需要新建 | 新建服务 | 层级 | 核心能力 | |---------|------|---------| | `QueryService` | Q | LLM 意图解析 + 追问(通过平台 `LLMFactory` + `PromptService`) | | `DataDiagnosticService` | Q | 异常值/正态性/平衡性检测(扩展现有 `DataProfileService`) | | `DecisionTableService` | P | 四维匹配 | | `FlowTemplateService` | P | 流程模板管理 | | `DynamicReport.tsx` | E(前端) | Block-based 动态渲染 | > **注意:** `ReflectionService` 不是新建,而是对已有 `ConclusionGeneratorService` 的 LLM 升级重构(见 2.2)。 ### 2.4 已有数据库资产(Prisma schema `ssa_schema`) | 模型 | 状态 | 说明 | |------|------|------| | `SsaSession` / `SsaMessage` / `SsaFileUpload` | ✅ 使用中 | 会话管理 + 消息 + 文件上传 | | `SsaDecisionTable` | ✅ 已建模 | 决策表记录,`DecisionTableService` 应优先使用此表 | | `SsaRCodeLibrary` | ✅ 已建模 | R 代码库记录 | | `SsaAnalysisResult` | ✅ 已建模 | 分析结果记录(含 `report_blocks` 字段)| > Phase P 的决策表配置应评估是否直接使用 `SsaDecisionTable` 数据库表,而非仅用静态 JSON 文件。 --- ## 3. 开发计划(5 个 Phase) ### Phase E+ : Execute 层标准化 — Block-based(2.5 天) **为什么先做这个:** 后续每新增一个 R 工具,前端都需要写自定义渲染代码。Block-based 协议让这个成本归零,是所有后续开发的前提。 ``` 目标:R 输出 report_blocks[] → Node.js 透传 → 前端 DynamicReport 渲染 ``` | # | 任务 | 层级 | 工时 | 产出 | |---|------|------|------|------| | 1 | R 端辅助函数库 `block_helpers.R` | R | 1h | `make_table_block()`, `make_kv_block()`, `make_image_block()` | | 2 | 改造 `t_test_ind.R` 输出 `report_blocks` | R | 1h | 第一个 Block-based 工具 | | 3 | 改造其余 6 个 R 工具 | R | 6h | 全部输出 `report_blocks` | | 4 | 后端透传 `report_blocks` 全链路 | 后端 | 1.5h | `WorkflowExecutorService` → SSE 事件 → 前端 store;需验证 `SsaAnalysisResult` 表 `report_blocks` 字段持久化 | | 5 | 前端 `DynamicReport.tsx` 组件 | 前端 | 2h | 4 个 Block 渲染子组件 | | 6 | 前端 `exportBlocksToWord.ts` | 前端 | 2h | Block 数组 → Word 文档 | | 7 | `SSAWorkspacePane` 集成 + fallback | 前端 | 1h | 优先读 `report_blocks`,旧逻辑兜底 | | 8 | 旧渲染代码保留为 fallback | 前端 | 1h | `DescriptiveResultView` 等组件(2026-02-20 新建)暂保留,当 `report_blocks` 为空时自动降级使用;待 Block-based 全面验证后再清理 | **验收标准:** 所有 7 个 R 工具的结果通过 `DynamicReport` 统一渲染,Word 导出正常。 --- ### Phase Q : Query 层建设 — LLM 意图理解(4 天) **为什么第二做:** 这是用户体验提升最大的单一改进。从"用户必须知道 T 检验"变为"用户说'有没有效'就够了"。 ``` 目标:用户自然语言 → LLM 提取四维信息 → 不确定时追问 ``` #### 3.1 后端任务 | # | 任务 | 工时 | 说明 | |---|------|------|------| | 1 | 通过 `PromptService` 注册 Intent Prompt | 5h | 编写 Prompt + seed 脚本写入 `capability_schema.prompt_templates`;输出:`{ goal, outcome_var, predictor_vars, design, confidence }`。**必须包含 3-5 组 Few-Shot 示例**(覆盖"哪个组更好"→差异比较、"血压和体重有关吗"→关联分析等典型口语表达),显著提升首次解析准确率。**Confidence Rubric 客观化**:Prompt 中必须内嵌打分准则(0.9+ = 用户原话明确了 Y 和至少一个 X;0.7-0.8 = 指出了 Y 但 X 需推断;< 0.7 = 只说了"帮我分析"需追问),防止 LLM 自评虚高 | | 2 | 新建 `QueryService.ts` | 8h | 通过 `LLMFactory.create()` 调用 LLM + json-repair + Zod 校验 + 低置信度追问逻辑 + 正则兜底(保留现有 `parseUserIntent` 逻辑作为 fallback)。**Zod 动态防幻觉**:用 `createIntentSchema(validColumns)` 动态生成 Schema,通过 `.refine()` 校验 LLM 输出的 `outcome_var`/`predictor_vars` 是否存在于真实列名中,捏造列名立即触发重试或 fallback。**Confidence 二次验证**:若 LLM 打分 ≥0.9 但 outcome_var 或 predictor_vars 为空,强制降级到 0.5 | | 3 | 增强 `DataProfileService` | 4h | 增加异常值检测(IQR)、分组平衡性、样本量评估;注意:DataParserService 的 0/1 分类规则已在 2026-02-19 优化完成,不重复。**非分析变量自动标记**:Python 端对高基数字符串列(uniqueValues/totalRows > 0.95)、日期列、列名匹配 `_id/_no/编号/序号` 模式的列打 `is_id_like: true` 标签;Q→P 上下文裁剪时物理剔除这些列,让 LLM 看不到 patient_id 等非分析列 | | 4 | 定义 `ParsedQuery` 接口 | 1h | Q 层输出 → P 层输入的标准契约,放 `types/query.types.ts` | | 5 | 新建追问 API | 2h | `POST /api/v1/ssa/sessions/:id/clarify`,在现有 `analysis.routes.ts` 中扩展 | | 6 | 安装 `json-repair` + `zod` 依赖 | 0.5h | Phase 1 遗留任务,此处一并完成 | | 7 | **Q→P 上下文裁剪(Context Pruning)** | 1h | Q 层识别出 Y/X 变量后,只提取这几列的 DataProfile 子集传给 P 层(Hot Context);其余列的详情留在数据库按需拉取(Cold Context)。防止 100 列 CSV 的 50KB DataProfile JSON 导致 Planner 的 Token 爆炸和"注意力涣散" | | 8 | **DataProfile 会话级缓存** | 0.5h | 用户上传文件后 DataProfiler 只执行一次,结果通过 `CacheService` 缓存(key: `ssa:profile:{sessionId}:{fileHash}`)。同一会话+同一文件的后续 Q 层循环(如追问后重新解析)直接读缓存,实现毫秒级响应,避免重复调用 Python 解析 20MB CSV | **QueryService 核心逻辑:** ```typescript // 伪代码 — 使用平台通用能力层 class QueryService { constructor( private promptService: PromptService, // 平台通用能力:Prompt 管理 private llmFactory: LLMFactory, // 平台通用能力:LLM 网关 ) {} async parseQuery(userText: string, dataProfile: DataProfile): Promise { // 1. 从数据库加载 Prompt 模板(支持版本化 + A/B 测试) const promptTemplate = await this.promptService.getPrompt('ssa_intent_parse', 'latest'); const prompt = this.promptService.render(promptTemplate, { userText, variables: dataProfile.variables }); // 2. 通过 LLMFactory 调用 LLM(自动选模型 + 统一错误处理) const llm = this.llmFactory.create({ model: 'deepseek-v3', temperature: 0.1 }); const llmResult = await llm.chat(prompt); // 3. Zod 校验结构(json-repair 容错) const parsed = IntentSchema.parse(jsonRepair(llmResult)); // 4. 低置信度 → 生成追问 if (parsed.confidence < 0.7) { return { needsClarification: true, questions: this.generateQuestions(parsed) }; } // 5. 融合数据诊断结果 const diagnosis = await this.diagnoseData(dataProfile, parsed); // 6. 上下文裁剪(Context Pruning)— 只保留 Y/X 变量的 DataProfile 子集 // Planner 不需要看 100 列的完整统计特征,只需相关变量的 Hot Context const prunedProfile = this.pruneForPlanner(dataProfile, parsed); return { ...parsed, dataDiagnosis: diagnosis, prunedProfile, needsClarification: false }; } // 上下文裁剪:从全量 DataProfile 中提取 Planner 需要的最小子集 private pruneForPlanner(fullProfile: DataProfile, parsed: ParsedQuery): PrunedProfile { const relevantVars = [parsed.outcome_var, ...parsed.predictor_vars]; return { schema: fullProfile.variables.map(v => ({ name: v.name, type: v.type })), // 全部列的类型(轻量) details: fullProfile.variables.filter(v => relevantVars.includes(v.name)), // 只有 Y/X 的详细统计 sampleSize: fullProfile.sampleSize, missingRateSummary: fullProfile.overallMissingRate, }; } // Fallback:LLM 调用失败时降级到现有正则匹配(WorkflowPlannerService.parseUserIntent) private async fallbackToRegex(userText: string, variables: Variable[]): Promise { ... } } ``` #### 3.2 前端任务 | # | 任务 | 工时 | 说明 | |---|------|------|------| | 6 | 追问卡片组件 `ClarificationCard.tsx` | 3h | **必须是封闭式数据驱动选择题**,禁止开放式提问。后端基于 DataProfile 变量列表预生成 2-3 个具有统计学意义的分析假设,前端渲染为可点击的快捷 Tag(如:"检测到 [Drug] 和 [BP],您是想:👉[比较差异] 👉[相关分析]")。错误示范:`"请问您想分析哪两列?"` | | 7 | `SSAChatPane` 集成追问流程 | 2h | 收到 `needsClarification` 时展示卡片;用户点击选项后自动填充 ParsedQuery 缺失字段,重新提交 | | 8 | 数据诊断增强展示 | 2h | `DataProfileCard` 增加异常值/平衡性信息 | #### 3.3 Prompt 设计要点 > **存储方式:** 以下 Prompt 通过 seed 脚本写入 `capability_schema.prompt_templates` 表,`template_key = 'ssa_intent_parse'`。运行时通过 `PromptService.getPrompt()` 加载,**禁止硬编码在 TypeScript 中**。 ``` 你是一位临床研究统计顾问。请从用户的描述中提取以下信息: 1. goal: 分析目的 - "difference": 比较差异("有没有效"、"是否不同"、"比较") - "association": 关联分析("相关"、"影响因素"、"关系") - "prediction": 预测建模("预测"、"风险"、"预后") - "description": 描述统计("描述"、"特征"、"分布") - "cohort_study": 队列研究全套分析("出一套完整报告"、"基线+单因素+多因素"、"Table 1-3"、"队列研究") 2. outcome_var: 结局变量(Y),从变量列表中识别 3. predictor_vars: 自变量/分组变量(X),从变量列表中识别 4. design: 实验设计 - "independent": 独立样本(不同的人比较) - "paired": 配对设计(同一患者前后比较) 5. confidence: 0.0-1.0,你对以上解析的确信程度 **重要规则:** - 请自动忽略明显的非分析变量(如 patient_id、姓名、病历号、病床号、录入日期等标识/管理类字段),不要将它们选为 outcome_var 或 predictor_vars。 - 如果变量列表中附有用户标注的中文含义(variable_description),优先参考含义而非列名进行角色判断。 可用变量列表:{{variables}} 用户查询:{{query}} ``` **验收标准:** - "分析 sex 与 Yqol 的相关性" → `{ goal: "association", outcome: "Yqol", predictors: ["sex"], confidence: 0.9 }` - "有没有效" → `{ goal: "difference", confidence: 0.3, needsClarification: true, questions: [...] }` - 追问**必须是选择题**:`"检测到数据包含 [Drug](分类) 和 [BP](连续),您是想:A. 比较用药组 vs 对照组的 BP 差异 B. 分析 Drug 与 BP 的相关性"` - 禁止开放式提问如 `"请问您想比较哪个指标?"` - `prunedProfile` 只包含 Y/X 变量的详细统计(非 100 列全量),schema 字段包含全部列名+类型 - **探路测试(Tracer Bullet):** 后端需提供 `test_query_pipeline.ts` 脚本,用 mock DataProfile 验证 Q 层输出结构正确,无需前端和 R 服务 --- ### Phase P : Planner 层重构 — 决策表 + 流程模板(4 天) **为什么第三做:** 让系统从"单方法执行"跃迁到"完整分析流程"。用户会看到一个包含 4-6 个步骤的专业分析计划。 ``` 目标:ParsedQuery → 决策表匹配 → 流程模板填充 → 完整 SAP ``` #### 3.4 决策表设计 **核心思想:一张表解决"该用什么方法"的问题。** > **v7.0 增强:** ① 新增 `Switch Condition` 列 — 描述 Primary/Fallback 互换的触发条件,让方法切换从硬编码变为配置化;② **参数检验优先原则** — Primary 始终设为参数检验(统计效力更高),非参数方法作为 Fallback(安全网),最终裁决权交给 R 引擎的数据分布检验。 | Goal | Y_Type | X_Type | Design | Primary Tool | Fallback Tool | Switch Condition | |------|--------|--------|--------|-------------|---------------|-----------------| | difference | continuous | categorical_2 | independent | ST_T_TEST_IND | ST_MANN_WHITNEY | `normality_fail`: Shapiro-Wilk P<0.05 | | difference | continuous | categorical_2 | paired | ST_T_TEST_PAIRED | — | — | | difference | continuous | categorical_multi | independent | ST_ANOVA_ONE | ST_KRUSKAL | `normality_fail`: Shapiro-Wilk P<0.05 | | difference | categorical | categorical | independent | ST_CHI_SQUARE | ST_FISHER | `expected_freq_low`: 期望频数<5 | | association | continuous | continuous | — | ST_CORRELATION | — | — | | association | categorical | any | — | ST_CHI_SQUARE | ST_FISHER | `expected_freq_low`: 期望频数<5 | | prediction | categorical_binary | any | — | ST_LOGISTIC | — | — | | prediction | continuous | any | — | ST_LINEAR_REG | — | — | | description | any | any | — | ST_DESCRIPTIVE | — | — | | **cohort_study** | **categorical_binary** | **categorical** | **independent** | **ST_BASELINE_TABLE** | — | — | > **`cohort_study` 场景说明:** 当 Q 层识别到用户意图为"队列研究全套分析"(触发词:"出一套完整报告"、"基线比较+单因素+多因素"、"Table 1-3"等)时,不走单工具匹配,而是直接选用 `cohort_study_standard` 流程模板。Q 层 `ParsedQuery.goal` 需扩展支持 `'cohort_study'` 值。 #### 3.4.1 决策表存储策略 — Repository 模式 > **v7.0 新增架构决策:** 决策表初期使用 JSON 文件(内容稳定、不需 A/B 测试),但通过 Repository 接口解耦,后期可无缝切换到数据库。 ```typescript // Repository 接口 — 核心业务逻辑只依赖此接口 interface IDecisionTableRepo { getRules(): Promise; } // Phase P 实现:JSON 文件 class JsonDecisionTableRepo implements IDecisionTableRepo { async getRules() { return decisionTableJson as DecisionRule[]; } } // 预留 Phase Deploy 实现:数据库(SsaDecisionTable 模型已建好) class PgDecisionTableRepo implements IDecisionTableRepo { async getRules() { return await prisma.ssaDecisionTable.findMany(); } } ``` #### 3.4.2 Expected vs Actual 双层审计日志 > **v7.0 新增架构决策:** P 层规划时**不做正态性预检**(正态性检验只在 R 引擎中执行),而是生成"策略日志 (Planned Trace)"。E 层执行后生成"事实日志 (Actual Trace)"。R 层合并两者生成方法学说明。 ``` P 层(策略)──→ PlannedTrace: { primary: "T-Test", fallback: "Wilcoxon", switchCondition: "Shapiro P<0.05" } ↓ 前端 SAP 卡片: "将执行 T 检验。🛡️护栏:若正态性不满足,将自动降级为 Wilcoxon。" ↓ E 层(事实)──→ R trace_log: { check: "normality", result: "fail", action: "switched to Wilcoxon" } ↓ R 层(合并)──→ 结合 PlannedTrace + trace_log 生成论文级方法学说明 ``` **PlannedTrace 类型定义:** ```typescript interface PlannedTrace { matchedRule: string; // "Goal=difference, Y=continuous, X=categorical_2, Design=independent" primaryTool: string; // "ST_T_TEST_IND" fallbackTool: string | null; // "ST_MANN_WHITNEY" switchCondition: string | null; // "normality_fail: Shapiro-Wilk P<0.05" templateUsed: string; // "standard_analysis" reasoning: string; // 人类可读的方法选择理由 } ``` > **原则:** P 层只描述"如果…则…"的策略,**绝不在 Node.js 中做正态性预检**。R 引擎已有完整的分布检验 + 自动降级逻辑,它的 `trace_log` 天然就是 Actual Trace。 #### 3.5 流程模板设计 **核心思想:每种分析目的对应一个标准流程。** > **v7.0 优化:** 将 `two_group_comparison` 和 `association_analysis` 合并为 `standard_analysis`(结构相同,区别仅在决策表填充的工具不同)。最终为 **4 个通用模板 + 1 个队列研究专用模板**。 ```typescript const FLOW_TEMPLATES = { // 通用三步模板 — 适用于 comparison / correlation / association "standard_analysis": { name: "标准分析流程", steps: [ { order: 1, role: "descriptive", tool: "ST_DESCRIPTIVE" }, { order: 2, role: "primary_test", tool: "{{matched_tool}}" }, { order: 3, role: "sensitivity", tool: "{{fallback_tool}}", condition: "fallback_exists" }, ] }, // 配对设计专用 "paired_analysis": { name: "配对设计分析", steps: [ { order: 1, role: "descriptive", tool: "ST_DESCRIPTIVE" }, { order: 2, role: "primary_test", tool: "{{matched_tool}}" }, ] }, // 回归建模 "regression_analysis": { name: "回归建模", steps: [ { order: 1, role: "descriptive", tool: "ST_DESCRIPTIVE" }, { order: 2, role: "primary_test", tool: "{{matched_tool}}" }, ] }, // 纯描述统计 "descriptive_only": { name: "描述性统计", steps: [ { order: 1, role: "descriptive", tool: "ST_DESCRIPTIVE" }, ] }, // v6.0 新增:队列研究全套分析(覆盖经典 Table 1 → Table 2 → Table 3) "cohort_study_standard": { name: "经典队列研究全套分析", steps: [ { order: 1, role: "baseline_table", tool: "ST_BASELINE_TABLE", name: "表1: 组间基线特征比较", params_mapping: { group_var: "{{exposure_var}}", analyze_vars: "{{all_covariates}}" } }, { order: 2, role: "univariate_screen", tool: "ST_BASELINE_TABLE", name: "表2: 结局指标单因素分析", params_mapping: { group_var: "{{outcome_var}}", analyze_vars: "{{all_covariates}}" } }, { order: 3, role: "multivariate_reg", tool: "ST_LOGISTIC_BINARY", name: "表3: 多因素 Logistic 回归", params_mapping: { outcome_var: "{{outcome_var}}", predictors: "{{epv_capped_predictors}}" } }, ] } }; ``` #### 3.5.1 队列研究 Table 3 — "全量推入 + EPV 防护 + 免责声明"策略 > **v7.0 新增架构决策:** Phase Q+ 的变量选择面板尚未上线前,Table 3 的自变量列表采用**全量推入**策略,辅以 EPV 上限防护。 **EPV(Events Per Variable)防护逻辑:** ``` 可纳入变量数 = floor( min(outcome=0的数量, outcome=1的数量) / 10 ) ``` - 如果 Q 层给出 20 个 predictor,但 EPV 只允许 5 个 → P 层自动截断到前 5 个 - 截断优先级:按 DataProfile 中与 outcome 的关联强度排序(分类变量用卡方统计量,连续变量用 T/Mann-Whitney 统计量) - 前端 SAP 卡片标注红色 Tag: **`[AI 自动探索模式]`** - 免责文案:"当前多因素回归模型纳入了 AI 自动选择的变量(受样本量限制已自动筛选),在后续版本中您将可以手动调整变量。" > **Phase Q+ 上线后:** `{{epv_capped_predictors}}` 替换为 `{{user_confirmed_predictors}}`,EPV 防护仍保留作为兜底。 #### 3.6 后端任务 > **v7.1 调整:** 新增任务 0(配置化基础设施)和任务 1(工具注册表),原有任务顺延。所有领域知识从代码常量迁移到 JSON 文件,通过 Repository + Zod 校验加载。 | # | 任务 | 工时 | 说明 | |---|------|------|------| | 0 | **配置化基础设施:ConfigLoader + Zod Schema + 热更新 API** | 2h | ① 通用 `ConfigLoader` 基类(读 JSON + Zod 校验 + 内存缓存);② `POST /api/v1/system/reload-config`(管理员权限,校验失败保留旧配置,返回 400 + 错误详情);③ 四个领域 JSON 文件的 Zod Schema 定义 | | 1 | **创建工具注册表 `tools_registry.json`** | 2h | 将 `AVAILABLE_TOOLS` 硬编码常量迁移为 JSON 文件。每个工具定义包含:`code`, `name`, `category`, `description`, `inputParams`(含 Zod 类型), `outputType`, `prerequisite`, `fallback`。通过 `ToolRegistryService` + Repository 加载 | | 2 | 创建决策表配置 `decision_tables.json` | 2h | 四维匹配规则的 JSON 版本,含 `switchCondition` 字段 | | 3 | 新建 `DecisionTableService.ts` | 4h | 通过 `IDecisionTableRepo` 接口加载(Phase P: JSON 实现),四维匹配逻辑 | | 4 | 创建流程模板配置 `flow_templates.json` | 2h | 4+1 个标准模板 | | 5 | 新建 `FlowTemplateService.ts` | 4h | 模板选择 + 参数填充(含 EPV 截断逻辑) | | 6 | 重构 `PlannerService.ts` | 4h | 接收 ParsedQuery → 调用决策表 → 选模板 → 生成 SAP + PlannedTrace | | 7 | 前端 SAP 确认卡片增强 | 3h | 显示每步方法、选择理由、护栏说明、`[AI 自动探索模式]` 标签 | | 8 | 联调测试 | 3h | Q → P → E 全链路 + Tracer Bullet 脚本 | > **配置化校验流程:** > ``` > 方法学团队修改 JSON → 调用 POST /reload-config → Zod 校验 > ├── 通过 → 刷新内存缓存 → 返回 200 ✅ 新规则生效 > └── 失败 → 保留旧配置 → 返回 400 + 详细错误信息 ❌ 服务不受影响 > ``` **PlannerService 核心逻辑(v7.0 更新):** ```typescript // 伪代码 — 体现 Repository 模式 + Expected/Actual 双层日志 class PlannerService { constructor( private decisionTableRepo: IDecisionTableRepo, // Repository 模式:JSON 或 DB private flowTemplateService: FlowTemplateService ) {} async plan(query: ParsedQuery, profile?: DataProfile): Promise { // 1. 决策表匹配 → 主方法 + 备选方法(通过 Repository 加载规则) const rules = await this.decisionTableRepo.getRules(); const match = this.matchRule(rules, query.goal, query.outcome_type, query.predictor_types, query.design); // 2. 选择流程模板 const template = this.flowTemplateService.select(query.goal, query.design, match); // 3. 填充参数(含 EPV 防护) const steps = this.flowTemplateService.fill(template, { matched_tool: match.primaryTool, fallback_tool: match.fallbackTool, outcome_var: query.outcome_var, predictor_vars: query.predictor_vars, grouping_var: query.grouping_var, profile, }); // 4. 生成 PlannedTrace(策略日志 — P 层只描述"如果…则…") const plannedTrace: PlannedTrace = { matchedRule: `Goal=${query.goal}, Y=${query.outcome_type}, X=${query.predictor_types[0]}, Design=${query.design}`, primaryTool: match.primaryTool, fallbackTool: match.fallbackTool, switchCondition: match.switchCondition, // "normality_fail: Shapiro-Wilk P<0.05" templateUsed: template.id, reasoning: this.explainMethodChoice(match, query), }; // 注意:Actual Trace 由 E 层的 R trace_log 提供,P 层不做正态性预检 // 5. 生成 SAP return { steps, plannedTrace, methodology: plannedTrace.reasoning }; } } ``` **验收标准(v7.0 更新):** - 输入 `{ goal: "comparison", outcome_type: "continuous", predictor_types: ["binary"], design: "independent" }` - 输出 3 步流程:描述统计 → T 检验(Primary) → Mann-Whitney(Sensitivity,条件展示) - `plannedTrace` 包含 `switchCondition: "normality_fail"` + `reasoning` 人类可读理由 - 前端 SAP 卡片每步显示选择理由;有 fallback 的步骤显示护栏说明("若正态性不满足,将自动降级为…") - **P 层不做正态性预检**,方法切换由 R 引擎在执行时根据数据分布自行决定 - **队列研究场景**:输入 `{ goal: "cohort_study" }` → 输出 Table 1/2/3 三步流程,Table 3 自变量经 EPV 截断 + `[AI 自动探索模式]` 标签 - **探路测试(Tracer Bullet):** 后端需提供 `test_planner_pipeline.ts` 脚本,用 mock ParsedQuery 验证 P 层输出结构正确(不依赖前端和 R 服务)。Q→P 串联测试也必须通过 --- ### Phase R : Reflection 层建设 — LLM 论文级结论(3 天) **为什么第四做:** 这是让输出从"P=0.015"变成"可以直接用于论文"的关键。 ``` 目标:StepResult[] → LLM 综合解读 → 论文级结论 + 方法学说明 ``` #### 3.7 后端任务 | # | 任务 | 工时 | 说明 | |---|------|------|------| | 1 | 通过 `PromptService` 注册 Reflection Prompt | 3h | seed 脚本写入 `capability_schema.prompt_templates`,`template_key = 'ssa_reflection'`;输出:6 要素结论。**统计量通过槽位注入,禁止 LLM 生成数值**。**含敏感性分析冲突处理准则(3.10 节)** | | 2 | 重构 `ConclusionGeneratorService` → `ReflectionService` | 6h | 通过 `LLMFactory` 调用 LLM + **Zod 强校验 LLM 输出结构(3.11 节)** + 结果整合。接收 `decision_trace` 生成方法学说明。LLM 输出完整收集后校验,不做字符级流式推送(3.12 节) | | 3 | **E 层运行时崩溃优雅处理**(重试机制推迟至 Phase Deploy) | 1h | 错误分类映射表(NA 值/列名缺失/R Fatal Error → 友好用户提示),不做跨层 Self-healing。**完整重试机制(MAX_RETRIES=2 + 参数级修复 + 方法级降级)推迟至 Phase Deploy**(见 3.8 节详述) | | 4 | 结论缓存(`CacheService`)+ API | 2h | `GET /sessions/:id/conclusion`,使用平台 `CacheService` 缓存结论避免重复 LLM 调用 | | 5 | 前端论文结论展示 | 4h | Markdown 渲染 + 一键复制 + 折叠/展开;**采用"完整 JSON + 逐 section 渐入动画"方案(3.12 节)**,不做字符级流式推送 | | 6 | Word 报告增强 | 3h | 纳入 LLM 结论(替代当前的简单 summary) | | 7 | 联调测试 | 3h | Q → P → E → R 完整链路 | > **重要:** `ReflectionService` 是对现有 `ConclusionGeneratorService` 的升级重构,不是从零新建。保留 `ConclusionGeneratorService` 作为 fallback 入口,LLM 调用失败**或 Zod 校验失败**时降级到规则拼接。 #### 3.8 E 层运行时错误处理策略 > **来源:** 《QPER架构审查与工程避坑指南》暗礁 2 + 架构委员会裁决。 > > **关键区分:** "统计降级"(如正态性不满足自动切非参数)由 R 脚本内部 if-else 处理,**不是**运行时错误。"运行时崩溃"(如 NA 值导致 R Fatal Error、列名隐形空格、数据结构异常)才需要 Node.js 层处理。 **Phase R 当前实施(优雅错误分类):** 现有 `WorkflowExecutorService` 已具备步骤级 try-catch + `step_error` SSE 推送能力。Phase R 在此基础上增加**错误分类映射表**,将 R 引擎的原始错误转化为用户友好提示: | 错误模式(R 报错关键词) | 用户友好提示 | 错误码 | |------------------------|-----------|--------| | `NA`、`missing values`、`incomplete cases` | "数据中存在缺失值,请检查数据清洗后重试" | `E_MISSING_DATA` | | `column not found`、`undefined columns`、`not found` | "运算引擎未找到指定变量列,请检查数据源列名是否正确" | `E_COLUMN_NOT_FOUND` | | `system is computationally singular`、`collinear` | "数据存在严重共线性,建议排除冗余变量后重试" | `E_COLLINEARITY` | | `not enough observations`、`sample size` | "样本量不足以执行该统计方法,建议增加样本或选用非参数方法" | `E_INSUFFICIENT_SAMPLE` | | `contrasts can be applied only to factors with 2 or more levels` | "分组变量的水平数不足,请检查数据分组" | `E_FACTOR_LEVELS` | | 其他未匹配的 R 错误 | "运算引擎遇到异常,请检查数据结构后重试" | `E_UNKNOWN` | **Phase Deploy 推迟实施(完整重试短路机制):** > 以下机制推迟到 Phase Deploy 阶段实施,Phase R 不做跨层 Self-healing。 **强制规则:** `MAX_RETRIES = 2`,超过后直接中断。 | 错误类型 | 典型报错关键词 | 处理策略 | |---------|--------------|---------| | **不可重试(Hard Abort)** | `system is computationally singular`、`not enough observations`、`contrasts can be applied only to factors with 2 or more levels` | 直接中断,跳过重试,生成诊断报告给用户 | | **可重试 — 参数级修复** | `column not found`、`invalid factor level`、`missing values` | LLM 分析错误日志 → 修改参数 JSON → 重新请求同一 R 脚本(**禁止生成新 R 代码**) | | **可重试 — 方法级降级** | `sample size too small for parametric test` | 回调 Planner 切换 Fallback Tool(如 T 检验 → Mann-Whitney) | #### 3.9 统计量"槽位注入"反幻觉机制 > **来源:** 《医疗AI统计助手架构研究》议题 3 — LLM 被剥夺生成数值 Token 的权限。 **核心规则:** 结论中的所有统计量(P 值、效应量、置信区间等)**必须**来自 R 引擎的实际输出,通过模板槽位渲染,**禁止 LLM 在自由文本中"编写"任何数值**。 ``` // Reflection Prompt 中的槽位引用示例 各步骤结果(以下数值为系统自动注入,你不得修改或重新表述这些数字): 步骤 1:独立样本 T 检验 - 统计量:t = {{steps[1].statistic}} - P 值:{{steps[1].p_value}} - 效应量 Cohen's d:{{steps[1].effect_size}} - 95% 置信区间:{{steps[1].ci_lower}} ~ {{steps[1].ci_upper}} 请基于上述精确数值生成论文结论,结论中引用数值时必须与上方完全一致。 ``` **实现方式:** `ReflectionService.extractKeyFindings()` 将 R 返回的 JSON 中的数值提取为 `{{slot}}` 变量,在 Prompt 渲染时注入。LLM 只负责生成叙述性文字框架。 **ReflectionService 核心逻辑:** ```typescript // 伪代码 — 使用平台通用能力层(v8.0 更新:Zod 校验 + 完整 JSON 推送) import { z } from 'zod'; import { jsonrepair } from 'jsonrepair'; // Zod Schema — 强校验 LLM 输出结构(见 3.11 节) const ConclusionReportSchema = z.object({ executive_summary: z.string().min(10), key_findings: z.array(z.string()).min(1), statistical_summary: z.object({ total_tests: z.number(), significant_results: z.number(), methods_used: z.array(z.string()), }), methodology: z.string().min(10), limitations: z.array(z.string()).min(1), recommendations: z.array(z.string()).optional(), }); class ReflectionService { constructor( private promptService: PromptService, // 平台通用能力:Prompt 管理 private llmFactory: LLMFactory, // 平台通用能力:LLM 网关 private cacheService: CacheService, // 平台通用能力:缓存 private conclusionGenerator: ConclusionGeneratorService, // Fallback:旧规则拼接 ) {} async reflect(plan: AnalysisPlan, results: StepResult[], sseEmitter?: SSEEmitter): Promise { // 0. 缓存命中检查 const cacheKey = `ssa:conclusion:${plan.workflow_id}`; const cached = await this.cacheService.get(cacheKey); if (cached) return cached; // 1. 发送 QPER 状态:正在生成结论 sseEmitter?.emit({ type: 'qper_status', status: 'reflecting', message: '正在生成论文级结论...' }); // 2. 整合所有步骤的关键指标(提取为槽位变量,用于反幻觉注入) const keyFindings = this.extractKeyFindings(results); // 3. 从数据库加载 Prompt 模板 const promptTemplate = await this.promptService.getPrompt('ssa_reflection', 'latest'); const prompt = this.promptService.render(promptTemplate, { goal: plan.goal, methodology: plan.methodology, decision_trace: plan.decision_trace, findings: keyFindings, sampleSize: plan.sampleInfo }); // 4. 调用 LLM(完整收集,不做字符级流式推送) const llm = this.llmFactory.create({ model: 'deepseek-v3', temperature: 0.3 }); try { const rawOutput = await llm.chat(prompt); // 5. jsonrepair + Zod 强校验(见 3.11 节) const repaired = jsonrepair(rawOutput); const parsed = JSON.parse(repaired); const conclusion = ConclusionReportSchema.parse(parsed); // 6. 缓存 + 通过 SSE 推送完整结构化结论(见 3.12 节) const report: ConclusionReport = { workflow_id: plan.workflow_id, ...conclusion, step_summaries: this.buildStepSummaries(results), generated_at: new Date().toISOString(), }; await this.cacheService.set(cacheKey, report, { ttl: 3600 }); sseEmitter?.emit({ type: 'reflection_complete', conclusion: report }); return report; } catch (error) { // LLM 失败或 Zod 校验失败 → 降级到规则拼接 logger.warn('[SSA:Reflection] LLM/Zod failed, falling back to rule-based', { error }); const fallback = this.conclusionGenerator.generateConclusion(results, plan.goal); const report = this.adaptLegacyToNew(plan.workflow_id, fallback); sseEmitter?.emit({ type: 'reflection_complete', conclusion: report }); return report; } } } ``` **Reflection Prompt 设计要点:** > **存储方式:** 以下 Prompt 通过 seed 脚本写入 `capability_schema.prompt_templates` 表,`template_key = 'ssa_reflection'`。运行时通过 `PromptService.getPrompt()` 加载。 ``` 你是一位高级生物统计师,请基于以下分析结果生成论文级结论。 分析目标:{{goal}} 采用方法:{{methodology}} 方法选择的决策轨迹(请据此撰写方法学说明,不得臆造选择理由): {{decision_trace.matched_rule}} {{#each decision_trace.diagnosis_adjustments}} - 调整:{{this}} {{/each}} 各步骤结果(⚠️ 以下数值由系统自动注入,你必须原样引用,不得修改、四舍五入或重新表述任何数字): {{#each findings}} 步骤 {{step_number}}:{{tool_name}} - 统计量:{{statistic}} - P 值:{{p_value}} - 效应量:{{effect_size}} - 置信区间:{{ci_lower}} ~ {{ci_upper}} {{/each}} 请生成包含以下要素的结论: 1. 样本描述(纳入/排除) 2. 主要结果(含统计量和 P 值 — 必须与上方数值完全一致) 3. 效应量解读(临床意义) 4. 敏感性分析结论(如有) 5. 方法学说明(基于上方决策轨迹撰写,解释为什么选择此方法) 6. 局限性声明 要求:使用论文"结果"章节的行文风格,可直接复制到论文中。 ``` **验收标准:** - 输入 T 检验结果 `{ t=2.45, p=0.015, d=0.52 }` - 输出包含 6 个要素的论文级结论 - Word 导出包含 LLM 结论(非简单数字罗列) #### 3.10 敏感性分析结论冲突 Prompt 策略 > **来源:** 架构委员会建议 1 — 当主分析与敏感性分析显著性不一致时,LLM 容易陷入逻辑混乱或强行拼凑显著性。 > > **业务痛点:** 在"描述→主分析→敏感性分析"的标准流程中,如果 T 检验(主)P=0.04(显著)但 Wilcoxon 检验(辅)P=0.06(不显著),LLM 可能选择性忽略不一致的结果。在临床研究中,这属于必须报告的结果稳健性问题。 **实施方式:** 在 `ssa_reflection` Prompt 模板中硬编码冲突处理准则: ``` ## 冲突处理准则(强制执行) 当主分析与敏感性分析的显著性结论不一致时: 1. 在【局限性声明】中必须指出:"敏感性分析未得到一致结论,结果的稳健性(Robustness)较弱,需谨慎解释临床意义" 2. 在【主要发现】中以主分析结果为基准报告,但需加注"敏感性分析未验证此结论" 3. 严禁选择性报告、强行拼凑显著性 4. 当所有分析方向一致时,在【主要发现】中强调"敏感性分析进一步验证了结论的稳健性" ``` **效果:** LLM 遇到矛盾数据时有明确的处理规程,既保证了学术诚信,又为临床研究者提供了稳健性判断依据。实现成本极低(仅在 Prompt 中增加 4 行准则)。 #### 3.11 Zod Schema 强校验 LLM 输出 > **来源:** 架构委员会建议 2 — LLM 偶尔漏掉 JSON Key 或将数组写成字符串,导致前端渲染崩溃。 > > **与 Phase Q 的模式一致性:** Phase Q 中已成功实践 `jsonrepair` + Zod 动态校验(`createDynamicIntentSchema`)。Phase R 延续同一防御范式。 **实施方式:** `ReflectionService` 中对 LLM 输出进行三层防御: ``` Layer 1: jsonrepair — 修复 LLM 输出的 JSON 格式错误(漏逗号、多余尾逗号等) Layer 2: JSON.parse — 解析为 JS 对象 Layer 3: Zod Schema — 强校验结构完整性(字段是否齐全、类型是否正确、数组最小长度等) ``` **Zod Schema 定义(与前端 `ConclusionReport` 接口对齐):** ```typescript const ConclusionReportSchema = z.object({ executive_summary: z.string().min(10), // 不允许空摘要 key_findings: z.array(z.string()).min(1), // 至少 1 条发现 statistical_summary: z.object({ total_tests: z.number(), significant_results: z.number(), methods_used: z.array(z.string()), }), methodology: z.string().min(10), // 不允许空方法学说明 limitations: z.array(z.string()).min(1), // 至少 1 条局限性 recommendations: z.array(z.string()).optional(), // 建议为可选 }); ``` **降级触发:** Zod 校验失败时,立即切回旧的 `ConclusionGeneratorService`(规则拼接),确保前端始终收到有效的结论结构。日志记录 Zod 错误详情,用于后续 Prompt 迭代优化。 #### 3.12 Reflection 输出交付策略(完整 JSON + 逐 Section 渐入) > **来源:** 架构委员会建议 3 — 逐字推送 JSON 字符串会导致前端频繁抛出 `SyntaxError`。 > > **核心决策:** Phase R 的 Reflection 输出**不做字符级流式推送**,采用"后端完整收集 + Zod 校验 + 一次性推送 + 前端逐 section 渐入动画"方案。 **不采用字符级流式推送的理由:** 1. **Zod 校验需要完整 JSON** — 无法对不完整的 JSON 片段执行结构校验 2. **结构化数据需求** — Word 导出、数据库存储、API 返回都需要完整的 `ConclusionReport` JSON 3. **等待时间可接受** — LLM 生成结论通常 3-8 秒,期间通过 `qper_status` SSE 事件展示"正在生成论文级结论...",用户已看到所有数字结果(E 层 `report_blocks`),不会焦虑 4. **避免 Markdown↔JSON 双向转换的脆弱性** — 如果 LLM 输出纯 Markdown 再解析为 JSON,逆向解析极易出错 **完整的交付流程:** ``` 后端流程: 1. 发送 SSE: { type: 'qper_status', status: 'reflecting', message: '正在生成论文级结论...' } 2. LLM 完整生成结构化 JSON(非流式) 3. jsonrepair → JSON.parse → Zod 校验 4. 校验通过 → SSE: { type: 'reflection_complete', conclusion: ConclusionReport } 5. 校验失败 → ConclusionGeneratorService fallback → SSE: { type: 'reflection_complete', ... } 前端渲染(逐 section 渐入动画): 1. 收到 qper_status(reflecting) → 显示加载动画 "📝 正在生成论文级结论..." 2. 收到 reflection_complete → 开始渐入动画: - 0ms: executive_summary 淡入 - 300ms: key_findings 逐条滑入 - 600ms: methodology 淡入 - 900ms: limitations 淡入 - 1200ms: recommendations 淡入(如有) 3. 动画完成 → 显示"一键复制"和"导出 Word"按钮 ``` **SSE 事件类型调整(见 5.5 节):** 原 `reflection_stream`(chunk-based)改为 `reflection_complete`(一次性完整推送)。 --- ### Phase Deploy : 工具补齐 + 部署上线(4 天) **为什么最后做:** 智能化架构搭好后,补齐剩余工具和部署上线。 | # | 任务 | 工时 | 说明 | |---|------|------|------| | 1 | R 工具补齐:ANOVA | 3h | `ST_ANOVA_ONE` + Block-based 输出 | | 2 | R 工具补齐:Fisher | 2h | `ST_FISHER` + Block-based 输出 | | 3 | R 工具补齐:Wilcoxon | 2h | `ST_WILCOXON` + Block-based 输出 | | 4 | R 工具补齐:线性回归 | 3h | `ST_LINEAR_REG` + Block-based 输出 | | 5 | **复合工具 `ST_BASELINE_TABLE`(R 端)** | 4h | 基于 `gtsummary::tbl_summary(by=group_var) %>% add_p()` 封装;输入:`group_var` + `analyze_vars[]`;自动判断连续/分类、正态/非正态、选择 T 检验/Mann-Whitney/卡方/Fisher;R Docker 镜像需新增 `gtsummary`+`gt`+`broom` 依赖 | | 6 | **`gtsummary` → `report_blocks` 转换层** | 4h | `gtsummary::as_tibble()` 提取结构化表格 → 转换为 `table` block(需处理合并行:分类变量名+各水平值;列头:分组名称);输出需同时包含渲染用 blocks 和结构化 `significant_vars[]`(P<0.05 的变量列表,供后续步骤消费) | | 7 | **前端三线表渲染增强** | 4h | `DynamicReport.tsx` 的 `table` block 增强:支持 `rowspan`(分类变量合并行)、分组列头(group1 vs group2 vs P 值)、P<0.05 加粗/标星、横向滚动(20+ 变量场景) | | 8 | 决策表补齐所有工具映射 | 2h | 11 工具全部纳入(含 `ST_BASELINE_TABLE`) | | 9 | 流程模板补齐 | 2h | 覆盖所有 Goal 类型 + `cohort_study_standard` 模板(见下方) | | 10 | R Docker 镜像推送 ACR | 1h | 新工具 + `gtsummary` 依赖包含在内 | | 11 | SAE 部署 + 联调 | 4h | R 服务 + 后端 + 前端 | | 12 | 端到端测试(12 场景) | 8h | 原 10 场景 + 队列研究全套流程(表1→表2→表3)×2 数据集 | --- ### Phase Q+ : 人机协同增强 — 变量字典 + 变量选择面板(2.5 天) **为什么独立分期:** Phase Q 核心目标是证明 LLM 能从自然语言中提取 `[Goal, Y, X, Design]`。将重度前端交互(表格编辑、穿梭框、状态回传)与核心 AI 逻辑耦合,会导致单点阻塞。Phase Q+ 在 Q-P-E-R 主线闭环跑通后再启动,此时:① 已有 AI 基线准确率数据,可量化人机协同的提升价值;② 后端接口已稳定,前端可安全叠加。 > **核心哲学:AI 负责统计专业知识,医生负责临床领域知识。** ``` 定位:Phase Q 的增强层,非阻塞主线交付 前置依赖:Phase Q 验收通过(LLM 意图解析基线可用) 触发时机:Q-P-E-R 主线闭环跑通后 ``` #### 3.10 增强点一:变量数据字典(Data Dictionary) **解决什么问题:** 临床数据列名极不规范(`grp`、`Tx`、`SBP`),DataProfile 只有统计特征没有语义信息。LLM 靠猜列名含义,猜错即全链路错误。 **交互模式:"AI 先猜,用户微调"** - 文件上传 + DataProfiler 完成后,后端静默调用 LLM(基于列名 + 前 5 行数据 + 数据类型)猜测每个变量的中文含义 - 前端推送**变量字典编辑面板**,用户确认或修改 - 经用户确认的字典成为**黄金上下文(Golden Context)**,后续 Q/P/R 全链路可消费 | # | 任务 | 层级 | 工时 | 说明 | |---|------|------|------|------| | 1 | `VariableDictService.ts` — LLM 变量含义猜测 | 后端 | 3h | 通过 `PromptService` + `LLMFactory` 调用 LLM,输入列名+前5行+类型,输出 `{ name, guessed_meaning, guessed_role }[]`;Zod 校验 | | 2 | Prisma schema 扩展 | 后端 | 1h | `SsaFileUpload` 新增 `variable_dictionary: Json?` 字段(或新建 `SsaVariableDictionary` 表),存储用户确认后的字典 | | 3 | 变量字典 API | 后端 | 1h | `GET /api/v1/ssa/sessions/:id/variable-dict`(获取 AI 猜测结果)+ `PUT`(保存用户修改) | | 4 | `VariableDictEditor.tsx` 前端组件 | 前端 | 3h | 表格形式:`变量名 | 检测类型 | 前5个值预览 | AI猜测含义(可编辑) | 用户标注角色提示`;支持批量确认 | | 5 | `QueryService` 集成字典 | 后端 | 1h | `parseQuery` 方法接受可选 `variableDict` 参数,有字典时将 `variable_description` 注入 Prompt 的 `{{variables}}` 中 | **验收标准:** - 上传含 `grp(1,2)` 列的 CSV → AI 猜测"分组变量" → 用户标注"1=新药, 2=安慰剂" → 后续 Q 层正确识别 `grp` 为分组变量 - 用户跳过字典编辑(直接点"确认")→ 系统正常运行(AI 猜测结果作为默认值) #### 3.11 增强点二:变量选择确认面板(Variable Selection) **解决什么问题:** LLM 从 100 列中猜 Y/X 变量,可能选错或遗漏关键协变量。医生最清楚哪些是核心指标、哪些必须作为调整变量纳入。 **交互模式:AI 推荐 + 医生确认/调整** - Q 层 LLM 解析完意图后,在进入 P 层之前推送**变量选择确认面板** - AI 预选的 Y/X 变量已标记,医生可增删、可添加协变量(Confounders) - 医生确认后的变量集合替代 AI 原始选择,传入 P 层 | # | 任务 | 层级 | 工时 | 说明 | |---|------|------|------|------| | 6 | `QueryService` 输出扩展 | 后端 | 1h | `ParsedQuery` 增加 `suggested_confounders: string[]`(AI 建议的协变量)+ `all_candidate_vars` 列表供前端渲染 | | 7 | `VariableSelectionPanel.tsx` 前端组件 | 前端 | 4h | 穿梭框/卡片交互:左侧"可用变量"(全部),右侧"已选变量"分三区(Y/X/协变量);AI 预选项高亮标记;支持拖拽或点击增删 | | 8 | 确认 API + Context Pruning 衔接 | 后端 | 1h | `POST /api/v1/ssa/sessions/:id/confirm-variables`;用户确认的变量集合覆盖 AI 原始选择,`pruneForPlanner` 以医生确认集合为基准裁剪 | | 9 | SSE 事件 + 状态回传 | 后端 | 1h | `DICT_EDITING` / `VARIABLE_CONFIRMING` 状态推送 + 前端状态管理集成 | | 10 | `SSAChatPane` 集成两个面板 | 前端 | 2h | 接收 SSE 事件渲染字典编辑器/变量选择面板,用户操作后回传并恢复流程 | **验收标准:** - AI 推荐 Y=BP, X=Drug → 医生增加协变量 Age, Sex → P 层收到完整的 `[Y=BP, X=Drug, Confounders=[Age, Sex]]` - 医生不修改直接确认 → 使用 AI 原始选择(零额外成本) #### 3.12 Phase Q+ 默认放行策略 > **重要:** Phase Q+ 未上线前,系统自动跳过 `DICT_EDITING` 和 `VARIABLE_CONFIRMING` 状态,直接使用 AI 自动解析结果。后端接口契约保持不变。 ```typescript // Phase Q+ 开关(feature flag) const PHASE_Q_PLUS_ENABLED = false; // Phase Q+ 上线后改为 true 或从配置读取 // 状态机跳过逻辑 if (currentStatus === 'profiling_done') { nextStatus = PHASE_Q_PLUS_ENABLED ? 'dict_editing' : 'pending_intent'; } if (currentStatus === 'intent_parsed') { nextStatus = PHASE_Q_PLUS_ENABLED ? 'variable_confirming' : 'planning'; } ``` **Phase Q+ 工时汇总:** | 类型 | 任务 | 工时 | |------|------|------| | 后端 | VariableDictService + Schema + API + QueryService集成 + 确认API + SSE | 8h | | 前端 | VariableDictEditor + VariableSelectionPanel + SSAChatPane集成 | 9h | | 测试 | 两个面板的端到端验证 | 3h | | **合计** | | **20h(2.5天)** | --- ## 4. 代码目录结构(渐进式,不做大规模重组) > **策略:** 所有新文件放入现有的 `services/` 和 `config/` 目录,而非创建 `query/` `planner/` `reflection/` 子目录。原因:现有 SSA 模块已有大量文件互相引用,子目录化会导致全量 import 路径变更、风险过高、产出为零。用 **文件命名前缀** 区分 QPER 层级即可。 ``` backend/src/modules/ssa/ ├── services/ ← 所有服务统一放此目录 │ ├── QueryService.ts # Q 层:LLM 意图解析 + 追问逻辑(新建) │ ├── VariableDictService.ts # Q+ 层:LLM 变量含义猜测 + 字典管理(Phase Q+ 新建) │ ├── DataDiagnosticService.ts # Q 层:数据深度诊断(新建,扩展 DataProfileService) │ ├── PlannerService.ts # P 层:核心规划逻辑(新建,接收 ParsedQuery) │ ├── DecisionTableService.ts # P 层:决策表加载 + 四维匹配(新建) │ ├── FlowTemplateService.ts # P 层:流程模板管理(新建) │ ├── ReflectionService.ts # R 层:LLM 结论生成(重构自 ConclusionGeneratorService) │ ├── WorkflowExecutorService.ts # E 层:步骤编排 + SSE(✅ 保持不变) │ ├── RClientService.ts # E 层:R 引擎调用(✅ 保持不变) │ ├── WorkflowPlannerService.ts # 兼容层:旧入口,内部委托 QueryService + PlannerService │ ├── ConclusionGeneratorService.ts # 兼容层:旧入口,内部委托 ReflectionService │ ├── DataProfileService.ts # 共享:Python 数据质量(✅ 保留) │ └── DataParserService.ts # 共享:文件解析(✅ 保留) │ ├── config/ ← 决策表 + 流程模板配置 │ ├── decision_table.json # P 层配置 │ └── flow_templates.json # P 层配置 │ ├── types/ ← 新增 QPER 接口定义 │ ├── query.types.ts # ParsedQuery 接口 │ ├── planner.types.ts # AnalysisPlan / AnalysisStep 接口 │ └── reflection.types.ts # Conclusion 接口 │ ├── routes/ # API 路由(扩展,不重建) │ ├── analysis.routes.ts # ✅ 保留 │ ├── workflow.routes.ts # ✅ 保留 │ └── config.routes.ts # ✅ 保留(已有决策表/代码库 CRUD) └── ... ``` > **Prompt 不放在本地文件中**,而是通过平台 `PromptService` 存入 `capability_schema.prompt_templates` 数据库表,支持版本管理和 A/B 测试。开发阶段使用 seed 脚本初始化。 --- ## 5. 数据流协议 ### 5.1 Q → P 接口契约 ```typescript interface ParsedQuery { // 用户意图 goal: 'difference' | 'association' | 'prediction' | 'description'; confidence: number; // 0.0 - 1.0 // 变量角色 outcome_var: string; // 结局变量 (Y) outcome_type: 'continuous' | 'categorical' | 'binary'; predictor_vars: string[]; // 自变量 (X) predictor_types: string[]; // 各自变量类型 // 实验设计 design: 'independent' | 'paired'; // 数据诊断(Q 层产出) dataDiagnosis: { sampleSize: number; missingRate: Record; outlierCount: Record; normalityTests?: Record; groupBalance?: { groups: string[]; counts: number[] }; }; // 追问状态 needsClarification: boolean; clarificationQuestions?: string[]; } ``` ### 5.2 P → E 接口契约 > **兼容策略:** 新 `AnalysisPlan` 接口必须兼容现有 `WorkflowPlan`(`WorkflowExecutorService` 已在使用),避免 Execute 层改动。`AnalysisStep` 扩展自现有 `WorkflowStep`,新增字段为可选。 ```typescript // 现有接口(WorkflowExecutorService 已使用,不可破坏) interface WorkflowStep { step_number: number; tool_code: string; tool_name: string; description: string; params: Record; } // 新增接口(扩展,向后兼容) interface AnalysisStep extends WorkflowStep { role?: 'descriptive' | 'primary_test' | 'sensitivity' | 'effect_size'; // 可选,新增 } interface AnalysisPlan { workflow_id: string; title: string; methodology: string; // "因 Y 为连续变量且两组独立,采用独立样本 T 检验" rationale: string; // "数据满足正态分布假设(Shapiro P=0.15)" steps: AnalysisStep[]; // 兼容 WorkflowStep[] // 元信息(新增,P 层产出) primary_tool: string; fallback_tool?: string; flow_template_used: string; // 决策审计日志(P 层产出 → R 层消费,用于生成方法学说明) decision_trace: { matched_rule: string; // "Y=continuous, X=categorical_2, Design=independent → ST_T_TEST_IND" diagnosis_adjustments: string[]; // ["Shapiro-Wilk P=0.03 < 0.05 → 切换非参数 Mann-Whitney"] triggered_at: string; // ISO timestamp }; } // 兼容转换函数(PlannerService 内部使用) function toWorkflowPlan(plan: AnalysisPlan): WorkflowPlan { return { workflow_id: plan.workflow_id, steps: plan.steps, // AnalysisStep extends WorkflowStep,直接兼容 // ... }; } ``` ### 5.3 E → R 接口契约 ```typescript interface ExecutionResult { steps: StepResult[]; // 各步骤结果(已有) report_blocks: ReportBlock[]; // Block-based 输出(Phase E+ 新增) total_duration_ms: number; } ``` ### 5.4 会话状态机(防面条代码) > **来源:** 《QPER V3.0 架构审查与工程护航报告》隐患 2 — QPER 流程存在 Clarifier(等用户回复)和 Reflection(错误重试)两处中断/恢复逻辑,用嵌套 if-else + while 会迅速劣化为面条代码。 ```typescript enum ExecutionStatus { PENDING_UPLOAD = 'pending_upload', // 等待数据上传 PROFILING = 'profiling', // 数据诊断中(Python Tool C) DICT_EDITING = 'dict_editing', // 🆕 Phase Q+:变量字典编辑中 — ⏸️ 中断,等待用户确认 PENDING_INTENT = 'pending_intent', // 等待意图解析 CLARIFYING = 'clarifying', // 追问中 — ⏸️ 中断,等待用户回复 VARIABLE_CONFIRMING = 'variable_confirming', // 🆕 Phase Q+:变量选择确认中 — ⏸️ 中断,等待用户确认 PLANNING = 'planning', // 规划中 PLAN_CONFIRMING = 'plan_confirming', // 等待用户确认 SAP — ⏸️ 中断 EXECUTING = 'executing', // R 引擎执行中 REFLECTING = 'reflecting', // 结论生成中 RETRYING = 'retrying', // 错误修复后重试中(MAX_RETRIES=2) COMPLETED = 'completed', // 完成 FAILED = 'failed', // 失败(不可恢复) } ``` **状态持久化:** `ExecutionStatus` 存入 `SsaSession` 表。当状态为 `DICT_EDITING`、`CLARIFYING`、`VARIABLE_CONFIRMING` 或 `PLAN_CONFIRMING` 时,后端立即中断执行并向前端发送交互面板,等待下一次 HTTP 请求(用户操作/确认)后从该状态恢复继续。 **状态流转图:** ``` PENDING_UPLOAD → PROFILING → [DICT_EDITING]* → PENDING_INTENT → CLARIFYING (可选,循环) ↓ [VARIABLE_CONFIRMING]* ↓ PLANNING → PLAN_CONFIRMING (可选) ↓ EXECUTING → REFLECTING → COMPLETED ↑ ↓ └── RETRYING (MAX 2次) ↓ (超限) FAILED * [方括号] 表示 Phase Q+ 新增状态,Phase Q+ 未上线时自动跳过(feature flag 控制) ``` ### 5.5 QPER 级 SSE 事件类型(全链路状态推送) > **来源:** 《QPER V3.0 架构审查与工程护航报告》隐患 3 — R 层重试过程可能耗时 15-20 秒,用户只看到转圈会认为系统死了。"展示 AI 工作过程"是信任建立的关键。 当前 SSE 只覆盖 E 层步骤进度。扩展为 QPER 全链路状态推送: ```typescript // SSE 事件类型扩展(v8.0 更新:reflection_stream → reflection_complete) type SSAEvent = | { type: 'qper_status'; status: ExecutionStatus; message: string } // 状态跃迁 | { type: 'step_progress'; step: number; total: number; ... } // E 层步骤进度(已有) | { type: 'clarification'; questions: ClarificationOption[] } // 追问卡片 | { type: 'plan_ready'; plan: AnalysisPlan } // SAP 确认 | { type: 'reflection_complete'; conclusion: ConclusionReport } // R 层完整结论(一次性推送,非字符流) | { type: 'error'; code: string; message: string } // 错误 // 前端渲染效果示例: // [Query] 🧠 正在理解您的分析意图... // [Query] ✅ 识别到:差异比较 | Y=血压 | X=用药组 // [Planner] 📋 正在规划分析方案... // [Planner] ✅ 已生成 3 步分析计划(描述统计 → T检验 → Mann-Whitney) // [Executor] 🔄 步骤 1/3:描述统计... // [Executor] ✅ 步骤 1/3 完成 [0.8s] // [Executor] ❌ 步骤 2/3 失败:变量存在完全共线性 // [Executor] ⚠️ 运算引擎遇到异常:数据存在严重共线性(E_COLLINEARITY) // [Executor] 🔄 步骤 3/3:Mann-Whitney... // [Executor] ✅ 步骤 3/3 完成 [0.6s] // [Reflection] 📝 正在生成论文级结论...(qper_status: reflecting) // [Reflection] ✅ 结论生成完成(reflection_complete → 前端逐 section 渐入动画) ``` --- ## 6. 工时与里程碑 | Phase | 名称 | 工时 | 日历天 | 里程碑 | 变更说明 | |-------|------|------|--------|--------|---------| | **E+** | Block-based 标准化 | 15.5h | 2.5天 | R 输出标准化,前端动态渲染 | 后端透传需含 report_blocks 全链路验证 | | **Q** | Query 层(LLM 意图) | 33h | 5天 | 用户说"有没有效"即可分析 | v4.0 +1.5h:DataProfile 会话级缓存(0.5h) + Intent Prompt Few-Shot(1h);v3.0 +1h:Context Pruning | | **P** | Planner 层(决策表+模板) | 23h | 4天 | 完整分析流程(非单方法) | v3.0 +1h:decision_trace 决策审计日志输出 | | **R** | Reflection 层(LLM 结论) | 22h | 3天 | 论文级结论,可直接用于论文 | v3.0 +2h:统计量槽位注入反幻觉;v8.0 -1h:完整重试推迟至 Deploy,Phase R 仅做错误分类映射(1h);+0h:敏感性冲突 Prompt + Zod 校验 + 完整 JSON 交付(融入现有任务工时) | | **Deploy** | 工具补齐 + 部署 | 37h | 5.5天 | 11 工具上线(含复合工具),生产环境可用 | v6.0 +12h:ST_BASELINE_TABLE(4h) + gtsummary→blocks 转换(4h) + 前端三线表增强(4h);端到端测试 +2h | | **主线合计** | | **130.5h** | **~20天** | **Q-P-E-R 主线闭环** | v6.0 Deploy +12h;v4.0 +1.5h;v3.0 +4h;v8.0 -1h | | --- | --- | --- | --- | --- | --- | | **Q+** | 人机协同增强 | 20h | 2.5天 | 变量字典 + 变量选择面板 | v5.0 新增:医生注入临床领域知识,AI→人机协同 | | **总计** | | **150.5h** | **~22.5天** | **Q-P-E-R + 人机协同 + 队列研究全套** | 主线闭环后启动 Phase Q+ | ### 里程碑时间线 ``` Week 1 ────────────────────────────────── Day 1-2: Phase E+ (Block-based) Day 3-5: Phase Q (Query 层前半:Prompt 注册 + QueryService) Week 2 ────────────────────────────────── Day 6-7: Phase Q (Query 层后半:DataDiagnostic + 前端追问 + 联调) Day 8-11: Phase P (Planner 层) Week 3 ────────────────────────────────── Day 12-14: Phase R (Reflection 层) Day 15-19: Phase Deploy (原子工具补齐 + ST_BASELINE_TABLE 复合工具 + 部署) Day 20: 队列研究端到端验证(Table 1→2→3 全流程) Week 4 ────────────────────────────────── ✅ Q-P-E-R 主线闭环上线(里程碑 1:含队列研究全套分析能力) Day 20: 收集 AI 基线准确率数据(无人机协同下的意图识别准确率) Week 4-5 ──────────────────────────────── Day 19-21: Phase Q+ (变量字典 + 变量选择面板) ✅ 人机协同增强上线(里程碑 2) 对比量化:人机协同 vs 纯 AI 的准确率提升 ``` --- ## 7. 风险管理 | 风险 | 概率 | 影响 | 应对 | |------|------|------|------| | LLM 意图提取准确率不足 | 中 | 高 | 低置信度时追问,不猜测;保留正则兜底(`WorkflowPlannerService.parseUserIntent` 作为 fallback) | | 决策表覆盖率不足 | 低 | 中 | 先覆盖 10 工具,后续 Excel 可热加载扩展;评估使用已有 `SsaDecisionTable` 数据库表 | | LLM 结论"幻觉"虚构统计量 | 中 | 高 | **统计量槽位注入机制(3.9 节)**:所有数值通过 `{{slot}}` 从 R 输出渲染,LLM 被剥夺生成数值 Token 的权限;Prompt 中明确标注"不得修改、四舍五入或重新表述任何数字" | | Reflection 延迟过高(LLM 调用) | 中 | 中 | 通过平台 `StreamingService` 流式输出,用户先看到数字结果,结论异步加载 | | Q→P→E 串联复杂度 | 低 | 中 | 每层独立可测试,接口契约明确 | | **新增:LLM 服务不可用** | 中 | 高 | Q 层降级到正则匹配,R 层降级到规则拼接(`ConclusionGeneratorService`),系统功能不中断 | | **新增:Prisma 迁移遗漏** | 低 | 高 | 新增字段(如 `report_blocks` 存储)需通过 `npx prisma migrate dev` 正式迁移,禁止 `db push --force-reset`;每个 Phase 完成时检查 schema 变更 | | **新增:AnalysisPlan 接口破坏 Execute 层** | 低 | 高 | `AnalysisStep extends WorkflowStep`,保持向后兼容;E 层不做任何接口改动 | | **新增:旧组件过早删除** | 中 | 中 | `DescriptiveResultView` 等现有组件(2026-02-20 新建)在 Block-based 未完全验证前保留作为 fallback,不提前删除 | | **新增:R 引擎运行时崩溃(Runtime Crash)** | 中 | 中 | 区分"统计降级"(R 内部 if-else 处理)与"运行时崩溃"(NA 值/列名错误/Fatal Error);后者通过错误分类映射表转化为友好提示,Phase R 不做跨层 Self-healing,完整重试推迟至 Phase Deploy | | **新增:敏感性分析结论冲突** | 中 | 高 | 主分析与敏感性分析显著性不一致时,Reflection Prompt 硬编码冲突处理准则:必须在局限性中报告,严禁强行拼凑显著性(3.10 节) | | **新增:LLM Reflection 输出结构残缺** | 中 | 中 | `jsonrepair` + Zod Schema 三层防御(3.11 节);校验失败自动降级到 `ConclusionGeneratorService` 规则拼接 | ### 7.1 回退策略(Fallback Plan) | 层级 | 正常路径 | 降级路径 | 触发条件 | |------|---------|---------|---------| | **Q 层** | `QueryService`(LLM 意图解析) | `WorkflowPlannerService.parseUserIntent()`(正则匹配) | LLM 超时/不可用/解析失败 | | **P 层** | `PlannerService`(决策表+模板) | `WorkflowPlannerService.generateSteps()`(硬编码 if/else) | 决策表无匹配 | | **E 层** | `WorkflowExecutorService`(不变) | 错误分类映射 → 友好用户提示 | R 引擎运行时崩溃(NA/列名/Fatal Error) | | **R 层** | `ReflectionService`(LLM 论文结论) | `ConclusionGeneratorService`(规则拼接) | LLM 超时/不可用/**Zod 校验失败** | | **前端** | `DynamicReport`(Block-based 渲染) | 现有自定义渲染组件 | `report_blocks` 为空时 | > **原则:** 每个 QPER 层都有明确的降级路径,确保 LLM 不可用时系统仍然可用(退化为 Phase 2A 水平)。R 层新增 Zod 校验失败作为降级触发条件——LLM 输出结构不完整时,自动切回规则拼接,前端始终收到有效的 `ConclusionReport` 结构。 --- ## 8. 验收场景 ### 8.1 核心验收(5 个场景) | # | 用户输入 | 期望 Q 输出 | 期望 P 输出 | 期望 R 输出 | |---|---------|------------|------------|------------| | 1 | "比较两组血压" | goal=difference, design=independent | 描述统计 → T检验 → Mann-Whitney | "两组差异显著(P<0.001),中等效应(d=0.52)" | | 2 | "分析 sex 与 Yqol 的关系" | goal=association, Y=Yqol(categorical), X=sex | 描述统计 → 卡方检验 | "性别与 Yqol 存在显著关联(χ²=8.3, P=0.004)" | | 3 | "age、smoke 对 Yqol 的影响" | goal=prediction, Y=Yqol(binary) | 描述统计 → Logistic 回归 | "Logistic 回归显示 smoke 是显著预测因子(OR=2.1, P=0.03)" | | 4 | "描述一下数据" | goal=description | 描述统计 | 各变量的集中趋势和离散程度汇总 | | 5 | "有没有效" | confidence<0.7 → 追问 | 等待用户澄清后规划 | — | ### 8.2 智能化对比(改造前 vs 改造后) | 场景 | 改造前 | 改造后 | |------|--------|--------| | 用户说"有没有效" | ❌ 无法理解 | ✅ 追问后正确规划 | | 数据不正态 | 🟡 R 内部降级,用户不知 | ✅ 规划阶段就选非参数方法,告知理由 | | 分析结果 | P=0.015 + 数字表格 | 论文级结论:"两组差异具有统计学意义..." | | 分析流程 | 1 个方法 | 描述统计 → 主分析 → 敏感性分析 | | Word 报告 | 表格 + 数字 | 完整论文段落 + 方法学说明 | --- ## 9. 与旧计划的关系 | 旧计划内容 | 处理方式 | 理由 | |-----------|---------|------| | 配置中台(Excel 导入体系) | **延后** | 10 个工具用 JSON 配置足够,100+ 工具时再做 | | 咨询模式(无数据 SAP) | **延后** | 独立功能,不影响核心智能分析 | | 决策表概念 | **保留并简化** | 从 Excel 配置中台简化为内置 JSON | | Brain-Hand 命名 | **升级为 Q-P-E-R** | 更精确地描述四层职责 | | 10 工具目标 | **扩展为 11 工具** | 原 10 个原子工具 + 1 个复合工具 `ST_BASELINE_TABLE`(v6.0) | | SAE 部署 | **保留** | Phase Deploy 中完成 | --- **文档维护者:** SSA 架构团队 **创建日期:** 2026-02-20 **最后更新:** 2026-02-21(v8.0 — Phase R 架构增强) **下一步行动:** Phase E+/Q/P 已完成,按 R1→R7 顺序启动 Phase R 开发 ### 变更日志 | 版本 | 日期 | 变更内容 | |------|------|---------| | v1.0 | 2026-02-20 | 初版:QPER 架构设计 + 5 Phase 开发计划 | | v2.0 | 2026-02-21 | 代码审查后修订:① 核心原则新增"复用平台通用能力层";② ReflectionService 修正为重构;③ 目录结构改为渐进式平铺;④ 伪代码使用 LLMFactory + PromptService;⑤ AnalysisStep extends WorkflowStep;⑥ 新增已有数据库资产盘点;⑦ 新增全链路回退策略表;⑧ 工时 108.5h→114h | | v3.0 | 2026-02-21 | 外部调研审查后修订(综合4份报告):① 架构红线(禁止 LangGraph/AutoGen/LLM 生成 R 代码/多智能体辩论);② Context Pruning 上下文裁剪;③ 封闭式追问;④ decision_trace 决策审计日志;⑤ 重试短路机制;⑥ 统计量槽位注入反幻觉;⑦ Tracer Bullet 探路测试;⑧ 工时 114h→118h | | v4.0 | 2026-02-21 | 工程护航修订(依据《QPER V3.0 架构审查与工程护航报告》):① DataProfile 会话级缓存;② 显式状态机(ExecutionStatus 14 态);③ QPER 级 SSE 全链路状态推送;④ Intent Prompt Few-Shot 示例;⑤ 工时 118h→119.5h | | v5.0 | 2026-02-21 | 人机协同增强修订(依据《架构与产品委员会综合评估报告》):① 新增 Phase Q+(变量字典 + 变量选择面板,20h/2.5天);② 状态机扩展 DICT_EDITING + VARIABLE_CONFIRMING(含 feature flag 自动跳过);③ Intent Prompt 增加忽略非分析变量指令;④ 总工时 119.5h→139.5h | | v6.0 | 2026-02-21 | 复合工具扩展修订(依据队列研究 Table 1-3 终态验证 +《SSA-Pro 架构诊断与复合工具扩展方案》审查):① Phase Deploy 新增复合 R 工具 `ST_BASELINE_TABLE`(基于 `gtsummary` 封装,一次遍历所有变量 + 自动选方法 + 合并出表,同时覆盖表1基线比较和表2单因素筛选);② 新增 `gtsummary` → `report_blocks` 转换层(4h,含合并行/分组列头的结构化提取);③ 前端三线表渲染增强(4h,`DynamicReport.tsx` 支持 rowspan/colspan/P值标星/横向滚动);④ 新增 `cohort_study_standard` 流程模板(表1→表2→表3 三步流程,表3自变量由用户确认而非自动 P<0.05 筛选);⑤ 决策表新增 `cohort_study` 场景行;⑥ Q 层 Intent Prompt `goal` 扩展支持 `cohort_study` 值(识别"出一套完整报告"等触发词);⑦ 工具总数从 10 扩展为 11(10 原子 + 1 复合);⑧ Phase Deploy 工时 25h→37h(+12h),总工时 139.5h→151.5h | | v7.0 | 2026-02-21 | Phase P 架构增强(进入开发前的设计审查):① **Expected/Actual 双层审计日志** — P 层生成 PlannedTrace(策略),E 层 R trace_log 提供 Actual Trace(事实),R 层合并两者生成方法学说明;P 层绝不做正态性预检;② **Repository 模式** — DecisionTableService 通过 `IDecisionTableRepo` 接口解耦,初期 JSON 实现,后期可切 DB,核心业务逻辑零改动;③ **参数检验优先原则** — 决策表 Primary 始终为参数检验(效力更高),Fallback 为非参数(安全网),新增 `switch_condition` 列描述触发条件;④ **EPV 变量上限防护** — 队列研究 Table 3 采用"全量推入 + EPV 截断 + 免责声明"策略,防止过拟合和 R 引擎崩溃;⑤ **流程模板合并** — `two_group_comparison` + `association_analysis` 合并为 `standard_analysis`,最终 4+1 模板 | | v7.1 | 2026-02-21 | **新增核心原则"领域知识可配置化"** + Phase P 配置化基础设施:① **核心原则第 6 条** — "QPER 四层的所有领域知识由统计学方法学团队配置,不写死在代码中","可配置"作为贯穿所有 Phase 的架构约束,IT 团队搭引擎、方法学团队填内容;② **Zod Schema 严格校验** — 方法学团队编辑 JSON 时的拼写/结构错误在加载时立即拦截;③ **热更新 API** — `POST /reload-config` 无需重启服务即可生效,校验失败保留旧配置不影响线上;④ **领域文件物理拆分** — `tools_registry.json`(E 层)、`decision_tables.json`(P 层)、`flow_templates.json`(P 层)、`narrative_templates.json`(R 层),每文件有明确的方法学团队所有者;⑤ Phase P 后端任务新增任务 0(ConfigLoader 基础设施,2h)+ 任务 1(工具注册表,2h),总任务数 7→9 | | v8.0 | 2026-02-21 | **Phase R 架构增强(进入开发前的设计审查,依据架构委员会建议)**:① **敏感性分析结论冲突 Prompt 策略(3.10 节)** — 主分析与敏感性分析显著性不一致时,Reflection Prompt 硬编码冲突处理准则,严禁强行拼凑显著性,必须在局限性中报告稳健性问题;② **Zod Schema 强校验 LLM 输出(3.11 节)** — 延续 Phase Q 的 `jsonrepair` + Zod 三层防御范式,校验失败自动降级到 `ConclusionGeneratorService` 规则拼接;③ **Reflection 输出交付策略调整(3.12 节)** — 不做字符级流式推送,采用"后端完整收集 + Zod 校验 + 一次性 SSE 推送 `reflection_complete` + 前端逐 section 渐入动画"方案,原 `reflection_stream` 事件改为 `reflection_complete`;④ **运行时崩溃优雅处理(3.8 节重构)** — 区分"统计降级"与"运行时崩溃",Phase R 仅做错误分类映射(R 报错关键词→友好用户提示),完整重试短路机制(MAX_RETRIES + 参数级修复 + 方法级降级)推迟至 Phase Deploy;⑤ **ReflectionService 伪代码更新** — 加入 Zod Schema 定义、`qper_status` 状态推送、`reflection_complete` SSE 事件、Zod 校验失败降级逻辑;⑥ **风险管理表新增 3 行** — R 运行时崩溃、敏感性分析冲突、LLM 输出结构残缺;⑦ **回退策略表更新** — R 层触发条件新增"Zod 校验失败",E 层新增运行时崩溃处理路径;⑧ Phase R 工时 23h→22h(-1h:重试机制简化) |