Files
AIclinicalresearch/docs/03-业务模块/SSA-智能统计分析/04-开发计划/12-Plan-and-Execute分步执行架构开发计划.md
HaHafeng 6edfad032f feat(ssa): finalize strict stepwise agent execution flow
Align Agent mode to strict stepwise generation and execution, add deterministic and safety hardening, and sync deployment/module documentation for Phase 5A.5/5B/5C rollout.

- implement strict stepwise execution path and dependency short-circuiting
- persist step-level errors/results and stream step_* progress events
- add agent plan params patch route and schema/migration support
- improve R sanitizer/security checks and step result rendering in workspace
- update SSA module guide and deployment change checklist

Made-with: Cursor
2026-03-11 22:49:05 +08:00

27 KiB
Raw Blame History

Plan-and-Execute 分步执行架构开发计划

来源:C:\Users\zhibo\.cursor\plans\plan-and-execute_architecture_0895bce2.plan.md
归档日期2026-03-11
归档说明:先完成“一步模式稳定化”,再灰度切入分步执行(与你当前共识一致)


name: Plan-and-Execute Architecture overview: 将 Agent 通道从"一次性生成完整 R 脚本"改造为"分步生成、分步执行"架构。采用代码累加法(零改动 R Docker配合 XML 标签提取、AST+安全预检、防御性 prompt、错误分类短路、确定性执行头注入等工程护栏每步 50-80 行代码独立生成和执行。当前版本仅交付最小分步主链Phase 5A/5A.5/5B/5C单步重跑与级联重跑Phase 5D延期到主链稳定后再启用。PlannerAgent 决定步骤数:简单分析 1 步(等价一次性),复杂分析 3-5 步。 todos:

  • id: p5a content: "Phase 5A: CoderAgent 防错护栏 — XML 标签提取 + AST 预检 + 防御性 prompt + 高保真 Schema 注入" status: pending
  • id: p5a5 content: "Phase 5A.5: 变量确认与可编辑交互层 — 复用 QPER 变量编辑能力(单变量/多变量)到 Agent 计划确认阶段" status: pending
  • id: p5b content: "Phase 5B: 后端分步执行引擎 — DB schema + 代码累加执行循环 + 按步生成 + 错误分类短路 + 新 SSE 事件" status: pending
  • id: p5c content: "Phase 5C: 前端分步展示 — 类型扩展 + AgentCodePanel 多步骤 UI + SSE 处理器" status: pending
  • id: p5d content: "Phase 5D: 单步重跑(延期)— 医生修改指令 + agentRerunStep + 级联重跑后续步骤 + 审计轨迹(主链稳定两周后再启动)" status: cancelled isProject: false

Plan-and-Execute 分步执行架构 (v3 - 场景增强版)

现状与核心痛点

当前 Agent 管线是"一锅炖"模式Planner 生成 4 步计划 -> Coder 一次生成 300 行 R -> Runner 一次执行全部 -> 全成功 or 全失败。

致命缺陷(因果悖论)Step 3多因素回归需要 Step 2单因素分析的实际 P 值来决定纳入变量。LLM 在一次性写代码时不知道 P 值,只能写复杂的动态元编程,崩溃率 95%+。分步执行后LLM 看到真实 P 值,可直接写死 glm(Yqol ~ age + smoke + ...),成功率接近 100%。

编排模型后端驱动CoderAgent 被动生成

核心原则CoderAgent 不控制流程,不知道循环的存在。

ChatHandlerService编排层       CoderAgent代码生成器       R Docker执行器
        │                                │                            │
        │  "请为 Step 1 写代码"           │                            │
        │  + dataSchema                  │                            │
        │  + step.description            │                            │
        ├──────────────────────────────► │                            │
        │                                │  返回 <r_code>...</r_code> │
        │ ◄──────────────────────────────┤                            │
        │                                                             │
        │  accumulatedCode + stepCode                                 │
        ├────────────────────────────────────────────────────────────►│
        │                                                   result    │
        │ ◄──────────────────────────────────────────────────────────┤
        │                                                             │
        │  "请为 Step 2 写代码"           │                            │
        │  + step1 结果摘要              │                            │
        ├──────────────────────────────► │                            │
        │          ...继续循环...         │                            │
  • 编排层ChatHandlerService 拥有全局视角plan、步骤列表、累积代码、前序结果
  • CoderAgent 每次只看到"当前步骤描述 + 前序结果摘要 + 数据 Schema",输出当前步骤的 R 代码
  • CoderAgent 不调用任何工具,不决定是否重试/跳过/终止,这些全由编排层根据错误分类判断
  • 用户点击"修改此步骤"时,请求发送到编排层,编排层调用 CoderAgent 重新生成该步代码

统一架构PlannerAgent 决定步骤数

简单分析和复杂分析共用同一条代码路径,区别仅在于 PlannerAgent 生成的步骤数不同:

用户请求 PlannerAgent 决策 执行循环次数 等价于
"比较两组血压" 1 步:独立样本 T 检验 1 次 一次性执行
"描述统计 + 组间比较" 2 步 2 次 轻量分步
"单因素→多因素→敏感性" 4 步 4 次 完整分步

PlannerAgent 的步骤拆分规则(写入 System Prompt

  • 如果只涉及一种统计方法,合并为 1 步
  • 如果涉及多种方法但彼此独立,可合并为 1-2 步
  • 只有当后续步骤需要前序步骤的运行时结果(因果依赖)时,必须拆为独立步骤
  • 步骤数建议:简单分析 1 步,标准分析 2-3 步,复杂分析 3-5 步

目标架构

flowchart TB
    subgraph planPhase [Phase 1: Plan]
        Planner["PlannerAgent\n生成 N 步计划"]
    end
    subgraph execPhase [Phase 2: Step-by-Step Execute]
        S1_Code["Step 1: Coder 生成代码\n50-80 行"] --> S1_Run["R 执行\n代码累加法"]
        S1_Run --> S1_Result["展示结果\n+ 传递给下一步"]
        S1_Result --> S2_Code["Step 2: Coder 生成代码\n参考 Step 1 结果"]
        S2_Code --> S2_Run["R 执行\nCode_A + Code_B"]
        S2_Run --> S2_Result["展示结果"]
        S2_Result --> SN["Step N: ..."]
    end
    subgraph guards [工程护栏]
        XMLExtract["XML 标签提取"]
        ASTCheck["AST 语法预检"]
        ErrorClass["错误分类短路"]
        DefPrompt["防御性 Prompt"]
    end
    Planner --> S1_Code
    guards -.->|"每步都经过"| S1_Code
    guards -.->|"每步都经过"| S2_Code
    SN --> Summary["LLM 综合总结"]

关键设计决策

  • 代码累加法(零改动 R Docker:每步执行时,将所有前序步骤代码 + 当前步骤代码拼接后一次性发给 R Docker。R Docker 保持无状态,无需 session 池。
  • 不引入独立 Fixer AgentCoderAgent 内置重试 prompt 模板(上下文重置模式),分步执行后每步只有 50-80 行代码,重新生成的成功率本身就很高。
  • 对于 <5000 行的医学数据集,重跑前序步骤 <1 秒,用户无感知。

分三个子阶段实施

Phase 5A: CoderAgent 防错护栏 (~2h)

目标:从 Prompt、代码提取、语法检查三层大幅提升首次生成成功率。

改动文件

  • backend/src/modules/ssa/services/AgentCoderService.ts — Prompt + parseCode
  • r-statistics-service/plumber.R — AST 预检
  • backend/src/modules/ssa/services/SessionBlackboardService.ts — Schema 增强

1) XML 标签提取(替代 Markdown 代码块)

System Prompt 改为要求 <r_code>...</r_code> 标签包裹代码:

你必须且只能将 R 代码放在 <r_code> 和 </r_code> 标签之间。
标签外面禁止出现任何代码。标签里面禁止出现任何自然语言解释。

parseCode() 方法改为正则提取 <r_code> 内容fallback 到 markdown 代码块:

const xmlMatch = content.match(/<r_code>([\s\S]*?)<\/r_code>/);
const mdMatch = content.match(/```r\s*([\s\S]*?)```/);
const code = xmlMatch?.[1]?.trim() || mdMatch?.[1]?.trim();
if (!code || code.length < 20) throw new Error('未找到有效 R 代码');

2) 防御性编程 Prompt 注入

在 System Prompt 的"R 代码规范"中新增防御规则:

## 防御性编程规则(铁律)
1. 模型计算前,强制剔除涉及变量的 NA 值
2. 分组变量强制转 as.factor(),数值变量强制转 as.numeric()
3. 回归前检查因子水平数,只有 1 个水平的变量直接跳过
4. 所有统计检验用 tryCatch 包裹,失败时返回 NA 而非崩溃
5. 禁止假设数据完美,永远做类型和缺失值检查

3) 高保真 Schema 注入

