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>
1298 lines
82 KiB
Markdown
1298 lines
82 KiB
Markdown
# 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<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,
|
||
};
|
||
}
|
||
|
||
// Fallback:LLM 调用失败时降级到现有正则匹配(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 上限防护。
|
||
|
||
**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<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-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<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/卡方/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<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/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:重试机制简化) |
|