Files
AIclinicalresearch/docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 意图识别与对话架构设计.md
HaHafeng bf10dec4c8 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>
2026-02-22 10:05:14 +08:00

629 lines
27 KiB
Markdown
Raw Permalink 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.
# 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
**下一步:** 评审确认后制定详细开发计划