Files
AIclinicalresearch/docs/03-业务模块/SSA-智能统计分析/04-开发计划/10-QPER架构开发计划-智能化主线.md
HaHafeng 371e1c069c feat(ssa): Complete QPER architecture - Query, Planner, Execute, Reflection layers
Implement the full QPER intelligent analysis pipeline:

- Phase E+: Block-based standardization for all 7 R tools, DynamicReport renderer, Word export enhancement

- Phase Q: LLM intent parsing with dynamic Zod validation against real column names, ClarificationCard component, DataProfile is_id_like tagging

- Phase P: ConfigLoader with Zod schema validation and hot-reload API, DecisionTableService (4-dimension matching), FlowTemplateService with EPV protection, PlannedTrace audit output

- Phase R: ReflectionService with statistical slot injection, sensitivity analysis conflict rules, ConclusionReport with section reveal animation, conclusion caching API, graceful R error classification

End-to-end test: 40/40 passed across two complete analysis scenarios.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-21 18:15:53 +08:00

1298 lines
82 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SSA-Pro Q-P-E-R 架构开发计划 — 智能化主线
> **文档版本:** v7.1
> **创建日期:** 2026-02-20
> **最后更新:** 2026-02-21v7.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 PromptFew-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-based2.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 和至少一个 X0.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<ParsedQuery> {
// 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,
};
}
// FallbackLLM 调用失败时降级到现有正则匹配WorkflowPlannerService.parseUserIntent
private async fallbackToRegex(userText: string, variables: Variable[]): Promise<ParsedQuery> { ... }
}
```
#### 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<DecisionRule[]>;
}
// 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 上限防护。
**EPVEvents 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<AnalysisPlan> {
// 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-WhitneySensitivity条件展示
- `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<ConclusionReport> {
// 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/卡方/FisherR 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 |
| **合计** | | **20h2.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<string, number>;
outlierCount: Record<string, number>;
normalityTests?: Record<string, { pValue: number; isNormal: boolean }>;
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<string, unknown>;
}
// 新增接口(扩展,向后兼容)
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/3Mann-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.5hDataProfile 会话级缓存(0.5h) + Intent Prompt Few-Shot(1h)v3.0 +1hContext Pruning |
| **P** | Planner 层(决策表+模板) | 23h | 4天 | 完整分析流程(非单方法) | v3.0 +1hdecision_trace 决策审计日志输出 |
| **R** | Reflection 层LLM 结论) | 22h | 3天 | 论文级结论,可直接用于论文 | v3.0 +2h统计量槽位注入反幻觉v8.0 -1h完整重试推迟至 DeployPhase R 仅做错误分类映射(1h)+0h敏感性冲突 Prompt + Zod 校验 + 完整 JSON 交付(融入现有任务工时) |
| **Deploy** | 工具补齐 + 部署 | 37h | 5.5天 | 11 工具上线(含复合工具),生产环境可用 | v6.0 +12hST_BASELINE_TABLE(4h) + gtsummary→blocks 转换(4h) + 前端三线表增强(4h);端到端测试 +2h |
| **主线合计** | | **130.5h** | **~20天** | **Q-P-E-R 主线闭环** | v6.0 Deploy +12hv4.0 +1.5hv3.0 +4hv8.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-21v8.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 扩展为 1110 原子 + 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 后端任务新增任务 0ConfigLoader 基础设施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重试机制简化 |