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
This commit is contained in:
2026-03-11 22:49:05 +08:00
parent d3b24bd8c3
commit 6edfad032f
19 changed files with 2105 additions and 158 deletions

View File

@@ -1,13 +1,20 @@
# SSA智能统计分析模块 - 当前状态与开发指南
> **文档版本:** v4.2
> **文档版本:** v4.3
> **创建日期:** 2026-02-18
> **最后更新:** 2026-03-08
> **最后更新:** 2026-03-11
> **维护者:** 开发团队
> **当前状态:** 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A + 双通道架构 Phase 1-3 + Agent 通道体验优化 + Agent Prompt 运营管理化完成**
> **当前状态:** 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A + 双通道架构 + Agent Prompt 运营管理化 + Phase 5A/5A.5/5B/5C 联调完成(严格分步写+分步执行)**
> **文档目的:** 快速了解SSA模块状态为新AI助手提供上下文
>
> **最新进展2026-03-08 Agent 核心 Prompt 接入运营管理端**
> **最新进展2026-03-11 Agent 分步执行主链落地**
> - ✅ **严格分步模式切换** — `confirm_plan` 不再生成整段 R 代码,仅进入执行确认;`confirm_code` 后按步骤逐步生成与执行
> - ✅ **依赖失败短路** — 当前步骤重试失败后,后续步骤直接标记 `skipped`,不再继续写代码与执行
> - ✅ **步骤结果可视化增强** — 右侧工作区在分步状态可展开查看每步 `reportBlocks`,便于排障与审计
> - ✅ **失败原因可追溯** — `stepResults.errorMessage` 落库并可回显,定位链路稳定
> - ✅ **R 语法修复器纠偏** — 修正 `} else` 处理策略,降低 `unexpected 'else'` 误报
>
> **此前进展2026-03-08 Agent 核心 Prompt 接入运营管理端):**
> - ✅ **PlannerAgent Prompt 动态化** — `AgentPlannerService.buildSystemPrompt()` 改为 `PromptService.get('SSA_AGENT_PLANNER', { dataContext })`,支持运营管理端在线编辑、灰度预览、版本管理
> - ✅ **CoderAgent Prompt 动态化** — `AgentCoderService.buildSystemPrompt()` 改为 `PromptService.get('SSA_AGENT_CODER', { dataContext })`,同上
> - ✅ **三级容灾** — 数据库 ACTIVE 版本 → 内存缓存5 分钟) → 代码 fallback`prompt.fallbacks.ts`),任何一层失败自动降级
@@ -88,12 +95,12 @@
| 项目 | 信息 |
|------|------|
| **模块名称** | SSA - 智能统计分析 (Smart Statistical Analysis) |
| **模块定位** | AI驱动的"白盒"统计分析系统 → 升级为"数据感知的统计顾问" |
| **模块定位** | AI驱动的"白盒"统计分析系统 → 升级为"数据感知的统计顾问"Agent 严格分步执行) |
| **架构模式** | **双通道QPER 管线(预制工具)+ LLM Agent 通道(代码生成)** + **四层七工具 + 对话层 LLM** |
| **前端状态模型** | **Unified Record Architecture — 一次分析 = 一个 Record = N 个 Steps** |
| **商业价值** | ⭐⭐⭐⭐⭐ 极高 |
| **目标用户** | 临床研究人员、生物统计师 |
| **开发状态** | 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A + 双通道架构 Phase 1-3 + Agent 体验优化完成** |
| **开发状态** | 🎉 **QPER 主线闭环 + Phase I-IV + Phase V-A + 双通道架构 + Agent 体验优化 + Phase 5A/5A.5/5B/5C 联调完成** |
### 核心目标
@@ -203,8 +210,8 @@ AnalysisRecord {
| **Plan-and-Execute 设计** | **分步执行架构设计(代码累加 + 工程护栏)** | **~4h** | ✅ **已完成(架构评审 + 三份评估报告)** | 2026-03-07 |
| **Phase 5A** | **CoderAgent 防错护栏XML 标签 + AST 预检 + 防御性 Prompt + 高保真 Schema** | **~6h** | ✅ **已完成** | 2026-03-08 |
| **Agent Prompt 管理化** | **PlannerAgent + CoderAgent Prompt 接入运营管理端PromptService 三级容灾)** | **~2h** | ✅ **已完成(种子脚本 + fallback + 文档)** | 2026-03-08 |
| **Phase 5B** | **后端分步执行引擎(DB schema + 代码累加循环 + 错误分类短路 + 新 SSE 事件)** | **~10h** | 📋 待开始 | - |
| **Phase 5C** | **前端分步展示(类型扩展 + AgentCodePanel 多步骤 UI + SSE 处理器)** | **~6h** | 📋 待开始 | - |
| **Phase 5B** | **后端分步执行引擎(确定性种子 + 分步生成执行 + 错误分类短路 + step_* 事件)** | **~10h** | **已完成(严格分步主链)** | 2026-03-11 |
| **Phase 5C** | **前端分步展示(类型扩展 + AgentCodePanel 多步骤 UI + SSE 处理器)** | **~6h** | **已完成(步骤状态与结果可视化)** | 2026-03-11 |
| **Phase V-B** | **反思编排 + 高级特性** | **18h** | 📋 待开始 | - |
| **Phase VI** | **集成测试 + 可观测性** | **10h** | 📋 待开始 | - |
@@ -229,7 +236,7 @@ AnalysisRecord {
| **Phase IV 前端** | useSSAChatanalysis_plan+plan_confirmed SSE 处理+pendingPlanConfirm→executeWorkflow+ SSAChatPaneAskUserCard 渲染+幽灵卡片清除 H2 | ✅ |
| **Phase V-A 后端** | PATCH /workflow/:id/paramsZod 结构校验防火墙)+ tool_param_constraints.json12 工具参数约束)+ inferGroupingVar 恢复(默认填充分组变量) | ✅ |
| **Phase V-A 前端** | WorkflowTimeline 可编辑化SingleVarSelect + MultiVarTags + 三层柔性拦截)+ ssaStore updateStepParams + SSAWorkspacePane 同步阻塞执行 + DynamicReport 对象 rows 兼容 + Word 导出修复 | ✅ |
| **双通道 Agent 通道** | PlannerAgent意图→分析计划+ CoderAgent计划→R 代码,含流式生成)+ CodeRunnerService沙箱执行+ AgentCodePanel三步确认 UI+ ModeToggle通道切换+ R Docker /execute-code 端点 | ✅ |
| **双通道 Agent 通道** | PlannerAgent意图→分析计划+ CoderAgent按步骤生成 R 代码,执行阶段逐步生成)+ CodeRunnerService沙箱执行+ AgentCodePanel三步确认 UI+ ModeToggle通道切换+ R Docker /execute-code 端点 | ✅ |
| **Agent 体验优化** | 方案 B 左右职责分离(视线牵引+状态互斥+历史穿梭)+ JWT 刷新 + 代码截断修复 + 重试流式生成 + R Docker 结构化错误20+ 模式)+ Prompt 铁律 + parseCode 健壮化 + consoleOutput 类型防御 + 进度条同步 + 导出/查看代码恢复 + ExecutingProgress 动态 UI | ✅ |
| **Agent Prompt 管理化** | PlannerAgent + CoderAgent System Prompt 从硬编码迁移至 PromptService 动态加载;运营管理端在线编辑/灰度预览/版本回滚三级容灾DB→缓存→fallback种子脚本 `seed-ssa-agent-prompts.ts` 幂等 | ✅ |
| **测试** | QPER 端到端 40/40 + 集成测试 7 Bug 修复 + Phase I E2E 31/31 + Phase II E2E 38/38 + Phase III E2E 13/13+4skip + Phase IV E2E 25/25 + Phase V-A 前后端集成测试通过 + 双通道 E2E 8/8 通过 + Agent 体验测试通过(统计分析结果+图表正常) | ✅ |
@@ -401,9 +408,11 @@ npx tsx prisma/seed-ssa-agent-prompts.ts # Agent: SSA_AGENT_PLANNER + SSA
```
用户消息
→ ChatHandlerService.handleAgentMode()
→ AgentPlannerService.generatePlan() ← SSA_AGENT_PLANNER
AgentCoderService.generateCodeStream() ← SSA_AGENT_CODER
CodeRunnerService.executeCode() ← 纯 R 执行,无 Prompt
→ AgentPlannerService.generatePlan() ← SSA_AGENT_PLANNER
confirm_plan: enter code_pending only ← 不提前生成整段代码
confirm_code: for each step
→ AgentCoderService.generateStepCodeStream() ← SSA_AGENT_CODER
→ CodeRunnerService.executeCode() ← 纯 R 执行,无 Prompt
```
---
@@ -429,22 +438,18 @@ npx tsx prisma/seed-ssa-agent-prompts.ts # Agent: SSA_AGENT_PLANNER + SSA
### 近期(优先级高)
1. **Phase 5A — CoderAgent 防错护栏**
- XML 标签提取:强制 `<r_code>...</r_code>` 标签 + `parseCode()` 严格正则
- 防御性 PromptNA 处理 / 类型转换 / 因子水平检查 / tryCatch 规则注入
- 高保真 Schema 注入:`buildDataContext()` 增加列类型 + 前 3 条样本值
- R Docker AST 预检:`parse()` 语法检查在 `eval()` 之前
1. **稳定性回归与压测**
- 严格分步主链:计划确认不生成代码、执行阶段逐步生成与执行
- 依赖短路:上游失败时后续步骤必须 `skipped`
- DB 回显:`stepResults/errorMessage/seedAudit` 全链路可追溯
2. **Phase 5B — 后端分步执行引擎**
- DB: `SsaAgentExecution` 新增 `stepResults: Json[]` + `currentStep: Int?`
- 代码累加执行循环R Docker 保持无状态,每步累加前序成功代码)
- 错误分类短路Fatal→硬停 / Retriable→重试 MAX 2 / Soft→跳过
- 新 SSE 事件:`step_coding / step_code_ready / step_executing / step_result / step_error / step_skipped / pipeline_aborted`
2. **Phase V-B — 反思编排 + 高级特性**
- 完成分步结果汇总与反思层输出增强
- 细化失败后的人类可读修复建议
3. **Phase 5C前端分步展示**
- 类型扩展:`AgentExecutionRecord` 增加 `stepResults[]` + `currentStep`
- AgentCodePanel 多步骤 UI可折叠步骤卡片 + 状态/代码/结果/错误)
- SSE 处理器适配新步骤级事件
3. **Phase VI集成测试 + 可观测性**
- 完善 step 级日志、指标和告警
- 联调验证清单标准化
### 中期
@@ -497,7 +502,7 @@ npx tsx prisma/seed-ssa-agent-prompts.ts # Agent: SSA_AGENT_PLANNER + SSA
---
**文档版本:** v4.2
**最后更新:** 2026-03-08
**当前状态:** 🎉 SSA Agent 模式 MVP 完成QPER 闭环 + Phase I-IV + Phase V-A + 双通道架构 + Agent 体验优化 + Prompt 运营管理化 + Phase 5A 护栏
**下一步:** Phase 5B分步执行引擎→ Phase 5C前端分步展示)→ Phase V-B反思编排
**文档版本:** v4.3
**最后更新:** 2026-03-11
**当前状态:** 🎉 SSA Agent 模式已进入严格分步执行QPER 闭环 + Phase I-IV + Phase V-A + Prompt 运营管理化 + Phase 5A/5A.5/5B/5C
**下一步:** 稳定性回归与压测 → Phase V-B反思编排)→ Phase VI可观测性

