docs(ssa): Complete intelligent dialogue and tool system architecture design

Architecture Design:
- Add intent recognition and dialogue architecture design (Intent Router + DataContext)
- Add tool system planning (4-layer 7-tool fusion solution: READ/INTERACT/THINK/ACT)
- Add 4-layer 7-tool implementation mechanism details (Conversation Layer LLM + Node.js orchestration)

Development Plan (v1.2):
- Create 6-phase development plan (134h/22 days) for intelligent dialogue system
- Add 8 architectural constraints (C1-C8): no Function Calling, Postgres-Only cache,
  streaming output, context guard, Zod dynamic validation
- Correct Session Blackboard to use CacheFactory (Postgres-Only, no Redis)

Status Updates:
- Update SSA module status: QPER complete + dialogue architecture design complete
- Update system-level status: add SSA architecture design milestone

Other:
- R tools minor fixes (chi_square, correlation, logistic_binary, mann_whitney, t_test_paired)
- Frontend AIA chat workspace style adjustment

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-22 10:05:14 +08:00
parent 11676f2840
commit bf10dec4c8
9 changed files with 3380 additions and 40 deletions

View File

@@ -0,0 +1,628 @@
# SSA-Pro 意图识别与对话架构设计
> **文档版本:** v1.0
> **创建日期:** 2026-02-21
> **文档类型:** 架构设计 (Architecture Design)
> **核心理念:** 从"统计参数提取器"升级为"数据感知的智能对话系统"
> **前置文档:** QPER 架构设计方案 V4、理想状态与智能化愿景设计
---
## 1. 问题诊断
### 1.1 当前架构的根本缺陷
当前 QPER 架构中Q 层QueryService的本质是**统计分析参数提取器**,不是真正的用户意图识别:
```
当前流程:
用户消息 → QueryService强制提取 goal/y/x/design→ Planner → Execute → Reflection
```
它假设用户发出的**每一条消息**都是一个分析请求,试图从中提取四维参数(分析目标、结局变量、预测变量、研究设计)。
**这导致了三个核心问题:**
| # | 问题 | 表现 |
|---|------|------|
| 1 | **无法自由对话** | 用户说"这个数据有什么特点"→ 系统强行匹配到 descriptive跑描述统计 |
| 2 | **跳过需求梳理** | 用户说"BMI 和血压有关系吗"→ 直接跑相关分析,但用户可能只是在咨询 |
| 3 | **LLM 能力浪费** | 只用 LLM 做参数提取,完全没利用 LLM 的知识库和推理能力 |
### 1.2 用户真实旅程 vs 系统假设
**系统假设的用户旅程:**
```
上传数据 → 说出分析需求 → 系统执行 → 查看结果
```
**医生的真实旅程:**
```
上传数据 → 理解数据全貌 → 与AI讨论探索 → 确定分析方案 → 执行分析 → 解读结果
↑ ↑ ↑
当前系统完全跳过了这三个关键阶段
```
### 1.3 一句话总结
> **当前系统只有"接单"能力,没有"对话"能力。QPER 是一个优秀的分析流水线,但它不应该是用户的唯一入口。**
---
## 2. 设计目标
### 2.1 核心目标
将 SSA 从"统计分析执行器"升级为"数据感知的统计顾问"
- 用户上传数据后,可以**自由与系统对话**,了解数据、讨论方案
- 系统基于**数据全貌**回答问题,像一个了解你数据的统计专家
- 只有当用户**明确表达分析意图**时,才触发 QPER 流水线
- 分析完成后,用户可以**继续讨论结果**,获得深入解读
### 2.2 设计原则
| 原则 | 含义 |
|------|------|
| **对话优先** | 默认是自由对话,分析执行是对话中的特殊行为 |
| **数据感知** | 所有 LLM 调用都携带数据全貌上下文 |
| **渐进深入** | 从数据概览 → PICO 分类 → 变量字典,逐步丰富理解 |
| **QPER 不变** | QPER 仍是核心分析引擎,但只在 analyze 意图时触发 |
| **用户主导** | 系统建议而非替用户决定,分析方案需用户确认 |
---
## 3. 总体架构
### 3.1 两层意图 + 数据全貌上下文
```
┌─────────────────────────────────┐
│ 数据全貌DataContext
│ 持久存在于整个会话生命周期中 │
│ 在对话过程中逐步丰富 │
└──────────┬──────────────────────┘
│ 注入
用户消息 ──→ 【意图路由器 Intent Router】──────────────────────────────────
├── 💬 自由对话Chat
│ LLM(DataContext) → 直接回复
├── 🔍 数据探索Explore
│ DataProfile + LLM(DataContext) → 直接回复
├── 📋 分析咨询Consult
│ LLM(DataContext + 统计知识) → 建议方案
├── 🎯 分析执行Analyze
│ → 进入 QPER 流水线Q → P → E → R
└── 📊 结果讨论Discuss
LLM(DataContext + 分析结果) → 解读回复
```
### 3.2 和现有架构的关系
```
现有 QPER保持不变
┌─────────────────────────┐
用户消息 → [Intent Router] → │ Q → P → E → R │
│ │ └─────────────────────────┘
│ │ ↑ 仅 analyze 意图触发
│ │
│ ├── chat → LLM(DataContext) → 回复
│ ├── explore → DataProfile/LLM → 回复
│ ├── consult → LLM(DataContext + 统计知识) → 建议
│ └── discuss → LLM(DataContext + 结果) → 解读
└── DataContext数据全貌贯穿会话
```
**关键:不是推翻 QPER而是在 QPER 前面加一层路由**。QPER 仍然是核心分析执行引擎,但它只在用户有明确分析意图时被触发。
---
## 4. 数据全貌DataContext
### 4.1 为什么需要数据全貌
当前系统中DataProfile 只在 Q 层被使用,用于辅助 LLM 提取分析参数。但数据全貌的价值远不止于此 —— 它应该是贯穿整个会话的**持久化上下文**,让 LLM 在每一次回复中都"了解"用户的数据。
### 4.2 三层数据全貌模型
```
DataContext {
┌─────────────────────────────────────────────────────────┐
│ Layer 1: 统计摘要Statistical Summary
│ 来源DataProfileService 自动生成 │
│ 时机:数据上传后立即生成 │
│ │
│ - totalRows: 200 │
│ - totalCols: 15 │
│ - missingRate: { overall: 3.2%, perColumn: {...} } │
│ - categoricalVars: ["group", "gender", "smoking"] │
│ - continuousVars: ["age", "bmi", "bp_baseline", ...] │
│ - dataStructure: "cross-sectional" │
│ - idLikeVars: ["patient_id"] │
└─────────────────────┬───────────────────────────────────┘
│ 自动
┌─────────────────────────────────────────────────────────┐
│ Layer 2: PICO 分类Clinical Context
│ 来源LLM 推断 + 用户确认/修正 │
│ 时机数据探索阶段LLM 主动推断或用户询问时 │
│ │
│ - population: "高血压患者,年龄 40-70 岁" │
│ - intervention: { │
│ var: "group", │
│ levels: ["treatment", "control"], │
│ description: "新型降压药 vs 安慰剂" │
│ } │
│ - comparator: "安慰剂对照" │
│ - outcomes: [ │
│ { var: "bp_change", type: "continuous", │
│ role: "primary", unit: "mmHg" } │
│ ] │
└─────────────────────┬───────────────────────────────────┘
│ LLM 推断 + 用户确认
┌─────────────────────────────────────────────────────────┐
│ Layer 3: 变量字典Variable Dictionary
│ 来源LLM 推断 + 用户逐步修正 │
│ 时机:对话过程中逐步丰富 │
│ │
│ - { name: "age", label: "年龄", │
│ type: "continuous", role: "baseline", │
│ unit: "years", description: "入组时年龄" } │
│ - { name: "group", label: "分组", │
│ type: "categorical", role: "intervention", │
│ levels: ["treatment", "control"] } │
│ - { name: "bp_change", label: "血压变化", │
│ type: "continuous", role: "outcome", │
│ unit: "mmHg", description: "治疗后-治疗前" } │
└─────────────────────────────────────────────────────────┘
```
### 4.3 数据全貌的生命周期
```
时间线:
─────────────────────────────────────────────────────────────→
│ │ │ │
上传数据 对话探索 分析执行 结果讨论
│ │ │ │
Layer 1 生成 Layer 2 推断 QPER 使用 解读引用
(自动) Layer 3 丰富 DataContext DataContext
(LLM+用户) (参数更准确) (结论更准确)
```
### 4.4 DataContext 接口定义
```typescript
interface DataContext {
sessionId: string;
uploadedAt: string;
// Layer 1: 自动生成
summary: {
totalRows: number;
totalCols: number;
missingOverview: { overall: number; perColumn: Record<string, number> };
categoricalVars: string[];
continuousVars: string[];
idLikeVars: string[];
dataStructure: 'cross-sectional' | 'longitudinal' | 'repeated-measures' | 'unknown';
sampleSizeWarning?: string;
};
// Layer 2: LLM 推断 + 用户确认
pico: {
population: string | null;
intervention: {
var: string | null;
levels: string[];
description: string | null;
};
comparator: string | null;
outcomes: Array<{
var: string;
type: 'continuous' | 'categorical' | 'time-to-event';
role: 'primary' | 'secondary';
unit?: string;
}>;
confirmed: boolean; // 用户是否已确认
};
// Layer 3: 变量字典
variableDictionary: Array<{
name: string; // 原始列名
label: string; // 中文标签LLM 推断或用户提供)
type: 'continuous' | 'categorical' | 'ordinal' | 'datetime' | 'id';
role: 'baseline' | 'intervention' | 'outcome' | 'covariate' | 'id' | 'unknown';
unit?: string;
levels?: string[]; // 分类变量的水平
description?: string; // 变量含义
confirmed: boolean; // 用户是否已确认该条目
}>;
// 元数据
lastUpdated: string;
enrichmentHistory: Array<{
timestamp: string;
layer: 1 | 2 | 3;
source: 'auto' | 'llm' | 'user';
description: string;
}>;
}
```
---
## 5. 意图路由器Intent Router
### 5.1 设计理念
意图路由器不是提取分析参数,而是**判断用户当前想做什么**。它是一个轻量级 LLM 调用(或规则匹配),输出一个意图分类。
### 5.2 五种意图类型
| 意图 | 触发示例 | 系统行为 | LLM 上下文 |
|------|---------|---------|-----------|
| **chat** | "这个数据有多少样本?"<br>"BMI 一般正常范围是多少?"<br>"临床试验中 P 值多少算显著?" | 直接 LLM 对话 | DataContext |
| **explore** | "帮我看看各组的样本分布"<br>"哪些变量缺失比较严重?"<br>"能推断出结局指标是哪些吗?" | DataProfile 增强探索 | DataContext + 统计摘要 |
| **consult** | "我想比较两组差异,应该用什么方法?"<br>"这个数据适合做什么分析?"<br>"帮我制定一个分析计划" | LLM 给出分析建议(不执行) | DataContext + 统计知识 |
| **analyze** | "对 BMI 和血压做相关分析"<br>"比较治疗组和对照组的血压差异"<br>"执行之前建议的分析方案" | 进入 QPER 流水线 | DataContext → Q 层 |
| **discuss** | "这个 p 值说明什么?"<br>"结果和我预期不一样,为什么?"<br>"需要做哪些敏感性分析?" | LLM 结果解读 | DataContext + 分析结果 |
### 5.3 意图识别策略
```
意图路由器的判断策略(优先级从高到低):
1. 显式触发词
- "执行" "运行" "分析一下" "开始分析" → analyze
- "为什么" "说明什么" "怎么解读" (且有最近结果) → discuss
- "应该用什么方法" "建议" "计划" "方案" → consult
- "帮我看看" "分布" "缺失" "概况" → explore
2. 上下文推断
- 刚上传数据、尚无对话 → 倾向 explore / chat
- 已有分析结果、围绕结果提问 → 倾向 discuss
- 讨论过方法选择、确认执行 → 倾向 analyze
3. 默认兜底
- 无法判断 → chat最安全的默认值
- chat 意味着 LLM 携带 DataContext 直接回复,不触发任何流水线
```
### 5.4 Intent Router 接口
```typescript
interface IntentRouterInput {
userMessage: string;
sessionId: string;
dataContext: DataContext | null; // 可能尚未上传数据
recentAnalysisResult: AnalysisRecord | null; // 最近的分析结果(用于判断 discuss
conversationHistory: Message[]; // 最近 N 条对话(用于上下文推断)
}
interface IntentRouterOutput {
intent: 'chat' | 'explore' | 'consult' | 'analyze' | 'discuss';
confidence: number;
reasoning: string; // LLM 给出的判断理由(用于调试)
suggestedResponse?: string; // chat/explore/consult/discuss 模式下的直接回复
}
```
### 5.5 关键设计决策LLM 路由 vs 规则路由
| 方案 | 优势 | 劣势 |
|------|------|------|
| **纯规则路由** | 快速、确定性强、无 LLM 成本 | 覆盖面有限,边界情况多 |
| **纯 LLM 路由** | 理解力强,边界情况少 | 多一次 LLM 调用,增加延迟和成本 |
| **混合路由(推荐)** | 快速覆盖明确场景 + LLM 兜底模糊场景 | 实现略复杂 |
**推荐方案:混合路由**
- 先走规则匹配(关键词 + 上下文状态)
- 规则无法判断时,调用轻量级 LLM仅分类不生成长文
- LLM 路由可与后续处理并行,减少感知延迟
---
## 6. 各意图的处理流程
### 6.1 Chat — 自由对话
```
用户: "BMI 在临床研究中一般怎么分类?"
系统处理:
1. Intent Router → chat
2. 构建 LLM Prompt:
- System: "你是一个临床统计顾问。用户上传了一份数据,以下是数据全貌..."
- System: {DataContext 的 JSON 摘要}
- User: "BMI 在临床研究中一般怎么分类?"
3. LLM 直接回复(利用自身知识库)
4. 回复展示在对话区
特点:
- 不触发任何分析流水线
- LLM 带着数据上下文回答,回复更有针对性
- 类似于和一个"读过你数据"的统计专家对话
```
### 6.2 Explore — 数据探索
```
用户: "帮我看看这个数据的缺失情况"
系统处理:
1. Intent Router → explore
2. 从 DataContext.summary 提取缺失率信息
3. 构建 LLM Prompt:
- System: "基于以下数据质量摘要,为用户解读缺失情况"
- System: {缺失率数据}
- User: "帮我看看这个数据的缺失情况"
4. LLM 生成结构化解读(哪些变量缺失严重、是否影响分析、建议处理方式)
5. 可选:生成缺失率可视化(调用 R 引擎)
进阶场景:
用户: "你觉得结局指标可能是哪些?"
→ LLM 基于 DataContext + 临床知识推断 PICO 分类
→ 生成推断结果,等待用户确认
→ 用户确认后更新 DataContext.pico
```
### 6.3 Consult — 分析咨询
```
用户: "我想比较治疗组和对照组的血压差异,应该用什么方法?"
系统处理:
1. Intent Router → consult
2. 构建 LLM Prompt:
- System: "你是统计方法学顾问。基于以下数据全貌,为用户推荐分析方法"
- System: {DataContext}
- System: "可用的分析方法列表: {tools_registry 摘要}"
- User: "我想比较治疗组和对照组的血压差异,应该用什么方法?"
3. LLM 回复:
- 推荐方法(如"建议使用独立样本 T 检验"
- 选择理由("因为结局变量是连续型,两组独立..."
- 前提条件("需要满足正态性和方差齐性"
- 替代方案("如果不满足正态性,可以使用 Wilcoxon 秩和检验"
4. 用户可以继续追问,或说"好,按这个方案执行"
关键区别:
- consult 只给建议,不执行
- 用户确认后再转为 analyze 意图
- 这让用户在执行前充分理解"为什么这样做"
```
### 6.4 Analyze — 分析执行(当前 QPER
```
用户: "好的,按你说的方案,对血压做 T 检验"
或: "对 BMI 和 bp_change 做相关分析"
系统处理:
1. Intent Router → analyze
2. 进入 QPER 流水线Q → P → E → R
3. 但此时 QPER 的 Q 层更轻松:
- DataContext 中已有 PICO 分类和变量字典
- consult 阶段已经讨论过方法选择
- Q 层只需确认参数,不需要"猜测"
提升点:
- Q 层可以直接从 DataContext.variableDictionary 获取变量类型和角色
- P 层的决策表匹配更准确(因为变量角色已确认)
- 用户的分析预期更明确(因为经过了 consult 阶段的讨论)
```
### 6.5 Discuss — 结果讨论
```
用户: "p 值 0.03 说明什么?为什么置信区间这么宽?"
系统处理:
1. Intent Router → discuss检测到最近有分析结果
2. 构建 LLM Prompt:
- System: "你是统计结果解读专家。以下是用户的数据全貌和最近的分析结果"
- System: {DataContext}
- System: {最近的 AnalysisRecord — 包含步骤结果和结论}
- User: "p 值 0.03 说明什么?为什么置信区间这么宽?"
3. LLM 深入解读结果
4. 可以建议后续分析("建议做一下亚组分析"
特点:
- 不重新跑分析,只是讨论已有结果
- LLM 带着完整的数据和结果上下文回答
- 可以引导用户进入下一轮分析discuss → consult → analyze
```
---
## 7. 对话状态机
### 7.1 用户旅程状态流转
```
┌──────────┐
┌────────→│ Chat │←───────┐
│ └────┬─────┘ │
│ │ │
│ ▼ │
┌────┴─────┐ ┌────────────┐ │
上传数据 ────────→ │ Explore │──→│ Consult │─────┤
└────┬─────┘ └────┬───────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │ Analyze │ │
│ │ (QPER) │ │
│ └────┬───────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
└────────→│ Discuss │─────┘
└────────────┘
```
**说明:**
- 任何状态都可以跳转到 Chat用户随时可以自由提问
- Explore → Consult → Analyze 是推荐的"主线",但不强制
- Discuss 之后可以回到任何状态(继续探索、咨询新方案、执行新分析)
- **状态之间没有硬性约束**,用户完全自主决定对话方向
### 7.2 数据全貌的渐进丰富
```
阶段 1 — 上传完成:
DataContext.summary ← DataProfileService 自动填充
阶段 2 — 探索对话中:
DataContext.pico ← LLM 推断,用户确认
DataContext.variableDictionary ← LLM 推断,用户逐步修正
阶段 3 — 分析执行时:
QPER 的 Q 层直接引用 DataContext参数提取更准确
阶段 4 — 结果讨论时:
LLM 引用 DataContext + 分析结果(解读更精准)
```
---
## 8. LLM Prompt 策略
### 8.1 System Prompt 模板
所有意图类型共享一个**基础 System Prompt**,差异在于注入的上下文片段:
```
[基础角色]
你是 SSA-Pro 智能统计分析助手,专注于临床研究统计分析。
你了解用户上传的数据,可以回答关于数据的问题,推荐分析方法,解读分析结果。
[数据全貌 — 始终注入]
用户上传了一份数据集,以下是数据全貌:
{DataContext.summary 的结构化摘要}
[PICO 分类 — 如果已推断]
根据数据特征,推断的 PICO 分类如下:
{DataContext.pico}
[变量字典 — 如果已丰富]
各变量的定义和角色:
{DataContext.variableDictionary}
[意图特定指令 — 按路由结果注入]
- chat: "请基于你的统计知识和用户数据回答问题。不要主动建议分析。"
- explore: "请基于数据摘要为用户解读数据特征。可以推断 PICO 分类。"
- consult: "请推荐合适的分析方法,给出理由和前提条件,不要执行分析。"
- discuss: "以下是最近的分析结果:{结果}。请帮助用户解读。"
[最近分析结果 — discuss 意图时注入]
{AnalysisRecord 摘要}
```
### 8.2 Token 成本控制
DataContext 注入 LLM 会增加 token 消耗,需要控制:
| 层 | 预估 Token | 控制策略 |
|----|-----------|---------|
| Layer 1 摘要 | ~200 | 始终注入,成本低 |
| Layer 2 PICO | ~150 | 已推断时注入 |
| Layer 3 变量字典 | ~50/变量 × N | 仅注入相关变量(裁剪策略) |
| 分析结果 | ~500/步骤 | discuss 时注入,限制摘要长度 |
**变量字典裁剪策略:**
- 变量数 ≤ 20全部注入
- 变量数 > 20只注入 confirmed + role ≠ unknown 的变量
- 极端情况(>50 列):只注入 PICO 相关变量
---
## 9. 和现有代码的映射
### 9.1 需要新增的组件
| 组件 | 位置 | 职责 |
|------|------|------|
| **IntentRouterService** | `backend/src/modules/ssa/services/` | 意图分类(规则 + LLM |
| **DataContextService** | `backend/src/modules/ssa/services/` | DataContext 生命周期管理 |
| **ChatService** | `backend/src/modules/ssa/services/` | chat/explore/consult/discuss 的 LLM 对话 |
| **DataContext 存储** | `prisma schema` 或内存 | 按 sessionId 持久化 DataContext |
### 9.2 需要改造的组件
| 组件 | 当前职责 | 改造方向 |
|------|---------|---------|
| **QueryService** | 意图解析 + 参数提取 | 仅保留参数提取,意图分类移到 IntentRouter |
| **workflow.routes** | `/api/ssa/workflow/plan` 直接调 QueryService | 新增路由入口,先走 IntentRouter |
| **DataProfileService** | 生成统计摘要 | 输出同时写入 DataContext.summary |
| **前端 SSAChatPane** | 消息 → 调用 workflow API | 消息 → 调用 IntentRouter API → 按意图分发 |
### 9.3 保持不变的组件
| 组件 | 原因 |
|------|------|
| **DecisionTableService** | P 层逻辑不变 |
| **FlowTemplateService** | P 层逻辑不变 |
| **WorkflowExecutorService** | E 层逻辑不变 |
| **ReflectionService** | R 层逻辑不变 |
| **RClientService** | R 引擎调用不变 |
| **所有 R 工具脚本** | R 层不变 |
---
## 10. 实施路线建议
### Phase 1: DataContext 基础(建议优先)
- 扩展 DataProfileService上传后自动生成 DataContext.summary
- DataContext 按 sessionId 内存缓存(后续可持久化)
- 前端上传完成后展示数据概览卡片
### Phase 2: Intent Router
- 实现 IntentRouterService先纯规则后加 LLM 兜底)
- 新增 API 入口 `/api/ssa/chat`,前端消息统一走此入口
- analyze 意图 → 转发到现有 QPER 流水线
- 其他意图 → ChatService 处理
### Phase 3: 数据感知对话
- ChatService 实现 chat / explore / consult / discuss 四种模式
- LLM 调用时注入 DataContext
- 前端对话区支持非分析类回复(纯文本,无卡片)
### Phase 4: PICO 推断与变量字典
- explore 对话中 LLM 推断 PICO 分类
- 前端展示 PICO 确认面板
- 变量字典逐步丰富机制
- DataContext 反哺 QPER Q 层
---
## 11. 预期效果对比
| 维度 | 当前系统 | 新架构 |
|------|---------|-------|
| **用户上传数据后** | 等待用户发出分析指令 | 自动展示数据概览,邀请探索 |
| **用户问"这个数据有什么特点"** | 强行匹配 descriptive goal | 进入 explore 模式LLM 解读数据 |
| **用户问"应该做什么分析"** | 强行猜测分析类型 | 进入 consult 模式,给出方法建议 |
| **用户确认"按这个方案做"** | N/A | 进入 analyze → QPER 执行 |
| **分析完成后用户追问** | 无法处理 | 进入 discuss 模式,解读结果 |
| **LLM 利用率** | 仅参数提取 | 全流程知识对话 |
| **数据理解深度** | 仅列名 + 类型 | PICO + 变量字典 + 临床含义 |
| **多轮对话能力** | 无(每条消息独立处理) | 完整对话上下文 + 数据全貌 |
---
**文档版本:** v1.0
**创建日期:** 2026-02-21
**下一步:** 评审确认后制定详细开发计划