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

27 KiB
Raw Permalink Blame History

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 接口定义

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 "这个数据有多少样本?"
"BMI 一般正常范围是多少?"
"临床试验中 P 值多少算显著?"
直接 LLM 对话 DataContext
explore "帮我看看各组的样本分布"
"哪些变量缺失比较严重?"
"能推断出结局指标是哪些吗?"
DataProfile 增强探索 DataContext + 统计摘要
consult "我想比较两组差异,应该用什么方法?"
"这个数据适合做什么分析?"
"帮我制定一个分析计划"
LLM 给出分析建议(不执行) DataContext + 统计知识
analyze "对 BMI 和血压做相关分析"
"比较治疗组和对照组的血压差异"
"执行之前建议的分析方案"
进入 QPER 流水线 DataContext → Q 层
discuss "这个 p 值说明什么?"
"结果和我预期不一样,为什么?"
"需要做哪些敏感性分析?"
LLM 结果解读 DataContext + 分析结果

5.3 意图识别策略

意图路由器的判断策略(优先级从高到低):

1. 显式触发词
   - "执行" "运行" "分析一下" "开始分析" → analyze
   - "为什么" "说明什么" "怎么解读" (且有最近结果) → discuss
   - "应该用什么方法" "建议" "计划" "方案" → consult
   - "帮我看看" "分布" "缺失" "概况" → explore

2. 上下文推断
   - 刚上传数据、尚无对话 → 倾向 explore / chat
   - 已有分析结果、围绕结果提问 → 倾向 discuss
   - 讨论过方法选择、确认执行 → 倾向 analyze

3. 默认兜底
   - 无法判断 → chat最安全的默认值
   - chat 意味着 LLM 携带 DataContext 直接回复,不触发任何流水线

5.4 Intent Router 接口

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
下一步: 评审确认后制定详细开发计划