buildDataContext() 增强:除列名和类型外,注入每列的前 3 行样本值:

变量名: age, 类型: numeric, 样本: [45, 67, 32]
变量名: sex, 类型: categorical, 样本: [1, 2, 1], 水平: [1, 2]
变量名: Yqol, 类型: categorical, 样本: [0, 1, 1], 水平: [0, 1]

4) R Docker AST + 安全预检(语法 + 危险调用拦截)

execute-code 端点中,eval() 之前增加双层预检:

tryCatch({
  parsed_code <- parse(text = input$code)

  # Layer A: 静态安全扫描MVP
  forbidden_pattern <- "(^|[^[:alnum:]_])(system|eval|parse|source|file\\.remove|unlink|setwd|download\\.file|readLines|writeLines)\\s*\\("
  if (grepl(forbidden_pattern, input$code, perl = TRUE, ignore.case = TRUE)) {
    stop("Security Violation: Detected forbidden function calls.")
  }
}, error = function(e) {
  return(list(
    status = "error",
    error_code = if (grepl("Security Violation", e$message, fixed = TRUE)) "E_SECURITY" else "E_SYNTAX",
    message = paste0("R 代码预检失败: ", e$message),
    user_hint = "代码存在语法或安全风险(危险函数调用),请修复后重试"
  ))
})

