# 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 }; 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 接口 ```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 **下一步:** 评审确认后制定详细开发计划