View File

@@ -0,0 +1,623 @@
# 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 深度扫描以降低绕过风险。

View File

@@ -0,0 +1,41 @@
# 2026-03-11 Agent Phase 5A.5 联调验证清单
## 验证目标
- 确认 Agent 计划阶段每步都带有 `toolCode + params`,可进入变量编辑态。
- 确认变量编辑保存接口可用:`PATCH /api/v1/ssa/agent-executions/:executionId/plan-params`
- 确认“确认计划”前会自动保存未提交的变量改动。
- 确认分步执行事件 `step_*` 在右侧工作区可视化正常。
## 前置条件
- 后端已应用迁移:`20260311_add_ssa_agent_step_seed_fields`
- 前端/后端/R 服务均使用本次代码构建并启动。
- 会话中已上传可用于回归或分组比较的数据集(包含分类与连续变量)。
## 联调步骤
1. 在 SSA 页面发起 Agent 分析请求(例如“做单因素+多因素回归”)。
2. 等待右侧出现“分析计划”,检查每个步骤是否可见变量参数编辑控件。
3. 修改至少一个单变量参数(如 `group_var`)与一个多变量参数(如 `predictors`)。
4. 点击“保存变量修改”,预期出现“变量参数已保存”提示,无报错。
5. 刷新页面后回到同一会话,确认步骤参数仍为保存后的值(持久化生效)。
6. 再次修改参数但不点“保存”,直接点“确认计划,生成代码”。
7. 预期系统先自动保存参数,再进入代码生成(`coding`),无“参数丢失”。
8. 点击“确认并执行代码”,观察右侧出现分步状态流转:`coding -> executing -> completed/error/skipped`
9. 若有失败步骤,检查错误信息显示在对应步骤卡片,不影响其余步骤状态展示。
10. 执行完成后,确认结果区 `DynamicReport` 正常渲染且可导出。
## 验收标准
- 计划步骤参数可编辑、可保存、可恢复。
- 自动保存逻辑在“确认计划”入口生效。
- `step_*` 事件驱动的步骤状态、耗时、错误信息显示正确。
- 未出现左侧对话区替代右侧工作区更新的回归问题。
## 常见失败点与排查
- 若无变量编辑控件:检查后端 `agent_plan_ready` 是否带 `steps[].params`
- 若保存失败 400检查变量是否存在于 `session.dataSchema.columns`
- 若保存失败 409检查执行状态是否仍为 `plan_pending`
- 若步骤状态不更新:检查 SSE 是否收到 `step_*` 事件,前端控制台是否有解析错误。