# Layer B: 运行时保护(在 sandbox_env 中覆盖高风险函数)
sandbox_env$system <- function(...) stop("Security Violation: function 'system' is forbidden.")
sandbox_env$eval <- function(...) stop("Security Violation: function 'eval' is forbidden.")
sandbox_env$source <- function(...) stop("Security Violation: function 'source' is forbidden.")
sandbox_env$unlink <- function(...) stop("Security Violation: function 'unlink' is forbidden.")
sandbox_env$file.remove <- function(...) stop("Security Violation: function 'file.remove' is forbidden.")
sandbox_env$setwd <- function(...) stop("Security Violation: function 'setwd' is forbidden.")

# 语法通过后才执行
eval(parsed_code, envir = sandbox_env)

Phase 5A.5: 变量确认与可编辑交互层(复用 QPER~3h

目标:在 Agent 计划确认阶段,系统自动填入每步变量参数,并允许医生修改后再生成代码。
原则复用已有 QPER 能力,不重写。

为什么必须做

  • 如果不让用户在计划阶段改变量CoderAgent 只能写大量“兜底判断代码”,复杂度和失败率都会升高。
  • 变量先确认后编码,可把代码生成约束成“确定输入 -> 直接执行”,显著降低异常分支。

复用资产(已在 QPER 跑通)

  • 前端可编辑控件:SingleVarSelectMultiVarTags
  • 约束规则:backend/src/modules/ssa/config/tool_param_constraints.json
  • 失配检测:detectPlanMismatches
  • 后端参数更新 APIPATCH /api/v1/ssa/workflow/:workflowId/params(结构校验 + 变量存在性校验)

改造策略(最小改动)

  1. 前端复用,不重复实现

    • WorkflowTimeline 中的变量编辑子能力抽离为可复用组件(建议迁移到 components/param-editors/)。
    • AgentCodePanelplan_pending 阶段渲染“步骤变量编辑区”,交互行为与 QPER 一致。
  2. 后端新增 Agent 参数更新端点(而非复用 workflow PATCH

    • 因 Agent 没有 workflowId/ssa_workflow_steps,新增:
    • PATCH /api/v1/ssa/agent-executions/:executionId/plan-params
    • ssa_agent_executions.review_result.steps[].params 内更新参数。
    • 参数约束复用 tool_param_constraints.json,避免双份规则漂移。
  3. 执行前强校验(三层)

    • Layer 1: 前端黄条提醒(类型/水平不匹配)
    • Layer 2: 后端 PATCH 校验(结构、变量存在)
    • Layer 3: 点击“确认计划”时阻断弹窗(告知可能失败,允许强行继续)
  4. 编码输入确定化

    • agentStreamCode 读取“用户已确认后的 steps.params”作为唯一输入。
    • CoderAgent Prompt 明确:按已确认变量写代码,不要再自动发散变量选择。

验收标准

  • Agent 计划生成后,步骤中变量默认自动填入。
  • 医生可修改单变量/多变量并保存,右侧实时更新。
  • 修改后的参数会进入后端持久化execution.reviewResult并参与后续代码生成。
  • 用户可在不改计划结构的情况下,仅通过改变量降低执行失败率。
  • 不新增第二套变量编辑逻辑QPER 与 Agent 共用同一套约束与交互组件)。

Phase 5B: 后端分步执行引擎(含确定性保障,~4h

目标agentExecuteCode 从"一次执行全部"改为"逐步生成+逐步执行"循环,采用代码累加法,并强制保证重跑确定性。

改动文件

  • backend/src/modules/ssa/services/ChatHandlerService.ts — 核心执行循环
  • backend/src/modules/ssa/services/AgentCoderService.ts — 按步骤生成代码
  • backend/src/modules/ssa/services/CodeRunnerService.ts — 代码累加包装
  • backend/prisma/schema.prisma — 步骤级存储

1) DB Schema 扩展

SsaAgentExecution 新增两个字段:

model SsaAgentExecution {
  // ... 现有字段 ...
  stepResults     Json?    @map("step_results")   // Array<AgentStepResult>
  currentStep     Int?     @map("current_step")
}

AgentStepResult 结构:

interface AgentStepResult {
  stepOrder: number;
  method: string;
  status: 'pending' | 'coding' | 'executing' | 'completed' | 'error' | 'skipped';
  code?: string;
  reportBlocks?: ReportBlock[];
  errorMessage?: string;
  retryCount: number;
  durationMs?: number;
}

2) 代码累加执行循环 (agentExecuteCode 重构)

