feat(ssa): Complete Phase 2A frontend integration - multi-step workflow end-to-end

Phase 2A: WorkflowPlannerService, WorkflowExecutorService, Python data quality, 6 bug fixes, DescriptiveResultView, multi-step R code/Word export, MVP UI reuse. V11 UI: Gemini-style, multi-task, single-page scroll, Word export. Architecture: Block-based rendering consensus (4 block types). New R tools: chi_square, correlation, descriptive, logistic_binary, mann_whitney, t_test_paired. Docs: dev summary, block-based plan, status updates, task list v2.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-20 23:09:27 +08:00
parent 23b422f758
commit 428a22adf2
62 changed files with 15416 additions and 299 deletions

View File

@@ -1,8 +1,8 @@
# SSA-Pro 后端开发指南
> **文档版本:** v1.5
> **文档版本:** v1.7
> **创建日期:** 2026-02-18
> **最后更新:** 2026-02-18(纳入专家配置体系 + 决策表匹配 + R代码库
> **最后更新:** 2026-02-20(纳入 Prompt 体系 + 多工具流程规划 + 数据质量核查报告
> **目标读者:** Node.js 后端工程师
---
@@ -61,6 +61,517 @@ backend/src/modules/ssa/
---
## 1.2 🆕 Prompt 体系与专家配置边界
> **核心理念:骨架与血肉的分离**
>
> 详细设计参考:`06-开发记录/SSA-Pro Prompt体系与专家配置边界梳理.md`
### 1.2.1 动态 Prompt 注入模式
```
┌─────────────────────────────────────────────────────────────────┐
│ Prompt 动态注入架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI 工程师维护 统计专家维护 │
│ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Prompt 模板 │ │ 配置中台 (Excel) │ │
│ │ (骨架) │ │ - 决策表 │ │
│ │ │ │ - 使用规则 │ │
│ │ {{占位符}} │ ◀─────── │ - 解读模板 │ │
│ │ │ 注入 │ - 禁用词列表 │ │
│ └─────────────┘ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 完整 Prompt = 骨架 + 血肉 → 发送给 LLM ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```
### 1.2.2 三个核心 Prompt
| 环节 | Prompt 名称 | 作用 | 专家注入内容 |
|------|------------|------|-------------|
| **意图重写** | `SSA_QUERY_REWRITER` | 将医生口语翻译为统计术语 | 同义词字典 |
| **智能规划** | `SSA_PLANNER` | 选择工具 + 生成参数映射 | 工具定义 + 使用规则 |
| **结果解读** | `SSA_CRITIC` | 生成论文级结论 | 解读模板 + 禁用词 |
### 1.2.3 职责边界表
| 资产类型 | 谁来写? | 存在哪里? | 被谁执行? |
|---------|---------|-----------|-----------|
| System Prompt 模板 (骨架) | AI 工程师 | `prompt_templates` 表 | 传给 LLM |
| 工具适用条件/数据要求 | 统计专家 | 配置中台 Excel | 注入 Prompt → LLM |
| 统计护栏规则 | 统计专家 | 配置中台 Excel | **传给 R 服务,由 R 强执行** |
| R 代码模板 | 统计专家 | 配置中台 Excel | 传给 R 服务 |
| 论文结论解释规范 | 统计专家 | 配置中台 Excel | 注入 Critic Prompt → LLM |
> ⚠️ **关键原则**:统计护栏规则(如正态性检验 P<0.05 降级)**绝对不要**放到 Prompt 里让 LLM 判断。这些规则必须由 R 代码强逻辑执行。
---
## 1.3 🆕 多工具流程规划设计
> **愿景目标**"不是执行方法,而是规划流程"
>
> **MVP 目标**LLM 能够规划 2-7 个工具的串联执行流程
### 1.3.1 流程规划架构
```
用户:"比较两组患者的血压差异"
┌─────────────────────────────────────────────────────────────────┐
│ 多工具流程规划 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: 意图解析 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 目的:差异比较 | 变量:连续 | 分组:二分类 | 设计:独立 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ Step 2: 流程规划LLM 输出) │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ { ││
│ │ "workflow": [ ││
│ │ { "step": 1, "tool": "ST_DATA_CHECK", "name": "数据校验" },││
│ │ { "step": 2, "tool": "ST_QUALITY_REPORT", "name": "质量核查" },││
│ │ { "step": 3, "tool": "ST_NORMALITY_TEST", "name": "正态性检验" },││
│ │ { "step": 4, "tool": "ST_LEVENE_TEST", "name": "方差齐性" },││
│ │ { "step": 5, "tool": "ST_T_TEST_IND", "name": "T检验" },││
│ │ { "step": 6, "tool": "ST_EFFECT_SIZE", "name": "效应量" },││
│ │ { "step": 7, "tool": "ST_CONCLUSION", "name": "结论生成" }││
│ │ ], ││
│ │ "reasoning": "两组独立样本比较,需先检验前提条件..." ││
│ │ } ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ Step 3: 串联执行 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 工具1 → 工具2 → 工具3 → ... → 工具N ││
│ │ ↓ ↓ ↓ ↓ ││
│ │ 结果1 → 结果2 → 结果3 → ... → 最终报告 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3.2 WorkflowPlannerService 设计
```typescript
// planner/WorkflowPlannerService.ts
interface WorkflowStep {
step: number;
toolCode: string;
toolName: string;
params?: Record<string, any>;
dependsOn?: number[]; // 依赖的前置步骤
}
interface WorkflowPlan {
workflow: WorkflowStep[];
reasoning: string;
estimatedTime: number; // 预估耗时(秒)
}
export class WorkflowPlannerService {
/**
* 生成多工具执行流程
*/
async generateWorkflow(
sessionId: string,
userQuery: string,
dataSchema: object
): Promise<WorkflowPlan> {
// 1. 获取候选工具
const tools = await this.toolRetrieval.retrieveTools(userQuery, dataSchema, 10);
// 2. 构造 Prompt包含流程规划指令
const prompt = this.buildWorkflowPrompt(userQuery, dataSchema, tools);
// 3. 调用 LLM 生成流程
const llm = LLMFactory.getAdapter('deepseek-v3');
const response = await llm.chat([
{ role: 'system', content: prompt },
{ role: 'user', content: userQuery }
]);
// 4. 解析 + 校验
return this.parseWorkflowPlan(response, tools);
}
/**
* 构造流程规划 Prompt
*/
private buildWorkflowPrompt(query: string, schema: object, tools: any[]): string {
return `
你是一位顶尖的临床数据科学家。你拥有一个包含 ${tools.length} 个专家级统计工具的代码库。
用户数据结构:
${JSON.stringify(schema, null, 2)}
可用工具库:
${tools.map(t => `- ${t.toolCode}: ${t.name} - ${t.description}`).join('\n')}
请根据用户需求,规划一个完整的统计分析流程。流程应包含:
1. 数据质量核查(必须)
2. 前提条件检验(如适用)
3. 核心统计分析
4. 效应量计算(如适用)
5. 结论生成
输出 JSON 格式:
{
"workflow": [
{ "step": 1, "toolCode": "工具代码", "toolName": "工具名称" },
...
],
"reasoning": "规划理由"
}
只输出 JSON不要其他内容。
`.trim();
}
}
```
### 1.3.3 WorkflowExecutorService 设计
```typescript
// executor/WorkflowExecutorService.ts
interface StepResult {
step: number;
toolCode: string;
status: 'success' | 'warning' | 'error';
result?: any;
error?: string;
executionMs: number;
}
export class WorkflowExecutorService {
/**
* 串联执行多个工具
*/
async executeWorkflow(
sessionId: string,
workflow: WorkflowStep[],
onStepComplete?: (stepResult: StepResult) => void // 实时回调
): Promise<StepResult[]> {
const results: StepResult[] = [];
let previousResult: any = null;
for (const step of workflow) {
const startTime = Date.now();
try {
// 构造本步骤的输入(可能依赖前置步骤的输出)
const input = this.buildStepInput(step, previousResult, sessionId);
// 调用 R 服务执行
const result = await this.rClient.execute(sessionId, {
tool_code: step.toolCode,
params: input
});
const stepResult: StepResult = {
step: step.step,
toolCode: step.toolCode,
status: result.status === 'success' ? 'success' : 'warning',
result: result,
executionMs: Date.now() - startTime
};
results.push(stepResult);
previousResult = result;
// 实时通知前端
onStepComplete?.(stepResult);
} catch (error: any) {
const stepResult: StepResult = {
step: step.step,
toolCode: step.toolCode,
status: 'error',
error: error.message,
executionMs: Date.now() - startTime
};
results.push(stepResult);
onStepComplete?.(stepResult);
// 决定是否继续执行后续步骤
if (this.isCriticalError(error)) {
break; // 关键错误,中断流程
}
// 非关键错误,继续执行
}
}
return results;
}
}
```
---
## 1.4 🆕 数据质量核查报告设计
> **愿景目标**:自动生成"数据体检报告",主动告诉用户数据有什么问题
>
> **MVP 目标**:在执行分析前,先生成数据质量核查报告
### 1.4.1 核查报告结构
```typescript
// types/DataQualityReport.ts
interface DataQualityReport {
// 基础统计
summary: {
totalRows: number;
totalColumns: number;
numericColumns: number;
categoricalColumns: number;
};
// 缺失值分析
missingAnalysis: {
totalMissing: number;
missingRate: number; // 总体缺失率
columns: Array<{
name: string;
missingCount: number;
missingRate: number;
suggestion: string; // 处理建议
}>;
};
// 异常值检测
outlierAnalysis: {
columns: Array<{
name: string;
outlierCount: number;
outlierValues: any[];
method: 'IQR' | 'ZScore';
suggestion: string;
}>;
};
// 分布检验(数值变量)
distributionAnalysis: {
columns: Array<{
name: string;
shapiroP: number;
isNormal: boolean;
skewness: number;
kurtosis: number;
suggestion: string;
}>;
};
// 分组平衡性(如有分组变量)
groupBalance?: {
groupColumn: string;
groups: Array<{
name: string;
count: number;
percentage: number;
}>;
isBalanced: boolean;
suggestion: string;
};
// 整体评估
overallAssessment: {
qualityScore: number; // 0-100
level: 'good' | 'acceptable' | 'poor';
warnings: string[];
recommendations: string[];
};
}
```
### 1.4.2 DataQualityService 设计
```typescript
// planner/DataQualityService.ts
export class DataQualityService {
/**
* 生成数据质量核查报告
* 这是 R 服务调用,不是 LLM
*/
async generateReport(sessionId: string): Promise<DataQualityReport> {
// 调用 R 服务的数据质量核查工具
const result = await this.rClient.execute(sessionId, {
tool_code: 'ST_QUALITY_REPORT',
params: {
check_missing: true,
check_outliers: true,
check_distribution: true,
check_balance: true
}
});
return this.transformToReport(result);
}
/**
* 生成用户友好的摘要(可选用 LLM 增强)
*/
async generateSummary(report: DataQualityReport): Promise<string> {
const llm = LLMFactory.getAdapter('deepseek-v3');
const prompt = `
你是一位数据分析专家。请根据以下数据质量核查结果生成一段简洁的中文摘要3-5句话
告诉用户数据的整体质量如何,主要问题是什么,是否可以继续分析。
核查结果:
${JSON.stringify(report, null, 2)}
请直接输出摘要文本。
`;
return await llm.chat([{ role: 'user', content: prompt }]);
}
}
```
### 1.4.3 R 服务端实现ST_QUALITY_REPORT
```r
# tools/quality_report.R
#' @tool_code ST_QUALITY_REPORT
#' @name 数据质量核查报告
#' @description 生成全面的数据质量评估报告
run_analysis <- function(input) {
# 加载数据
df <- load_data(input)
report <- list()
# 1. 基础统计
report$summary <- list(
totalRows = nrow(df),
totalColumns = ncol(df),
numericColumns = sum(sapply(df, is.numeric)),
categoricalColumns = sum(sapply(df, is.character) | sapply(df, is.factor))
)
# 2. 缺失值分析
report$missingAnalysis <- analyze_missing(df)
# 3. 异常值检测IQR 方法)
report$outlierAnalysis <- analyze_outliers(df)
# 4. 分布检验
report$distributionAnalysis <- analyze_distribution(df)
# 5. 分组平衡性
if (!is.null(input$group_var)) {
report$groupBalance <- analyze_balance(df, input$group_var)
}
# 6. 整体评估
report$overallAssessment <- calculate_quality_score(report)
return(list(
status = "success",
report = report
))
}
# 计算整体质量评分
calculate_quality_score <- function(report) {
score <- 100
warnings <- c()
recommendations <- c()
# 缺失值扣分
if (report$missingAnalysis$missingRate > 0.1) {
score <- score - 20
warnings <- c(warnings, "缺失值比例超过10%")
recommendations <- c(recommendations, "建议处理缺失值后再进行分析")
} else if (report$missingAnalysis$missingRate > 0.05) {
score <- score - 10
warnings <- c(warnings, "存在一定比例的缺失值")
}
# 异常值扣分
outlier_cols <- sum(sapply(report$outlierAnalysis$columns, function(x) x$outlierCount > 0))
if (outlier_cols > 0) {
score <- score - 5 * outlier_cols
warnings <- c(warnings, paste0(outlier_cols, "个变量存在异常值"))
}
# 非正态扣分(提示,不强制扣分)
non_normal <- sum(!sapply(report$distributionAnalysis$columns, function(x) x$isNormal))
if (non_normal > 0) {
recommendations <- c(recommendations,
paste0(non_normal, "个变量不满足正态分布,系统将自动选择非参数方法"))
}
# 确定等级
level <- if (score >= 80) "good" else if (score >= 60) "acceptable" else "poor"
return(list(
qualityScore = max(0, score),
level = level,
warnings = warnings,
recommendations = recommendations
))
}
```
### 1.4.4 前端展示(核查报告卡片)
```
┌─────────────────────────────────────────────────────────────────┐
│ 📊 数据质量核查报告 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📈 数据概况 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 总样本量200 行 × 15 列 ││
│ │ 数值变量8 个 | 分类变量7 个 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ⚠️ 发现的问题 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ • 缺失值:血压字段有 12 例缺失 (6%) ││
│ │ • 异常值2 例血压 > 300 mmHg疑似记录错误 ││
│ │ • 正态性:治疗组血压不满足正态分布 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ 💡 系统建议 │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 1. 建议处理 2 例异常值后再分析 ││
│ │ 2. 由于正态性不满足,系统将自动选择非参数方法 ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ 🎯 整体评估:良好 (82/100) │
│ │
│ [继续分析] [下载报告] │
└─────────────────────────────────────────────────────────────────┘
```
---
## 2. 数据库 SchemaPrisma
```prisma
@@ -683,6 +1194,45 @@ export class ToolRetrievalService {
### 4.3 PlannerServiceAI 规划 + JSON 容错)
#### 🆕 Prompt 世界观设计(智能化演进关键)
> **重要**Prompt 的"世界观"设计直接影响 LLM 的推理质量和未来演进能力。
>
> 详细背景参考:`04-开发计划/06-智能化演进共识与MVP执行计划.md`
**错误的世界观(接线员思维):**
```
你是一个工具选择器,从以下列表中选择合适的工具...
```
**正确的世界观(数据科学家思维):**
```
你是一位顶尖的临床数据科学家,拥有以下能力:
1. 深刻理解医学研究的统计学需求
2. 精通各类统计方法的适用场景和前提条件
3. 能够诊断数据特征并选择最优分析路径
你现在拥有一个包含 100+ 专家级统计算法的代码库。
每个算法都经过统计学专家的严格验证,确保结果的权威性。
请理解医生的研究意图,诊断数据特征,从代码库中选择最合适的工具组合,
并制定完整的分析计划。你的目标是帮助医生产出可以直接用于 SCI 论文的统计结果。
```
**为什么这很重要?**
| 维度 | 接线员思维 | 数据科学家思维 |
|------|-----------|---------------|
| LLM 自我认知 | 被动的工具选择器 | 主动的分析规划师 |
| 推理深度 | 简单匹配 | 深度分析数据特征 |
| 输出质量 | 可能选错工具 | 更准确的工具选择 |
| Phase 3 演进 | 难以扩展到代码修改 | 自然过渡到代码理解和修改 |
> **MVP 阶段行动**:更新 `SSA_PLANNER` Prompt 模板,使用"数据科学家"世界观。
---
```typescript
// services/PlannerService.ts
import { LLMFactory } from '@/common/llm/adapters/LLMFactory';