View File

@@ -4,7 +4,7 @@
> **维护规则**: 每次修改 Schema / 新增依赖 / 改配置时,**立即**在此文档追加记录
> **Cursor Rule**: `.cursor/rules/deployment-change-tracking.mdc` 会自动提醒
> **最后清零**: 2026-03-100310 部署完成后清零)
> **本次变更**: 无(当前待部署清单已清零
> **本次变更**: 已新增待部署项2026-03-11含 Agent 严格分步执行模式
---
@@ -16,19 +16,24 @@
| # | 变更内容 | 迁移文件 | 优先级 | 备注 |
|---|---------|---------|--------|------|
| — | *暂无* | | | |
| DB-1 | SSA Agent 执行记录新增分步执行与种子审计字段(`step_results/current_step/seed_audit` | `20260311_add_ssa_agent_step_seed_fields` | 高 | 按数据库规范生成Shadow DB 失败后采用降级流程产出 SQL并已人工收敛为仅本次字段变更 |
### 后端变更 (Node.js)
| # | 变更内容 | 涉及文件 | 需要操作 | 备注 |
|---|---------|---------|---------|------|
| — | *暂无* | | | |
| BE-1 | SSA Agent 执行链路增加确定性种子注入、错误分类、seed 审计透传 + 分步执行事件step_* | `backend/src/modules/ssa/services/ChatHandlerService.ts`, `backend/src/modules/ssa/services/CodeRunnerService.ts`, `backend/src/modules/ssa/services/AgentCoderService.ts` | 重新构建镜像 | 与 DB-1 配套上线,确保执行可复现与可追溯 |
| BE-2 | 新增 Agent 计划参数编辑接口 `PATCH /api/v1/ssa/agent-executions/:executionId/plan-params`(复用参数约束配置) | `backend/src/modules/ssa/routes/agent-execution.routes.ts`, `backend/src/modules/ssa/index.ts` | 重新构建镜像 | Phase 5A.5 后端入口,限制 `plan_pending` 状态可编辑 |
| BE-3 | Agent 切换为严格分步模式:`confirm_plan` 不生成整段代码,执行阶段统一按步骤生成 + 失败后依赖短路跳过后续步骤 | `backend/src/modules/ssa/services/ChatHandlerService.ts` | 重新构建镜像 | 修复“第3步失败仍尝试第4步”问题降低无效重试与误导性结果 |
| BE-4 | R 代码语法修复器纠正 `} else` 处理策略,避免引入 `unexpected 'else'` | `backend/src/modules/ssa/services/CodeRunnerService.ts` | 重新构建镜像 | 修复线上语法错误噪声,减少重试失败 |
### 前端变更
| # | 变更内容 | 涉及文件 | 需要操作 | 备注 |
|---|---------|---------|---------|------|
| — | *暂无* | | | |
| FE-1 | Agent 通道接入 step_* SSE 事件并展示分步执行状态(兼容旧 code_* 事件) | `frontend-v2/src/modules/ssa/hooks/useSSAChat.ts`, `frontend-v2/src/modules/ssa/components/AgentCodePanel.tsx`, `frontend-v2/src/modules/ssa/types/index.ts`, `frontend-v2/src/modules/ssa/stores/ssaStore.ts` | 重新构建镜像 | 右侧工作区可见每步状态/错误/耗时,便于排障 |
| FE-2 | Agent 计划阶段复用 QPER 变量编辑控件(单变量/多变量)并接入保存、确认前自动保存 | `frontend-v2/src/modules/ssa/components/AgentCodePanel.tsx`, `frontend-v2/src/modules/ssa/components/WorkflowTimeline.tsx`, `frontend-v2/src/modules/ssa/components/SSAWorkspacePane.tsx` | 重新构建镜像 | 对接 `PATCH /agent-executions/:executionId/plan-params`,实现 5A.5 前后端闭环 |
| FE-3 | Agent 工作区增强:在分步状态下可展开查看每步已生成结果(`reportBlocks`),并兼容严格分步模式下的 `code_pending` 空代码预览 | `frontend-v2/src/modules/ssa/components/AgentCodePanel.tsx`, `frontend-v2/src/modules/ssa/hooks/useSSAChat.ts` | 重新构建镜像 | 修复“有结果但代码下方不可见”与状态显示误导问题 |
### Python 微服务变更
@@ -40,7 +45,7 @@
| # | 变更内容 | 涉及文件 | 需要操作 | 备注 |
|---|---------|---------|---------|------|
| — | *暂无* | | | |
| R-1 | execute-code 端点升级为语法+安全双层预检,新增 E_SECURITY 与运行时高危函数拦截 | `r-statistics-service/plumber.R` | 重新构建镜像 | 阻断 system/eval/source/file.remove/setwd 等风险调用 |
### 环境变量 / 配置变更