accumulatedCode = ""   // 累积的成功代码
previousResults = []   // 前序步骤摘要(供 CoderAgent 参考)

for each step in plan.steps:
  1. CoderAgent.generateStepCodeStream(plan, step, previousResults)
     → SSE: step_coding { stepOrder, partialCode }
     → SSE: step_code_ready { stepOrder, code }
  
  2. fullCode = deterministicHeader + accumulatedCode + "\n" + stepCode
     CodeRunner.executeCode(sessionId, fullCode)
     → SSE: step_executing { stepOrder }
  
  3. if success:
     accumulatedCode = fullCode  // 累积成功代码
     previousResults.push(stepResultSummary)
     → SSE: step_result { stepOrder, reportBlocks }
  
  4. if error:
     → 错误分类判断:
       - Fatal (singular matrix / OOM): 硬阻断, SSE: pipeline_aborted
       - Fixable (object not found / syntax): 重试该步骤 (MAX 2 次)
       - 重试仍失败: 标记 skipped, 继续下一步
     → SSE: step_error { stepOrder, error, willRetry, isFatal }

全部步骤完成后 → LLM 综合总结

2.1) 确定性执行头P0必须

在 Node.js 拼接 fullCode必须注入确定性头避免“Step 1 重跑导致 Step 2 输入漂移”的隐性污染:

const baseSeed = deriveStableSeed({
  sessionId,
  datasetHash,   // 数据快照哈希
  executionId,   // 本次执行 ID
});
const stepSeed = (baseSeed + step.order) % 2147483647;

