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

624 lines
27 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.
# 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 不控制流程,不知道循环的存在。**
```text
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 步
## 目标架构
```mermaid
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 Agent**CoderAgent 内置重试 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>` 标签包裹代码:
```text
你必须且只能将 R 代码放在 <r_code> 和 </r_code> 标签之间。
标签外面禁止出现任何代码。标签里面禁止出现任何自然语言解释。
```
`parseCode()` 方法改为正则提取 `<r_code>` 内容fallback 到 markdown 代码块:
```typescript
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 代码规范"中新增防御规则:
```text
## 防御性编程规则(铁律)
1. 模型计算前,强制剔除涉及变量的 NA 值
2. 分组变量强制转 as.factor(),数值变量强制转 as.numeric()
3. 回归前检查因子水平数,只有 1 个水平的变量直接跳过
4. 所有统计检验用 tryCatch 包裹,失败时返回 NA 而非崩溃
5. 禁止假设数据完美,永远做类型和缺失值检查
```
**3) 高保真 Schema 注入**
`buildDataContext()` 增强:除列名和类型外,注入每列的前 3 行样本值:
```text
变量名: 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()` 之前增加双层预检:
```r
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 跑通)**
- 前端可编辑控件:`SingleVarSelect``MultiVarTags`
- 约束规则:`backend/src/modules/ssa/config/tool_param_constraints.json`
- 失配检测:`detectPlanMismatches`
- 后端参数更新 API`PATCH /api/v1/ssa/workflow/:workflowId/params`(结构校验 + 变量存在性校验)
**改造策略(最小改动)**
1. **前端复用,不重复实现**
-`WorkflowTimeline` 中的变量编辑子能力抽离为可复用组件(建议迁移到 `components/param-editors/`)。
- `AgentCodePanel``plan_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` 新增两个字段:
```prisma
model SsaAgentExecution {
// ... 现有字段 ...
stepResults Json? @map("step_results") // Array<AgentStepResult>
currentStep Int? @map("current_step")
}
```
`AgentStepResult` 结构:
```typescript
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` 重构)
```text
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 输入漂移”的隐性污染:
```typescript
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` 方法
```typescript
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` 判断:
```typescript
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 元素输入:
```typescript
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) 类型扩展**
```typescript
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**
```text
+------------------------------------------+
| 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` 方法**
```typescript
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) 前端交互**
```text
+------------------------------------------+
| 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 深度扫描以降低绕过风险。