const deterministicHeader = [
  "# --- 系统强制注入:保证累加执行确定性 ---",
  `set.seed(${stepSeed})`,
  "RNGkind('Mersenne-Twister', 'Inversion', 'Rejection')",
  "options(warn = 1)",
  "# --------------------------------------",
  ""
].join("\n");

const fullCode = deterministicHeader + accumulatedCode + "\n" + stepCode;

约束:

  • 不允许硬编码固定种子(如全局 42)作为唯一策略;
  • 必须记录 baseSeed/stepSeed/datasetHash 到执行审计字段,保证结果可追溯。

3) CoderAgent 按步骤生成 — 新增 buildStepMessage 方法

private buildStepMessage(
  plan: AgentPlan,
  step: PlanStep,
  previousResults: StepResultSummary[],
): string {
  // 传入: 当前步骤描述 + 前序步骤的关键发现
  // 例如: "Step 2 单因素分析发现 age(P=0.03), smoke(P=0.08) 显著"
  // 关键 prompt: "R 环境中已有 df。之前步骤的代码已执行变量可直接使用。"
  // 要求: 只生成当前步骤的代码,以 <r_code> 标签包裹
}

4) 错误分类短路机制

CodeRunnerService 返回错误后,后端根据 error_code 判断:

const FATAL_ERRORS = ['E005', 'E_OOM', 'E_TIMEOUT'];
const RETRIABLE_ERRORS = ['E001', 'E002', 'E_EXEC', 'E_SYNTAX', 'E100'];

function classifyError(errorCode: string): 'fatal' | 'retriable' {
  if (FATAL_ERRORS.includes(errorCode)) return 'fatal';
  return 'retriable';
}

Fatal 错误直接中断管线,不浪费 Token 重试。

5) 重试时的上下文重置

重试不 append 到长对话,而是构造干净的 3 元素输入:

private buildStepRetryMessage(
  step: PlanStep,
  failedCode: string,
  errorDetail: string,
  dataSchema: string,
): string {
  return `当前步骤的代码执行失败。

<original_code>${failedCode}</original_code>
<error_log>${errorDetail}</error_log>
<data_schema>${dataSchema}</data_schema>

请先分析错误的根本原因,然后输出修复后的完整代码(用 <r_code> 标签包裹)。`;
}

6) 新增 SSE 事件类型

  • step_coding — 步骤 N 代码流式生成中 { stepOrder, partialCode }
  • step_code_ready — 步骤 N 代码生成完成 { stepOrder, code }
  • step_executing — 步骤 N 正在执行 { stepOrder }
  • step_result — 步骤 N 执行成功 { stepOrder, reportBlocks, durationMs }
  • step_error — 步骤 N 执行失败 { stepOrder, error, willRetry, isFatal }
  • step_skipped — 步骤 N 被跳过 { stepOrder, reason }
  • pipeline_aborted — 管线因致命错误终止 { stepOrder, error }

Phase 5C: 前端分步展示 (~4h)

目标AgentCodePanel 变为多步骤视图,每步独立展示代码、状态、结果。

改动文件

  • frontend-v2/src/modules/ssa/types/index.ts — 类型扩展
  • frontend-v2/src/modules/ssa/hooks/useSSAChat.ts — 新 SSE 事件处理
  • frontend-v2/src/modules/ssa/stores/ssaStore.ts — 步骤级状态
  • frontend-v2/src/modules/ssa/components/AgentCodePanel.tsx — 多步骤 UI

1) 类型扩展

type StepStatus = 'pending' | 'coding' | 'executing' | 'completed' | 'error' | 'skipped';

interface AgentStepResult {
  stepOrder: number;
  method: string;
  status: StepStatus;
  code?: string;
  partialCode?: string;
  reportBlocks?: ReportBlock[];
  errorMessage?: string;
  retryCount: number;
  durationMs?: number;
}

interface AgentExecutionRecord {
  // ... 现有字段保留 ...
  stepResults?: AgentStepResult[];
  currentStep?: number;
}

2) AgentCodePanel 多步骤 UI

+------------------------------------------+
| Agent 分析流水线           [完成 3/4]     |
+------------------------------------------+
| [计划] 4 个分析步骤               已确认  |
+------------------------------------------+
| Step 1: 描述性统计         completed 2.1s |
|   [可折叠] 代码 / DynamicReport          |
+------------------------------------------+
| Step 2: 单因素分析         completed 5.3s |
|   [可折叠] 代码 / DynamicReport          |
+------------------------------------------+
| Step 3: 多因素回归         executing 12s  |
|   [展开] 流式代码 + 计时器               |
+------------------------------------------+
| Step 4: 敏感性分析         pending        |
+------------------------------------------+
  • 每个步骤可折叠/展开(当前步骤默认展开,已完成步骤默认折叠)
  • 已完成步骤展示 DynamicReport(表格、图表),可展开查看代码
  • 正在执行步骤展示流式代码 + 计时器
  • 失败步骤展示错误详情 + "重试此步骤" 按钮
  • 跳过步骤灰色显示 + 原因说明
  • 进度指示器:[完成 3/4][Step 3/4 执行中]

3) SSE 事件处理

useSSAChat.ts 中为每种 step_* 事件添加处理器,更新 stepResults[] 数组中对应 stepOrder 的状态。Store 中新增 updateStepResult(stepOrder, patch) action。

4) 导出报告和查看代码

导出报告:累积所有步骤的 reportBlocks 合并为一个文档。
查看代码:拼接所有步骤的 code,按步骤分段注释。

Phase 5D: 单步重跑 — 医生介入修改(延期,不在当前版本)

状态:延期。当前版本不交付该能力,待 Phase 5A-5C 线上稳定运行两周后再启动。
目标(延期后):医生可以对任意已完成步骤提出修改指令,系统仅重跑该步骤及其后续步骤。

高阶用户场景

场景 1强行纳入临床意义变量Forced Entry

  • Step 2 结果age(P=0.03), smoke(P=0.08), gender(P=0.15)
  • AI 的 Step 3 代码排除了 genderP>0.1
  • 医生凭临床常识,点击 Step 3 的"修改此步骤",输入"请把 Gender 也纳入模型作为混杂因素"
  • 系统仅重新生成并执行 Step 3-4Step 1-2 不受影响

场景 2图表样式个性化微调

  • 最后一步画了彩色生存曲线,但期刊要求黑白灰度图
  • 医生输入"改成黑白配色并加上 95% 置信区间带"
  • 系统仅重写最后一步画图代码,前序清洗和拟合完全不重跑

改动文件

  • (延期)backend/src/modules/ssa/services/ChatHandlerService.ts — 新增 agentRerunStep 方法
  • (延期)backend/src/modules/ssa/routes/chat.routes.ts — 新增 rerun_step agentAction
  • (延期)frontend-v2/src/modules/ssa/components/AgentCodePanel.tsx — 步骤卡片增加"修改此步骤"按钮

1) 后端 agentRerunStep 方法

async agentRerunStep(
  executionId: string,
  stepOrder: number,
  userInstruction: string,
  sseWriter: SSEWriter,
) {
  const execution = await this.getExecution(executionId);
  const stepResults = execution.stepResults as AgentStepResult[];

  // 1. 取出 Step 1..N-1 的已有代码作为累积前缀
  const accumulatedCode = stepResults
    .filter(s => s.stepOrder < stepOrder && s.status === 'completed')
    .map(s => s.code)
    .join('\n');

  // 2. 取出前序步骤结果摘要
  const previousResults = stepResults
    .filter(s => s.stepOrder < stepOrder && s.status === 'completed')
    .map(s => this.summarizeStepResult(s));

  // 3. 调用 CoderAgent 重新生成该步骤代码(带用户修改指令)
  const newCode = await this.coderAgent.generateStepCode({
    plan: execution.plan,
    step: execution.plan.steps[stepOrder - 1],
    previousResults,
    userInstruction,   // "请把 Gender 也纳入模型"
    dataSchema: await this.getDataSchema(execution.sessionId),
  });

  // 4. 执行:累积前缀 + 新代码
  const fullCode = accumulatedCode + '\n' + newCode;
  const result = await this.codeRunner.execute(execution.sessionId, fullCode);

  // 5. 更新 stepResults[stepOrder] 并标记后续步骤为 pending
  // 6. 如果还有后续步骤,级联重跑
  for (let i = stepOrder; i < plan.steps.length; i++) {
    // ... 继续分步执行循环
  }
}

2) 前端交互

+------------------------------------------+
| Step 3: 多因素回归         completed 8.2s |
|   glm(Yqol ~ age + smoke, ...)           |
|   [查看代码] [查看结果] [✏️ 修改此步骤]    |
+------------------------------------------+
       ↓ 点击"修改此步骤"
+------------------------------------------+
| 请输入修改指令:                           |
| [请把 Gender 也纳入模型作为混杂因素____]    |
|                         [确认修改并重跑]   |
+------------------------------------------+
       ↓ 确认后
+------------------------------------------+
| Step 3: 多因素回归         coding...      |
|   [流式代码生成中...]  标签: 🔄 已修改     |
+------------------------------------------+
| Step 4: 敏感性分析         pending ⏳      |
|   (等待 Step 3 完成后自动执行)             |
+------------------------------------------+
  • 已修改步骤标记 🔄 已修改(用户干预),保留审计轨迹
  • 该步骤之后的所有步骤自动重置为 pending,级联重跑
  • 左侧对话区追加审计消息:"✏️ 用户修改了步骤 3请把 Gender 也纳入模型"

3) SSE 事件

  • step_rerun — 步骤 N 被用户修改并重新执行 { stepOrder, userInstruction }

未来扩展V2 考虑MVP 不做)

算法 A/B 分支测试

场景:前 3 步完全相同Step 4 想对比 Logistic 回归 vs Random Forest 的效果。

实现思路:在 Step 4 处"开叉"stepResults 从线性数组扩展为树结构,支持同一 stepOrder 的多个 variant。前端并列展示两个 Step 4 变体的结果。

MVP 降级方案:医生先执行 Logistic 版,看完结果后点击"修改此步骤"改为 RF 版。虽然不能并列对比,但功能上可用。

Human-in-the-loop 步骤间确认

场景Step 2 跑完后,系统暂停并询问医生:"基于 P<0.1 规则AI 拟将 age, smoke 纳入回归。您是否需要强制纳入其他变量?"

实现思路:编排循环在特定步骤后挂起(await userConfirmation()),等待前端 confirm_step 事件后继续。

不需要改动的部分

  • PlannerAgent:计划格式不变,steps[] 结构已具备 order/method/description
  • DynamicReport:复用,每步结果用同一组件渲染
  • 左侧对话区审计轨迹:保持不变
  • R Docker execute-code 端点:保持无状态(仅新增 AST 预检)

明确不做的事项MVP

  • 不引入独立 Fixer AgentCoderAgent 内置重试 prompt 模板即可)
  • 不做 R session 内存池(代码累加法零改动 R Docker
  • 不做 RData 序列化/NAS 共享存储MVP 单实例,数据量小)
  • 不做错题本 RAG数据量不足延后至系统运行 3 个月后评估)
  • 不做 A/B 分支并列展示(降级为"修改此步骤"手动切换)
  • 不做 Human-in-the-loop 步骤间自动暂停确认(医生可通过"修改此步骤"事后干预)
  • 不做单步重跑/级联重跑Phase 5D 延期到主链稳定两周后)

风险和注意事项

  • 代码累加法的确定性:必须注入 deterministicHeader 并记录种子与数据哈希,避免随机抽样/插补导致的结果漂移。
  • 代码累加法的性能:后续步骤重跑前序代码。对 <5000 行数据集影响 <1 秒。若未来遇到大数据集,可升级为 RData 快照法。
  • 步骤间依赖CoderAgent 需获得前步骤的关键发现摘要P 值、显著变量等),通过 previousResults 传递。
  • 错误分类准确性:R_ERROR_MAPPING 需持续扩充,以正确区分 fatal vs retriable。
  • 步骤跳过后的总结LLM 综合总结时必须标注哪些步骤被跳过及原因。
  • 安全预检边界:parse() 仅覆盖语法,必须叠加危险函数拦截与运行时覆盖;后续可升级 AST 深度扫描以降低绕过风险。