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:
875
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 四层七工具实现机制详解.md
Normal file
875
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 四层七工具实现机制详解.md
Normal file
@@ -0,0 +1,875 @@
|
||||
# SSA-Pro 四层七工具实现机制详解
|
||||
|
||||
> **文档版本:** v1.1
|
||||
> **创建日期:** 2026-02-21
|
||||
> **最后更新:** 2026-02-21(v1.1 — 新增对话层 LLM 架构说明)
|
||||
> **文档类型:** 架构说明 (Architecture Reference)
|
||||
> **目标读者:** 开发团队
|
||||
> **前置文档:** `SSA-Pro 工具体系规划方案(团队讨论稿).md`(定义了"做什么"),本文档说明"怎么做"
|
||||
> **核心问题:** 每个工具的内部实现机制是什么?哪些依赖 LLM?哪些依赖规则引擎?哪些是纯计算?**对话层 LLM 如何保证多轮对话的连贯性?**
|
||||
|
||||
---
|
||||
|
||||
## 1. 三层架构全貌:对话层 LLM 是核心大脑
|
||||
|
||||
### 1.1 架构中的三个层次
|
||||
|
||||
系统不是简单的"Node.js 调工具",而是三层协同:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 第一层:Node.js 编排层(交通指挥) │
|
||||
│ ──────────────────────────────── │
|
||||
│ 职责:意图路由 → 工具调度 → 流程控制 → Session 黑板读写 │
|
||||
│ 特点:确定性、无 LLM、零 Token 成本 │
|
||||
│ 类比:交通警察,决定车(工具)往哪走,但不理解乘客(用户)在说什么 │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 第二层:对话层 LLM(全局大脑) ← 这是架构的核心 │
|
||||
│ ──────────────────────────── │
|
||||
│ 职责:理解用户意图 → 组织工具输出 → 生成连贯自然语言回复 │
|
||||
│ 特点:全局状态感知、多轮对话记忆、知识推理 │
|
||||
│ 类比:统计顾问本人,读过你的数据,记得之前聊了什么,知道什么时候该问 │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 第三层:工具层(专业手脚) │
|
||||
│ ──────────────────────── │
|
||||
│ 职责:执行具体任务(统计计算、规则匹配、R 脚本执行) │
|
||||
│ 特点:无状态函数,输入→输出,不关心对话上下文 │
|
||||
│ 类比:实验室设备,按按钮出结果,但不知道实验的完整背景 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 一次完整交互的真实流程
|
||||
|
||||
以用户说 **"帮我看看各组的样本分布"** 为例:
|
||||
|
||||
```
|
||||
用户消息: "帮我看看各组的样本分布"
|
||||
│
|
||||
▼
|
||||
┌─ 第一层:Node.js 编排 ──────────────────────────────────────────┐
|
||||
│ IntentRouter 分类 → explore │
|
||||
│ 决定调用 get_data_overview(若缓存未命中) │
|
||||
└──────────────┬──────────────────────────────────────────────────┘
|
||||
│ 工具调用
|
||||
▼
|
||||
┌─ 第三层:工具层 ────────────────────────────────────────────────┐
|
||||
│ get_data_overview → DataProfileService(Python) 返回结构化 JSON │
|
||||
│ { total_rows: 200, categorical_vars: [...], ... } │
|
||||
└──────────────┬──────────────────────────────────────────────────┘
|
||||
│ 结构化输出
|
||||
▼
|
||||
┌─ 第二层:对话层 LLM ────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ System Prompt: │
|
||||
│ "你是 SSA-Pro 智能统计分析助手... │
|
||||
│ 用户数据全貌: {DataContext} │
|
||||
│ 请基于数据摘要为用户解读数据特征。" │
|
||||
│ │
|
||||
│ 对话历史: │
|
||||
│ [user] 上传了数据 xxx.csv │
|
||||
│ [assistant] 您的数据已上传成功... │
|
||||
│ [user] 帮我看看各组的样本分布 ← 当前消息 │
|
||||
│ │
|
||||
│ 工具输出(注入为上下文): │
|
||||
│ get_data_overview 返回: { ... } │
|
||||
│ │
|
||||
│ → LLM 生成连贯的自然语言回复: │
|
||||
│ "您的数据共 200 例,分为 treatment 组(103 例)和 │
|
||||
│ control 组(97 例)。各组分布如下: │
|
||||
│ - treatment 组平均年龄 54.3 岁(SD=11.2)... │
|
||||
│ 需要我进一步看某个具体变量吗?" │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**关键点:工具返回的是结构化 JSON,但用户看到的是自然语言。中间的翻译者就是对话层 LLM。**
|
||||
|
||||
### 1.3 对话层 LLM 的六大职责
|
||||
|
||||
| # | 职责 | 靠 Node.js 能做到吗? | 说明 |
|
||||
|---|------|:---:|------|
|
||||
| 1 | **自然语言生成** | ❌ | 把结构化 JSON 变成"像人说的话" |
|
||||
| 2 | **多轮对话记忆** | ❌ | "刚才那个变量"→ 理解指的是上一轮讨论的 BMI |
|
||||
| 3 | **上下文推理** | ❌ | 用户说"有没有效"→ 结合 DataContext 推理出想比较什么 |
|
||||
| 4 | **主动引导** | ❌ | 分析完主动问"需要做敏感性分析吗?" |
|
||||
| 5 | **知识融合** | ❌ | 结合统计学知识解释"为什么选 T 检验而不是 ANOVA" |
|
||||
| 6 | **语气和策略** | ❌ | 对医生用临床语言,对统计师用技术语言 |
|
||||
|
||||
> **结论:Node.js 编排层绝对不够。对话层 LLM 是不可或缺的核心大脑。Node.js 管"做什么",LLM 管"怎么说"和"怎么想"。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 对话层 LLM 的技术实现
|
||||
|
||||
### 2.1 System Prompt 架构
|
||||
|
||||
对话层 LLM 有一个持久的 System Prompt,在整个会话生命周期内持续生效,并根据意图和状态动态组装:
|
||||
|
||||
```
|
||||
┌─ System Prompt(始终存在)────────────────────────────────────────┐
|
||||
│ │
|
||||
│ [基础角色](固定) │
|
||||
│ 你是 SSA-Pro 智能统计分析助手,专注于临床研究统计分析。 │
|
||||
│ 你了解用户上传的数据,可以回答关于数据的问题,推荐分析方法, │
|
||||
│ 解读分析结果。你的风格是专业但易懂的统计顾问。 │
|
||||
│ │
|
||||
│ [数据全貌](上传数据后始终注入,~200 Token) │
|
||||
│ 用户上传了一份数据集: │
|
||||
│ {DataContext.summary — 行数/列数/缺失率/类型/结构} │
|
||||
│ │
|
||||
│ [PICO 分类](推断后注入,~150 Token) │
|
||||
│ 根据数据特征,推断/确认的 PICO 分类如下: │
|
||||
│ {DataContext.pico — population/intervention/outcome} │
|
||||
│ │
|
||||
│ [变量字典](逐步丰富,~50 Token/变量,裁剪后注入) │
|
||||
│ 各变量的定义和角色: │
|
||||
│ {DataContext.variableDictionary — 裁剪策略控制} │
|
||||
│ │
|
||||
│ [意图特定指令](每次请求动态切换) │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ chat: 请基于统计知识和用户数据回答。不要主动建议分析。│ │
|
||||
│ │ explore: 请基于数据摘要解读数据特征。可以推断 PICO。 │ │
|
||||
│ │ consult: 请推荐分析方法,给出理由和前提。不要执行。 │ │
|
||||
│ │ analyze: 以下是工具执行结果,请向用户说明进展。 │ │
|
||||
│ │ discuss: 以下是分析结果: {结果}。请帮助用户解读。 │ │
|
||||
│ │ feedback: 以下是完整 QPER 记录: {trace}。请分析问题。 │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [工具输出](本轮工具调用的结果,动态注入) │
|
||||
│ {get_data_overview / method_consult / run_step 等的结构化输出} │
|
||||
│ │
|
||||
│ [分析结果](discuss/feedback 时注入,~500 Token/步骤) │
|
||||
│ {最近的 AnalysisRecord 摘要} │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ 对话历史(最近 N 轮,滑动窗口)─────────────────────────────────────┐
|
||||
│ [user] 上传了 clinical_trial.csv │
|
||||
│ [assistant] 您的数据已上传,共 200 行 15 列... │
|
||||
│ [user] 帮我看看各组的样本分布 │
|
||||
│ [assistant] treatment 组 103 例,control 组 97 例... │
|
||||
│ [user] BMI 的分布怎样? │
|
||||
│ [assistant] BMI 均值 25.3,标准差 4.1... │
|
||||
│ [user] 这两组 BMI 有差异吗?应该怎么分析? ← 当前消息 │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 对话层 LLM 在每种意图中的角色
|
||||
|
||||
| 意图 | Node.js 编排做了什么 | 对话层 LLM 做了什么 |
|
||||
|------|---------------------|-------------------|
|
||||
| **chat** | 几乎不介入(不调工具) | **完全主导**:纯 LLM 对话,基于 DataContext + 统计知识回答 |
|
||||
| **explore** | 调用 get_data_overview 或 get_variable_detail | **翻译 + 解读**:把结构化 JSON 变成易懂的数据解读 |
|
||||
| **consult** | 调用 method_consult(决策表匹配) | **融合 + 解释**:把匹配结果变成完整的方法推荐(带理由、前提、替代) |
|
||||
| **analyze** | 调用 analysis_plan → run_step ×N → write_report | **进度播报 + 过渡衔接**:每步执行后生成进展说明,衔接步骤间的上下文 |
|
||||
| **discuss** | 提取最近的 AnalysisRecord | **深度解读**:回答"p 值说明什么""置信区间为什么宽"等统计学问题 |
|
||||
| **feedback** | 注入 qperTrace 到 LLM | **问题诊断**:分析完整记录,找出问题根因,建议新方案 |
|
||||
|
||||
### 2.3 对话连贯性保障机制
|
||||
|
||||
```
|
||||
┌─ 保障机制 1:对话历史注入 ────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 每次 LLM 调用都携带最近 N 轮对话历史(滑动窗口) │
|
||||
│ → 用户说"刚才那个变量" → LLM 从历史中知道指的是 BMI │
|
||||
│ → 用户说"按你说的方案执行" → LLM 从历史中知道之前推荐了 T 检验 │
|
||||
│ │
|
||||
│ 窗口大小:根据 Token 预算动态调整 │
|
||||
│ - 简单对话:保留最近 10 轮 │
|
||||
│ - 长对话(含分析结果):保留最近 5 轮 + 关键事件摘要 │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ 保障机制 2:Session 黑板持久化 ──────────────────────────────────┐
|
||||
│ │
|
||||
│ 对话历史有滑动窗口(会丢失早期内容),但关键决策结果不会丢失: │
|
||||
│ - PICO 确认结果 → 写入 blackboard.confirmedPico → 始终可用 │
|
||||
│ - 变量角色确认 → 写入 blackboard.variableDictionary → 始终可用 │
|
||||
│ - 分析方案 → 写入 blackboard.currentPlan → 始终可用 │
|
||||
│ - 执行结果 → 写入 blackboard.stepResults → 始终可用 │
|
||||
│ │
|
||||
│ → 即使对话历史中早期的讨论被裁剪,结论仍在 Session 黑板中 │
|
||||
│ → LLM 通过 DataContext 注入随时可以引用这些结论 │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ 保障机制 3:意图切换时的上下文衔接 ──────────────────────────────┐
|
||||
│ │
|
||||
│ 用户从 consult 转到 analyze 时: │
|
||||
│ │
|
||||
│ [consult 阶段对话] │
|
||||
│ user: "我想比较两组血压差异,用什么方法?" │
|
||||
│ assistant: "建议独立样本 T 检验,理由是..." │
|
||||
│ user: "好的,按这个方案执行" │
|
||||
│ │
|
||||
│ → IntentRouter 识别为 analyze │
|
||||
│ → Node.js 编排层从 Session 黑板读取 consult 阶段的结论 │
|
||||
│ → 对话层 LLM 收到完整上下文: │
|
||||
│ System Prompt(含 DataContext + "用户已确认 T 检验方案") │
|
||||
│ + 对话历史(包含 consult 阶段的讨论) │
|
||||
│ + 意图特定指令("正在执行分析,请向用户说明进展") │
|
||||
│ → LLM 生成衔接回复: │
|
||||
│ "好的,我来按之前讨论的方案执行。分析计划如下: │
|
||||
│ 步骤 1: 描述统计(两组基线特征) │
|
||||
│ 步骤 2: 独立样本 T 检验(比较血压差异) │
|
||||
│ 确认开始执行吗?" │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.4 对话层 LLM 的技术参数
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|---|------|
|
||||
| 模型 | deepseek-v3 | 全系统统一模型 |
|
||||
| Temperature | 0.7(对话)/ 0.3(报告生成) | 对话允许自然变化,报告追求稳定 |
|
||||
| System Prompt | 持久注入 | 整个会话生命周期内持续生效 |
|
||||
| 对话历史 | 滑动窗口(5-10 轮) | Token 预算内尽量多保留 |
|
||||
| DataContext | 始终注入 | 通过 Session 黑板注入,保证数据感知 |
|
||||
| Prompt 管理 | DB 存储 + Seed 脚本 | 方法学团队可编辑,不写死在代码中 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 编排层 vs 对话层的职责划分
|
||||
|
||||
### 3.1 什么事 Node.js 编排层做
|
||||
|
||||
| 职责 | 说明 | 举例 |
|
||||
|------|------|------|
|
||||
| **意图路由** | 决定这条消息走哪个处理路径 | "比较两组" → analyze |
|
||||
| **工具调度** | 决定调用哪些工具、什么顺序 | analyze → plan → run_step ×N → report |
|
||||
| **Session 黑板读写** | 工具输出写入黑板,工具输入从黑板读 | PICO 写入 → plan 读取 |
|
||||
| **Token 控制** | 注入 LLM 前裁剪上下文 | 变量字典 >20 → 只注入已确认的 |
|
||||
| **data_source 注入** | R 引擎调用前自动注入数据源 | OSS 预签名 URL |
|
||||
| **错误分类** | run_step 出错后决定重试还是报告 | "Column not found" → 可自愈 |
|
||||
| **流程控制** | ask_user 中断/恢复、重试计数 | MAX 2 次 → 停止重试 |
|
||||
|
||||
### 3.2 什么事对话层 LLM 做
|
||||
|
||||
| 职责 | 说明 | 举例 |
|
||||
|------|------|------|
|
||||
| **自然语言生成** | 把结构化输出变成人话 | JSON → "您的数据共 200 例..." |
|
||||
| **多轮对话连贯** | 理解指代、省略、上下文引用 | "刚才那个" → BMI |
|
||||
| **知识推理** | 基于统计学知识回答专业问题 | "为什么选 T 检验?" |
|
||||
| **PICO 推断** | 基于临床知识推断数据角色 | 推断 outcome 可能是 bp_change |
|
||||
| **方法解释** | 解释决策表的匹配结果 | "选 T 检验是因为..." |
|
||||
| **结果解读** | 深度解释统计结果的含义 | "p=0.03 说明..." |
|
||||
| **主动引导** | 在合适时机引导用户下一步 | "需要做敏感性分析吗?" |
|
||||
| **反思分析** | 分析 QPER 全记录找问题根因 | "样本量不足导致检验效能低" |
|
||||
|
||||
### 3.3 协作模型
|
||||
|
||||
```
|
||||
Node.js 编排层 对话层 LLM
|
||||
───────────── ──────────
|
||||
|
||||
"这条消息是 explore 意图" →
|
||||
|
||||
"调用 get_data_overview" →
|
||||
|
||||
"工具返回了 JSON 结果" → "把 JSON + DataContext + 对话历史
|
||||
组装成 Prompt,生成自然语言解读"
|
||||
|
||||
← "您的数据共 200 例,分为..."
|
||||
|
||||
"把回复返回给前端" →
|
||||
|
||||
─── 下一轮 ───
|
||||
|
||||
"这条消息是 consult 意图" →
|
||||
|
||||
"调用 method_consult →
|
||||
(DecisionTable 匹配)"
|
||||
|
||||
"匹配结果: T 检验" → "把匹配结果 + DataContext + 对话历史
|
||||
组装成 Prompt,生成方法推荐 + 理由"
|
||||
|
||||
← "建议独立样本 T 检验,因为..."
|
||||
```
|
||||
|
||||
**核心原则:Node.js 决定"做什么",LLM 决定"怎么说"和"怎么想"。两者缺一不可。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 意图 → 编排流程 → LLM 角色映射
|
||||
|
||||
| 意图 | Node.js 编排流程 | 对话层 LLM 角色 |
|
||||
|------|-----------------|----------------|
|
||||
| **chat** | 几乎不介入 | **完全主导**:直接回答,DataContext 保证数据感知 |
|
||||
| **explore** | 调用 READ 层工具 | **翻译+解读**:JSON → 自然语言数据解读 |
|
||||
| **consult** | 调用 method_consult | **融合+解释**:决策表结果 → 完整方法推荐 |
|
||||
| **analyze** | 调用 THINK+ACT 全链路 | **播报+衔接**:步骤进展说明,上下文衔接 |
|
||||
| **discuss** | 提取分析结果 | **深度解读**:统计学问题的专业解答 |
|
||||
| **feedback** | 注入 QPER 记录 | **诊断+建议**:问题根因分析,新方案建议 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 四层七工具实现机制总览
|
||||
|
||||
### 5.1 四层七工具实现机制一览
|
||||
|
||||
| 层 | 工具 | 实现机制 | LLM 参与 | 核心引擎 | 风险/成本 |
|
||||
|----|------|---------|:---:|---------|----------|
|
||||
| **READ** | `get_data_overview` | Python 计算 + LLM 推断 | ✅ 部分 | DataProfileService (Python) | 零风险 |
|
||||
| **READ** | `get_variable_detail` | 纯 Python 计算 | ❌ | DataProfileService (Python) | 零风险 |
|
||||
| **READ** | `method_consult` | 匹配引擎 + LLM 补充 | ✅ 部分 | DecisionTableService (规则) | 零风险 |
|
||||
| **INTERACT** | `ask_user` | 纯系统机制 | ❌ | Node.js → 前端 → 用户 | 零风险 |
|
||||
| **THINK** | `analysis_plan` | 匹配引擎 + 模板填充 | ❌ | DecisionTable + FlowTemplate | 低风险 |
|
||||
| **ACT** | `run_step` | 纯 R 引擎执行 | ❌ | WorkflowExecutor → R Plumber | 有计算成本 |
|
||||
| **ACT** | `write_report` | LLM 生成 + 槽位注入 | ✅ 核心 | ReflectionService (LLM) | 有 Token 成本 |
|
||||
|
||||
### 5.2 辅助机制
|
||||
|
||||
| 机制 | 实现方式 | LLM 参与 |
|
||||
|------|---------|:---:|
|
||||
| **意图路由器** | 规则引擎优先 + LLM 兜底(混合路由) | ✅ 部分 |
|
||||
| **反思 — 自动重试** | 错误分类器(规则) + LLM 修正参数 | ✅ 部分 |
|
||||
| **反思 — 手动触发** | LLM 分析完整 QPER 记录 → 新方案 | ✅ 核心 |
|
||||
| **Session 黑板** | Node.js 内存缓存 | ❌ |
|
||||
| **Token 控制** | 代码裁剪函数 | ❌ |
|
||||
| **data_source 注入** | Node.js 编排层自动注入 | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## 6. READ 层 — 只读,零风险
|
||||
|
||||
### 6.1 get_data_overview
|
||||
|
||||
> **实现机制:Python 统计计算 + LLM PICO 推断(两步串联)**
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐
|
||||
session_id ──→ │ Step 1: DataProfileService │ ← 纯 Python 计算
|
||||
│ (Python pandas 统计分析) │ 无 LLM
|
||||
│ 输出:行数/列数/缺失率/类型/分布 │
|
||||
└──────────────┬───────────────────┘
|
||||
│ 结构化 JSON
|
||||
▼
|
||||
┌──────────────────────────────────┐
|
||||
│ Step 2: LLM PICO 推断 │ ← LLM Prompt
|
||||
│ 输入:统计摘要 + 临床知识 │ 纯推理,无工具调用
|
||||
│ 输出:population / intervention / │
|
||||
│ outcome 候选 + 置信度 │
|
||||
└──────────────┬───────────────────┘
|
||||
│ 合并
|
||||
▼
|
||||
写入 Session 黑板
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| 统计摘要生成 | **Python(pandas)** | 调用已有 `DataProfileService`,返回结构化 JSON |
|
||||
| PICO 推断 | **LLM Prompt** | 将统计摘要注入 Prompt,LLM 基于临床知识推断 PICO 分类 |
|
||||
| 结果缓存 | **Node.js 内存** | 写入 Session 黑板,后续工具直接读缓存 |
|
||||
|
||||
**关键约束:**
|
||||
- Step 1(统计计算)和 LLM 完全无关,是确定性结果
|
||||
- Step 2(PICO 推断)标记为 `inference`,必须经 `ask_user` 确认后才变为 `confirmed`
|
||||
- 已有实现基础:`DataProfileService` 已能生成统计摘要并缓存
|
||||
|
||||
---
|
||||
|
||||
### 6.2 get_variable_detail
|
||||
|
||||
> **实现机制:纯 Python 计算(零 LLM)**
|
||||
|
||||
```
|
||||
session_id + variable_name
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────┐
|
||||
│ DataProfileService │ ← 纯 Python 计算
|
||||
│ 按需查询单列 │ 无 LLM
|
||||
│ 输出:分布/直方图/异常值/样本值 │ 确定性结果
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| 单列统计分析 | **Python(pandas)** | 计算分布、异常值、直方图分箱等 |
|
||||
|
||||
**关键约束:**
|
||||
- 整个工具内部没有 LLM 参与,完全是 Python 数值计算
|
||||
- 是 `get_data_overview` 的按需 drill-down,解决大数据集 Token 爆炸问题
|
||||
- 需要在 Python 侧新增"单列查询" API(当前 `DataProfileService` 只有全量 profile)
|
||||
|
||||
---
|
||||
|
||||
### 6.3 method_consult
|
||||
|
||||
> **实现机制:决策表四维匹配 + LLM 推理补充(双引擎)**
|
||||
|
||||
```
|
||||
research_question + outcome_var + predictors + DataContext
|
||||
│
|
||||
├──→ ┌─────────────────────────────────────────┐
|
||||
│ │ 引擎 A: DecisionTableService │ ← 规则匹配引擎
|
||||
│ │ 四维匹配 Goal × OutcomeType × │ 无 LLM
|
||||
│ │ PredictorType × Design │ 确定性结果
|
||||
│ │ 输出:primaryTool + fallbackTool │
|
||||
│ └──────────────┬──────────────────────────┘
|
||||
│ │ 匹配结果
|
||||
│ ▼
|
||||
└──→ ┌─────────────────────────────────────────┐
|
||||
│ 引擎 B: LLM 推理补充 │ ← LLM Prompt
|
||||
│ 输入:匹配结果 + DataContext + 统计知识 │ 纯推理
|
||||
│ 输出: │
|
||||
│ rationale(选择理由) │
|
||||
│ prerequisites(前提条件) │
|
||||
│ limitations(局限性) │
|
||||
│ alternatives(替代方案 + 适用场景) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| 方法选择(选什么) | **DecisionTableService** | 四维规则匹配,JSON 驱动,确定性、可审计 |
|
||||
| 解释说明(为什么选) | **LLM Prompt** | 利用 LLM 统计学知识生成自然语言解释 |
|
||||
|
||||
**关键约束:**
|
||||
- 核心决策(选什么方法)由规则引擎决定,不由 LLM 决定 — 确保可审计、可配置
|
||||
- LLM 只负责"润色"和"补充" — 生成人类可读的理由和替代方案说明
|
||||
- 决策表当前 ~10 条规则,覆盖 7 个 R 工具,JSON 驱动,方法学团队可编辑
|
||||
- 未来工具扩展到 30+ 时,可在决策表前加一层 RAG Top-K 检索
|
||||
- 已有实现基础:`DecisionTableService.match()` 已实现四维匹配
|
||||
|
||||
---
|
||||
|
||||
## 7. INTERACT 层 — 人机交互,零风险
|
||||
|
||||
### 7.1 ask_user
|
||||
|
||||
> **实现机制:纯系统机制(Node.js 请求-响应模式,零 LLM)**
|
||||
|
||||
```
|
||||
Node.js 编排层决定需要提问
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 1: Node.js 生成 ClarificationCard JSON │ ← 无 LLM
|
||||
│ (question + options + context) │ Node.js 代码构造
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│ SSE 推送 / HTTP Response
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 2: 前端 ClarificationCard 组件渲染 │ ← 前端 React
|
||||
│ 用户看到选择卡片 → 点击选择 → 提交 │
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│ HTTP POST(用户选择结果)
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 3: Node.js 从 Session 黑板恢复上下文 │ ← 无 LLM
|
||||
│ 将用户选择写入黑板 → 继续后续流程 │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| 卡片生成 | **Node.js 代码** | 根据当前流程上下文构造问题和选项 |
|
||||
| 卡片渲染 | **前端 React** | `ClarificationCard` 组件,支持单选/多选/自由文本 |
|
||||
| 上下文恢复 | **Session 黑板** | 用户响应后,从黑板恢复上下文继续流程 |
|
||||
|
||||
**关键约束:**
|
||||
- 工具本身完全不涉及 LLM — 它是一个"人机交互通道"
|
||||
- 但"什么时候问、问什么"是由 Node.js 编排层决定的
|
||||
- 当前实现为"请求-响应模式":Node.js 发送卡片 → 中断等待 → 下一次 HTTP 请求恢复
|
||||
- 不是 LLM Function Calling 挂起模式(那是 Phase 4+ 的预研方向)
|
||||
- 已有实现基础:`QueryService.generateClarificationCards()` + `ClarificationCard` 前端组件
|
||||
|
||||
---
|
||||
|
||||
## 8. THINK 层 — 生成方案,低风险
|
||||
|
||||
### 8.1 analysis_plan
|
||||
|
||||
> **实现机制:决策表匹配 + 流程模板填充(确定性引擎,零 LLM)**
|
||||
|
||||
```
|
||||
confirmed_question + confirmed_methods + variable_mapping + DataContext
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 1: DecisionTableService.match() │ ← 规则匹配引擎
|
||||
│ 四维匹配 → 命中规则 → 得到 templateId │ 无 LLM
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│ matchResult { templateId, primaryTool, fallbackTool }
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 2: FlowTemplateService.fill() │ ← 模板引擎
|
||||
│ 选择模板 → 填充参数 → EPV 防护 → 有序步骤列表 │ 无 LLM
|
||||
│ 输出:[{order, tool_code, params, depends_on}] │ 确定性结果
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| 规则匹配 | **DecisionTableService** | 四维匹配(Goal × OutcomeType × PredictorType × Design) |
|
||||
| 模板填充 | **FlowTemplateService** | 从 `flow_templates.json` 加载模板,填入变量名和参数 |
|
||||
| EPV 防护 | **FlowTemplateService** | 检查事件数/自变量比,超限自动截断并生成免责声明 |
|
||||
|
||||
**关键约束:**
|
||||
- 整个工具内部没有 LLM 参与 — 完全是规则匹配 + 模板填充的确定性计算
|
||||
- 方法选择在 `method_consult` 阶段已完成并经用户确认,`analysis_plan` 只负责"编排顺序 + 填充参数"
|
||||
- 输出包含确定的 `tool_code` + 完整 `params`,`run_step` 只需傻瓜式执行
|
||||
- 生成后展示给用户审查,用户确认后才进入 ACT 层
|
||||
- 已有实现基础:`FlowTemplateService.fill()` + `decision_tables.json` + `flow_templates.json`
|
||||
|
||||
---
|
||||
|
||||
## 9. ACT 层 — 执行,有成本
|
||||
|
||||
### 9.1 run_step
|
||||
|
||||
> **实现机制:纯 R 引擎执行(傻瓜式 API 转发,零 LLM)**
|
||||
|
||||
```
|
||||
step_definition { tool_code, params } + session_id
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Node.js 编排层:自动注入 data_source │ ← 无 LLM
|
||||
│ 从 Session 黑板取 dataOssKey → 生成预签名 URL │ 系统机制
|
||||
│ 注入 params.data_source = { type:'oss', oss_url } │
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ WorkflowExecutorService → RClientService │
|
||||
│ HTTP POST 给 R Plumber API │ ← 无 LLM
|
||||
│ R 脚本执行统计分析 │ 纯 R 引擎计算
|
||||
│ 返回 ReportBlock[] + figures + r_code │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| data_source 注入 | **Node.js 编排层** | 从 Session 黑板取 `dataOssKey`,生成预签名 URL 自动注入 |
|
||||
| R 脚本执行 | **R Plumber API** | 接收 tool_code + params → 执行统计计算 → 返回结构化结果 |
|
||||
| 结果标准化 | **R Block Helpers** | R 端输出标准化为 `ReportBlock[]` 格式 |
|
||||
|
||||
**关键约束:**
|
||||
- 整个工具内部没有 LLM 参与 — 纯粹的 HTTP 转发 + R 引擎执行
|
||||
- 是"傻瓜式 API 转发器",不做任何方法选择决策
|
||||
- `data_source` 由 Node.js 编排层自动注入,LLM 和 `analysis_plan` 全程不感知数据文件位置
|
||||
- 错误处理由编排层接管(双轨反思机制)
|
||||
- 已有实现基础:`WorkflowExecutorService` + `RClientService` + `resolveDataSource()` 全部已实现
|
||||
|
||||
---
|
||||
|
||||
### 9.2 write_report
|
||||
|
||||
> **实现机制:LLM 生成叙述 + 统计量槽位注入 + Zod 校验(LLM 为核心引擎)**
|
||||
|
||||
#### generate 模式(论文级报告)
|
||||
|
||||
```
|
||||
step_results + DataContext
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 1: 提取 Key Findings │ ← 无 LLM
|
||||
│ 从 stepResults 中提取 p 值、置信区间、效应量等 │ 纯代码提取
|
||||
│ 生成 {{ slot }} 映射表 │
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 2: LLM 生成叙述框架 │ ← LLM 核心
|
||||
│ Prompt: "根据以下统计结果,生成论文级结论" │ 生成自然语言叙述
|
||||
│ LLM 输出包含 {{ slot }} 占位符 │
|
||||
│ LLM 被禁止生成任何数值(反幻觉机制) │
|
||||
└──────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Step 3: jsonrepair + Zod 校验 + 槽位渲染 │ ← 无 LLM
|
||||
│ 校验 LLM 输出结构完整性 │ 确定性后处理
|
||||
│ 用真实统计数值替换 {{ slot }} 占位符 │
|
||||
│ 校验失败 → 降级到 ConclusionGeneratorService │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### interpret 模式(结果解读)
|
||||
|
||||
```
|
||||
user_question + step_results + DataContext
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ LLM 深度解读 │ ← LLM 核心
|
||||
│ Prompt: DataContext + 分析结果 + 用户具体问题 │ 纯推理
|
||||
│ 输出:针对用户问题的统计学解读 │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**实现细节:**
|
||||
|
||||
| 环节 | 引擎 | 说明 |
|
||||
|------|------|------|
|
||||
| Key Findings 提取 | **Node.js 代码** | 从 R 输出中提取关键统计量,构建槽位映射表 |
|
||||
| 叙述框架生成 | **LLM (deepseek-v3)** | 生成论文级叙述,用 `{{ slot }}` 占位符替代数值 |
|
||||
| 输出校验 | **jsonrepair + Zod** | 修复 JSON 格式 → Schema 强校验 → 确保结构完整 |
|
||||
| 槽位渲染 | **Node.js 代码** | 用真实统计数值替换占位符 |
|
||||
| 降级兜底 | **ConclusionGeneratorService** | Zod 校验失败时,规则拼接生成基础结论 |
|
||||
|
||||
**关键约束:**
|
||||
- 这是 7 个工具中 LLM 参与度最高的,LLM 是核心引擎
|
||||
- 但 LLM 不是"裸奔"的:统计量通过槽位注入从 R 输出渲染,LLM 被禁止生成数值(反幻觉)
|
||||
- 三层防御:`jsonrepair` → `Zod Schema` → `ConclusionGeneratorService`(规则拼接兜底)
|
||||
- 已有实现基础:`ReflectionService` + `ConclusionGeneratorService` 全部已实现
|
||||
|
||||
---
|
||||
|
||||
## 10. 辅助机制详解
|
||||
|
||||
### 10.1 意图路由器(IntentRouterService)
|
||||
|
||||
> **实现机制:规则引擎优先 + LLM 兜底(混合路由)**
|
||||
|
||||
```
|
||||
用户消息 + Session 上下文
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Step 1: 规则匹配器 │ ← 无 LLM
|
||||
│ 关键词匹配 + 上下文状态推断 │ 确定性、零延迟
|
||||
│ "执行""分析一下" → analyze │
|
||||
│ "应该用什么方法" → consult │
|
||||
│ "帮我看看" → explore │
|
||||
│ "p 值说明什么"(有结果) → discuss │
|
||||
└──────────────┬───────────────────────┘
|
||||
│
|
||||
├── 命中 → 直接返回意图(不调 LLM)
|
||||
│
|
||||
└── 未命中 ──→ ┌──────────────────────────┐
|
||||
│ Step 2: LLM 轻量分类 │ ← LLM Prompt
|
||||
│ 仅做分类,不生成长文 │ 低 Token 成本
|
||||
│ 输出:intent + confidence │
|
||||
└──────────────┬───────────┘
|
||||
│
|
||||
├── confidence 足够 → 返回
|
||||
└── confidence 不足 → 默认 chat
|
||||
```
|
||||
|
||||
**关键约束:**
|
||||
- 规则优先:明确场景走规则,零延迟、零 Token 成本
|
||||
- LLM 兜底:模糊场景走轻量级 LLM 分类(仅输出 intent + confidence,不生成长文)
|
||||
- 默认安全:无法判断时 → chat(最安全的兜底,不触发任何流水线)
|
||||
|
||||
### 10.2 反思机制(编排层逻辑,非工具)
|
||||
|
||||
#### 自动反思(静默重试)
|
||||
|
||||
```
|
||||
run_step 返回 error
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 错误分类器 │ ← 无 LLM
|
||||
│ 读取 error_classification.json │ 规则匹配
|
||||
│ R 报错关键词 → 分类 │
|
||||
└──────────┬──────────┬────────────────┘
|
||||
│ │
|
||||
可自愈(参数级) 不可自愈(方法级)
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────────────────┐
|
||||
│ LLM 修正参数 │ │ 报告用户 + 建议替代方案 │
|
||||
│ 注入错误日志 │ │ 不重试 │
|
||||
│ + DataContext │ └──────────────────────────┘
|
||||
│ → 重试(MAX 2) │
|
||||
└──────────────┘
|
||||
← LLM Prompt
|
||||
```
|
||||
|
||||
#### 手动反思(用户驱动)
|
||||
|
||||
```
|
||||
用户: "结果不对" / "换个方法试试"
|
||||
│
|
||||
▼
|
||||
IntentRouter → feedback 意图
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ LLM 分析完整 QPER 记录 │ ← LLM 核心
|
||||
│ 注入 qperTrace(滑动窗口摘要) │ 深度推理
|
||||
│ 分析问题根因 → 生成新方案建议 │
|
||||
└──────────────┬───────────────────────┘
|
||||
│
|
||||
▼
|
||||
ask_user → 新 analysis_plan → run_step → write_report
|
||||
```
|
||||
|
||||
### 10.3 Session 黑板(纯系统基础设施)
|
||||
|
||||
```
|
||||
┌─ Session Blackboard ─────────────────────────────────────┐
|
||||
│ sessionId │
|
||||
│ dataOverview ← get_data_overview 写入 │
|
||||
│ confirmedPico ← ask_user 确认后写入 │
|
||||
│ variableDictionary ← 对话中逐步丰富 │
|
||||
│ dataOssKey ← 上传时写入(run_step 自动注入用) │
|
||||
│ currentPlan ← analysis_plan 写入 │
|
||||
│ stepResults ← run_step 每步追加 │
|
||||
│ report ← write_report 写入 │
|
||||
│ qperTrace ← 所有工具调用记录(反思用) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
|
||||
实现:纯 Node.js 内存缓存,无 LLM 参与
|
||||
Token 控制:注入 LLM 前的裁剪函数(纯代码逻辑)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. LLM 在架构中的准确定位
|
||||
|
||||
### 11.1 三层分类
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 完全不用 LLM 的 │
|
||||
│ │
|
||||
│ get_variable_detail — 纯 Python 计算 │
|
||||
│ ask_user — 纯系统机制(Node.js → 前端 → 用户) │
|
||||
│ analysis_plan — 纯规则匹配 + 模板填充 │
|
||||
│ run_step — 纯 R 引擎执行 │
|
||||
│ Session 黑板 / Token 控制 / data_source 注入 │
|
||||
│ │
|
||||
│ → 这些是"管道和基础设施",确定性、可审计、零 Token 成本 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 部分使用 LLM 的 │
|
||||
│ │
|
||||
│ get_data_overview — Python 计算(主) + LLM PICO 推断(辅) │
|
||||
│ method_consult — 决策表匹配(选什么) + LLM(解释为什么选) │
|
||||
│ 意图路由器 — 规则优先(确定性) + LLM 兜底(模糊场景) │
|
||||
│ 反思-自动重试 — 规则分类错误 + LLM 修正参数 │
|
||||
│ │
|
||||
│ → 这些是"引擎 + LLM 增强",核心决策不依赖 LLM │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ LLM 为核心的 │
|
||||
│ │
|
||||
│ write_report(generate) — LLM 生成叙述(但槽位注入防幻觉) │
|
||||
│ write_report(interpret) — LLM 深度解读用户问题 │
|
||||
│ 反思-手动触发 — LLM 分析完整 QPER 记录 │
|
||||
│ chat / explore 对话 — LLM(DataContext) 直接回复 │
|
||||
│ │
|
||||
│ → 这些是"LLM 的主场",充分发挥 LLM 知识和推理能力 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 11.2 LLM 调用点清单
|
||||
|
||||
| 调用点 | LLM 角色 | 模型 | 防护机制 |
|
||||
|--------|---------|------|---------|
|
||||
| PICO 推断 | 临床知识推理 | deepseek-v3 | ask_user 人工确认 |
|
||||
| method_consult 补充 | 统计学知识解释 | deepseek-v3 | 决策表为主,LLM 只补充说明 |
|
||||
| 意图路由 LLM 兜底 | 意图分类 | deepseek-v3 | 规则优先,LLM 只处理模糊场景 |
|
||||
| write_report(generate) | 论文级叙述生成 | deepseek-v3 | 槽位注入 + Zod 校验 + 规则拼接兜底 |
|
||||
| write_report(interpret) | 结果深度解读 | deepseek-v3 | DataContext 约束回答范围 |
|
||||
| 反思-自动修正 | 参数级错误修复 | deepseek-v3 | MAX 2 次重试限制 |
|
||||
| 反思-手动分析 | QPER 全记录分析 | deepseek-v3 | ask_user 确认新方案 |
|
||||
| chat/explore 对话 | 自由知识对话 | deepseek-v3 | DataContext 注入提供约束 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 架构哲学总结
|
||||
|
||||
### 12.1 核心协作模型
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ Node.js 编排层(交通指挥) │
|
||||
│ ┌────────────────────────┐ │
|
||||
│ │ 意图路由 → 工具调度 │ │
|
||||
│ │ Session 黑板读写 │ │
|
||||
│ │ Token 控制 + 流程控制 │ │
|
||||
│ └──┬──────────────┬──────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────────┘ └──────────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────────────┐ ┌────────────────┐ │
|
||||
│ │ 工具层(手脚) │ │ 用户 │ │
|
||||
│ │ │ 结构化输出 │ │ │
|
||||
│ │ Python 计算 │ ──────────────→ │ ask_user │ │
|
||||
│ │ DecisionTable │ │ PICO 确认 │ │
|
||||
│ │ FlowTemplate │ │ 方案审查 │ │
|
||||
│ │ R 引擎 │ │ 反思决策 │ │
|
||||
│ │ ErrorClassify │ └────────────────┘ │
|
||||
│ └────────┬───────┘ │
|
||||
│ │ 结构化输出 │
|
||||
│ ▼ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ 对话层 LLM(全局大脑) │ │
|
||||
│ │ │ │
|
||||
│ │ System Prompt(角色 + DataContext + 意图指令) │ │
|
||||
│ │ + 对话历史(滑动窗口) │ │
|
||||
│ │ + 工具输出(结构化 JSON) │ │
|
||||
│ │ │ │
|
||||
│ │ → 自然语言生成 → 多轮对话连贯 │ │
|
||||
│ │ → 知识推理 → 主动引导 │ │
|
||||
│ │ → PICO 推断 → 结果解读 │ │
|
||||
│ │ → 方法解释 → 反思分析 │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ 自然语言回复 → 前端展示 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 12.2 一句话总结
|
||||
|
||||
> **确定性的事交给规则引擎**(决策表、模板、R 脚本、Python 计算)
|
||||
> **需要知识和推理的事交给对话层 LLM**(理解意图、组织回复、知识推理、结果解读)
|
||||
> **需要判断和确认的事交给用户**(PICO 确认、方法选择、方案审查)
|
||||
> **三者通过 Node.js 编排层和 Session 黑板协同工作**
|
||||
|
||||
### 12.3 角色定位
|
||||
|
||||
| 层 | 角色类比 | 核心职责 | Token 成本 |
|
||||
|----|---------|---------|-----------|
|
||||
| **Node.js 编排层** | 交通指挥 | 决定做什么、走哪条路 | 零 |
|
||||
| **对话层 LLM** | 统计顾问本人 | 理解、推理、表达、引导 | 有(核心开销) |
|
||||
| **工具层** | 实验室设备 | 计算、匹配、执行 | 零(除 write_report) |
|
||||
| **用户** | 最终决策者 | 确认、修正、决定方向 | 零 |
|
||||
|
||||
**对话层 LLM 是系统的大脑和嘴巴** — 它理解用户在说什么,知道该怎么回答,把工具的结构化输出变成人类能理解的对话。没有它,系统只是一堆互不关联的 API 调用。
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
**文档维护者:** SSA 架构团队
|
||||
**创建日期:** 2026-02-21
|
||||
**最后更新:** 2026-02-21(v1.1 — 新增对话层 LLM 架构说明,明确三层架构模型)
|
||||
**关联文档:**
|
||||
- `SSA-Pro 工具体系规划方案(团队讨论稿).md` — 工具定义(做什么)
|
||||
- `SSA-Pro 意图识别与对话架构设计.md` — 意图路由 + System Prompt 策略
|
||||
- `04-开发计划/11-智能对话与工具体系开发计划.md` — 开发落地计划
|
||||
|
||||
### 变更日志
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|---------|
|
||||
| v1.0 | 2026-02-21 | 初版:四层七工具的内部实现机制 |
|
||||
| v1.1 | 2026-02-21 | **新增对话层 LLM 架构**:① 明确三层架构(编排层 + 对话层 LLM + 工具层);② 新增 System Prompt 架构(基础角色 + DataContext 注入 + 意图指令 + 对话历史);③ 新增对话连贯性三大保障机制(对话历史注入 + Session 黑板持久化 + 意图切换上下文衔接);④ 新增编排层 vs 对话层职责划分表 |
|
||||
675
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 工具体系规划方案(团队讨论稿).md
Normal file
675
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 工具体系规划方案(团队讨论稿).md
Normal file
@@ -0,0 +1,675 @@
|
||||
# SSA-Pro 工具体系规划方案
|
||||
|
||||
> **日期:** 2026-02-21
|
||||
> **版本:** v3.1(融合方案 + 工程补强)
|
||||
> **议题:** SSA 智能统计分析助手的工具定义与规划
|
||||
> **背景:** 当前 100 个 R 执行工具是基础能力,但工具体系过于单一。需要规划更完整的工具层,让 LLM Agent 能够在分析的全生命周期中为用户服务。
|
||||
> **结论:** 经过多轮讨论和团队评审,确定融合方案(四层 7 工具 + 反思编排 + Session 黑板 + Token 控制 + data_source 自动注入)
|
||||
|
||||
---
|
||||
|
||||
## 一、当前问题
|
||||
|
||||
**现状**:系统只有 R 执行工具(约 100 个),用户发消息 → 系统直接匹配统计方法 → 执行 R 脚本。
|
||||
|
||||
**问题**:
|
||||
- 用户无法与系统自由对话(每条消息都被当作分析请求)
|
||||
- LLM 面对 100 个 R 工具会决策瘫痪(工具过载)
|
||||
- 缺少"看数据""选方法""解读结果"等非执行类工具
|
||||
- 分析全过程对用户不透明
|
||||
|
||||
---
|
||||
|
||||
## 二、工具设计原则
|
||||
|
||||
| 原则 | 含义 |
|
||||
|------|------|
|
||||
| **正交性** | 每个工具有唯一职责,不重叠 |
|
||||
| **清晰触发** | LLM 看到工具名就知道何时该用 |
|
||||
| **组合性** | 工具可按不同顺序组合,下游只依赖上游输出而非特定调用链 |
|
||||
| **粒度适中** | 太粗=黑箱,太细=决策负担;5-8 个工具是 LLM 的甜区 |
|
||||
| **安全分级** | 只读工具随时调用,执行工具需确认 |
|
||||
| **最小暴露** | 每个阶段只暴露完成当前任务所必需的工具子集 |
|
||||
| **工具 vs 智能体** | 需要外部信息或改变外部世界 → 工具;纯 LLM 推理 → Prompt 职责,不封装为工具 |
|
||||
|
||||
---
|
||||
|
||||
## 三、融合方案总览
|
||||
|
||||
### 3.1 四层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LLM Agent 决策层 │
|
||||
│ 根据对话上下文 + 当前阶段,选择调用哪个工具 │
|
||||
└──┬─────────────┬──────────────┬──────────────┬──────────────┘
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
READ 层 INTERACT 层 THINK 层 ACT 层
|
||||
只读,零风险 人机交互 生成方案 执行,有成本
|
||||
随时可调用 零风险 需用户审查 需用户确认
|
||||
```
|
||||
|
||||
### 3.2 工具清单(7 个)
|
||||
|
||||
| 层 | # | 工具名 | 类型 | 一句话职责 |
|
||||
|----|---|--------|------|-----------|
|
||||
| **READ** | 1 | **get_data_overview** | 只读 | 数据全貌:统计摘要 + 缺失 + 变量分类 + PICO 自动推断 |
|
||||
| **READ** | 2 | **get_variable_detail** | 只读 | 单变量深入:分布 + 异常值 + 唯一值(按需查看,控制 Token) |
|
||||
| **READ** | 3 | **method_consult** | 只读 | 方法咨询:推荐统计方法 + 理由 + 前提 + 替代方案 |
|
||||
| **INTERACT** | 4 | **ask_user** | 交互 | 向用户提问/确认:渲染为选择卡片,用于 PICO 确认、方案审查 |
|
||||
| **THINK** | 5 | **analysis_plan** | 生成 | 分析规划:有序步骤 + 参数 + 依赖(输出确定的 tool_code + params) |
|
||||
| **ACT** | 6 | **run_step** | 执行 | 傻瓜式 R 执行:接收 tool_code + params,转发 R 引擎,返回结果 |
|
||||
| **ACT** | 7 | **write_report** | 生成 | 论文级报告 / 结果解读(双模式:generate + interpret) |
|
||||
|
||||
### 3.3 反思机制(不是工具,是编排层逻辑)
|
||||
|
||||
反思不定义为独立工具,而是系统编排机制,采用**双轨触发**:
|
||||
|
||||
**自动触发(静默重试)**:当 `run_step` 返回硬性错误(Column not found、NaN produced 等参数级错误),系统自动将错误日志注入 LLM 上下文,LLM 修正参数后静默重试,最多 2 次。用户无感知。
|
||||
|
||||
**手动触发(用户驱动)**:当执行成功但用户不满意("能不能换个方法""把极值删了再跑一次"),意图路由器识别为 feedback 意图,将完整 QPER 记录注入 LLM,LLM 分析问题后生成新的 `analysis_plan`。
|
||||
|
||||
```
|
||||
run_step 返回 error
|
||||
→ 错误分类器
|
||||
→ 可自愈(参数拼写、列名错误) → 自动修正 → 重试(最多 2 次)
|
||||
→ 不可自愈(方法不适用、数据不足) → 报告用户 + 建议替代方案
|
||||
|
||||
用户表达不满
|
||||
→ 意图路由器 → feedback 意图
|
||||
→ 注入完整 QPER 记录 → LLM 分析原因 → 新的 analysis_plan → 重新执行
|
||||
```
|
||||
|
||||
### 3.4 PICO 需求梳理(不是工具,是 Prompt 职责)
|
||||
|
||||
PICO 梳理在对话过程中自然完成,不需要专门的工具:
|
||||
|
||||
```
|
||||
1. LLM 调用 get_data_overview → 返回中包含 AI 自动推断的 PICO 分类(置信度标记)
|
||||
2. LLM 基于推断结果 + 临床知识 + System Prompt 组织 PICO 描述
|
||||
3. LLM 调用 ask_user → 让用户确认/修正 PICO 分类
|
||||
4. 确认后的 PICO 写入 Session 黑板,后续所有工具调用都携带
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、各工具详细定义
|
||||
|
||||
### 工具 1: get_data_overview
|
||||
|
||||
```
|
||||
名称: get_data_overview
|
||||
层级: READ(只读,零风险)
|
||||
触发时机: 数据上传后自动调用 / 用户询问数据概况时
|
||||
底层实现: DataProfileService(Python)
|
||||
|
||||
输入: {
|
||||
session_id: string
|
||||
}
|
||||
|
||||
输出: {
|
||||
total_rows: number,
|
||||
total_columns: number,
|
||||
missing_overview: {
|
||||
overall_rate: number,
|
||||
columns_with_missing: [{ name, rate }]
|
||||
},
|
||||
categorical_vars: [{ name, levels_count, top_levels }],
|
||||
continuous_vars: [{ name, mean, sd, min, max }],
|
||||
id_like_vars: [string],
|
||||
data_structure: "cross-sectional" | "longitudinal" | "repeated-measures" | "unknown",
|
||||
sample_size_warning: string | null,
|
||||
|
||||
// PICO 自动推断(AI 猜测,需用户通过 ask_user 确认后才生效)
|
||||
pico_inference: {
|
||||
population: string | null,
|
||||
intervention_var: string | null,
|
||||
intervention_levels: [string],
|
||||
outcome_candidates: [{ var, type, confidence }],
|
||||
design_guess: string | null,
|
||||
confidence: number // 整体推断置信度
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 输出一次性包含"下游需要的一切只读信息",满足组合性原则
|
||||
- PICO 推断标记为 inference(推断),不是 confirmed(确认),后续必须经过 ask_user 确认
|
||||
- 输出写入 Session 黑板缓存,后续工具直接读缓存,不重复调用
|
||||
|
||||
### 工具 2: get_variable_detail
|
||||
|
||||
```
|
||||
名称: get_variable_detail
|
||||
层级: READ(只读,零风险)
|
||||
触发时机: LLM 需要深入了解某个变量时(按需 drill-down)
|
||||
底层实现: DataProfileService(Python),按需查询单列
|
||||
|
||||
输入: {
|
||||
session_id: string,
|
||||
variable_name: string
|
||||
}
|
||||
|
||||
输出: {
|
||||
name: string,
|
||||
type: "continuous" | "categorical" | "ordinal" | "datetime" | "id",
|
||||
missing_rate: number,
|
||||
unique_values: number,
|
||||
distribution: {
|
||||
// 连续型: { mean, median, sd, q1, q3, min, max, histogram_bins }
|
||||
// 分类型: { levels: [{ value, count, percentage }] }
|
||||
},
|
||||
outliers: { count, threshold },
|
||||
sample_values: [...]
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 解决大数据集 Token 爆炸 — LLM 先看 overview 概览,再按需 drill-down 单列
|
||||
- 多次调用查看不同变量,每次只增加一列的 Token 消耗
|
||||
|
||||
### 工具 3: method_consult
|
||||
|
||||
```
|
||||
名称: method_consult
|
||||
层级: READ(只读,零风险)
|
||||
触发时机: 用户询问"应该用什么方法" / LLM 需要方法推荐时
|
||||
底层实现: DecisionTableService 四维匹配 + LLM 推理补充
|
||||
|
||||
输入: {
|
||||
research_question: string,
|
||||
outcome_var: string,
|
||||
predictor_vars: [string],
|
||||
data_context: DataContext // 从 Session 黑板读取
|
||||
}
|
||||
|
||||
输出: {
|
||||
recommended_method: {
|
||||
name: string, // "Independent Samples T-Test"
|
||||
tool_code: string, // "ST_T_TEST_IND"
|
||||
rationale: string,
|
||||
prerequisites: [string], // ["正态性", "方差齐性"]
|
||||
limitations: [string]
|
||||
},
|
||||
alternatives: [{
|
||||
name, tool_code, when_to_use // 何时该用替代方案
|
||||
}],
|
||||
warnings: [string]
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 当前 R 工具数量 7 个,决策表四维匹配(Goal × OutcomeType × PredictorType × Design)已足够精准
|
||||
- 未来工具扩展到 30+ 时,可在此处引入 RAG Top-K 检索替代/增强决策表
|
||||
- 只返回推荐,不执行。用户通过 ask_user 确认后才进入 THINK 层
|
||||
|
||||
### 工具 4: ask_user
|
||||
|
||||
```
|
||||
名称: ask_user
|
||||
层级: INTERACT(人机交互,零风险)
|
||||
触发时机: LLM 需要用户确认或澄清时
|
||||
前端渲染: 选择卡片(单选/多选)或输入框,嵌入对话流
|
||||
|
||||
输入: {
|
||||
question: string,
|
||||
context: string,
|
||||
options: [{
|
||||
id: string,
|
||||
label: string,
|
||||
description?: string
|
||||
}],
|
||||
allow_multiple: boolean,
|
||||
allow_free_text: boolean
|
||||
}
|
||||
|
||||
输出: {
|
||||
selected_options: [string],
|
||||
free_text: string | null
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 强制 LLM 在不确定时向用户提问,而非自行猜测
|
||||
- 贯穿全流程:PICO 确认、变量角色确认、方法选择确认、方案审查
|
||||
- 前端已有 ClarificationCard 组件基础,需要标准化接口
|
||||
|
||||
**实现方式(请求-响应模式,非 Function Calling 挂起)**:
|
||||
- 当前架构是 Node.js 编排层控制流程,不是 LLM 自主调用链
|
||||
- ask_user 不需要"挂起 LLM 推理线程":Node.js 生成卡片 → 返回前端 → 用户点击 → 前端 POST 新请求 → Node.js 从 Session 黑板恢复上下文 → 继续流程
|
||||
- 已有实现基础:`QueryService.generateClarificationCards()` + `/workflow/clarify` 路由
|
||||
- 未来若演进为 LLM Function Calling 模式(LLM 自主编排),需引入 Session 状态机 + 消息历史持久化(Phase 4 预研)
|
||||
|
||||
### 工具 5: analysis_plan
|
||||
|
||||
```
|
||||
名称: analysis_plan
|
||||
层级: THINK(生成方案,需用户审查)
|
||||
触发时机: 用户确认需求和方法后
|
||||
底层实现: FlowTemplateService 模板填充 + DecisionTableService 匹配
|
||||
|
||||
输入: {
|
||||
confirmed_question: string,
|
||||
confirmed_methods: [string], // 用户确认的 tool_code 列表
|
||||
variable_mapping: {
|
||||
outcome: string,
|
||||
predictors: [string],
|
||||
confounders?: [string],
|
||||
group_var?: string
|
||||
},
|
||||
data_context: DataContext // 从 Session 黑板读取
|
||||
}
|
||||
|
||||
输出: {
|
||||
plan_id: string,
|
||||
title: string,
|
||||
steps: [{
|
||||
step_id: string,
|
||||
order: number,
|
||||
tool_code: string, // 确定的 R 工具代码,如 "ST_T_TEST_IND"
|
||||
tool_name: string,
|
||||
description: string,
|
||||
params: { ... }, // 已填好的参数,run_step 直接透传
|
||||
depends_on: [step_id],
|
||||
expected_output: string
|
||||
}],
|
||||
estimated_duration: string,
|
||||
notes: [string]
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- 输出包含确定的 `tool_code` 和完整 `params`,`run_step` 只需要傻瓜式执行
|
||||
- 方法选择由 `method_consult`(决策表)+ 用户确认完成,`analysis_plan` 只负责编排顺序和填充参数
|
||||
- 生成后展示给用户审查,用户确认后才进入 ACT 层
|
||||
|
||||
### 工具 6: run_step
|
||||
|
||||
```
|
||||
名称: run_step
|
||||
层级: ACT(执行,有计算成本)
|
||||
触发时机: 用户确认分析计划后,逐步执行
|
||||
底层实现: WorkflowExecutorService → RClientService → R Plumber API
|
||||
|
||||
输入: {
|
||||
step_definition: { // 直接来自 analysis_plan 输出的某一步
|
||||
step_id: string,
|
||||
tool_code: string, // 如 "ST_T_TEST_IND"
|
||||
params: { ... }
|
||||
},
|
||||
session_id: string
|
||||
}
|
||||
|
||||
输出: {
|
||||
status: "success" | "warning" | "error",
|
||||
result: {
|
||||
blocks: ReportBlock[],
|
||||
figures: [{ type, data, title }],
|
||||
raw_output: { ... }
|
||||
},
|
||||
r_code: string,
|
||||
warnings: [string],
|
||||
error_message: string | null
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- **傻瓜式 API 转发器** — 不做任何方法选择决策,只接收 tool_code + params → 调用 R → 返回结果
|
||||
- **data_source 自动注入** — LLM 和 analysis_plan 都不需要关心数据文件位置。Node.js 编排层自动从 Session 黑板取出 `dataOssKey`,生成预签名 URL,注入 `{ type: 'oss', oss_url: signedUrl }` 后再 POST 给 R 服务(代码已实现:`WorkflowExecutorService.resolveDataSource()`)
|
||||
- 错误处理由编排层接管(双轨反思机制)
|
||||
- warning 状态的结果正常展示(R 引擎的 ggplot2 废弃警告等不影响结果)
|
||||
|
||||
### 工具 7: write_report
|
||||
|
||||
```
|
||||
名称: write_report
|
||||
层级: ACT(生成,有 LLM Token 成本)
|
||||
触发时机: 步骤全部执行完成后 / 用户要求解读结果时
|
||||
底层实现: ReflectionService(LLM 结论生成)+ 槽位注入 + Zod 校验
|
||||
|
||||
输入: {
|
||||
mode: "generate" | "interpret",
|
||||
step_results: [StepResult], // generate 模式:全部步骤结果
|
||||
data_context: DataContext,
|
||||
user_question?: string // interpret 模式:用户的具体问题
|
||||
}
|
||||
|
||||
输出: {
|
||||
conclusion: {
|
||||
summary: string,
|
||||
detailed: string,
|
||||
clinical_significance: string,
|
||||
limitations: [string],
|
||||
recommendations: [string]
|
||||
},
|
||||
export_formats: ["word", "markdown"]
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- **双模式**:`generate` 生成完整论文级报告(QPER R 层),`interpret` 解读已有结果(新增能力,支持 discuss 意图)
|
||||
- generate 模式在全部 run_step 完成后自动触发
|
||||
- interpret 模式在用户追问结果时触发("p 值说明什么""为什么置信区间这么宽")
|
||||
|
||||
---
|
||||
|
||||
## 五、Session 黑板(状态管理)
|
||||
|
||||
### 5.1 为什么需要 Session 黑板
|
||||
|
||||
7 个工具本身是无状态函数。要让它们协同工作,需要一个后端"会话黑板"(Session Context Blackboard)在工具之间传递上下文。
|
||||
|
||||
**核心规则:切勿让 LLM 反复重新生成上下文。** `get_data_overview` 的输出缓存到 Session,后续 `analysis_plan` / `run_step` / `write_report` 直接从 Session 读取,不重新调用。
|
||||
|
||||
### 5.2 Session 黑板结构
|
||||
|
||||
```typescript
|
||||
interface SessionBlackboard {
|
||||
sessionId: string;
|
||||
uploadedAt: string;
|
||||
|
||||
// get_data_overview 的缓存输出
|
||||
dataOverview: DataOverviewResult | null;
|
||||
|
||||
// 用户确认的 PICO(经过 ask_user 确认后写入)
|
||||
confirmedPico: {
|
||||
population: string;
|
||||
intervention: { var: string; levels: string[] };
|
||||
outcomes: [{ var: string; type: string; role: string }];
|
||||
confirmed: boolean;
|
||||
} | null;
|
||||
|
||||
// 变量字典(对话中逐步丰富)
|
||||
variableDictionary: Array<{
|
||||
name: string;
|
||||
label: string;
|
||||
type: string;
|
||||
role: string;
|
||||
confirmed: boolean;
|
||||
}>;
|
||||
|
||||
// 当前分析计划(analysis_plan 输出后写入)
|
||||
currentPlan: AnalysisPlanResult | null;
|
||||
|
||||
// 数据文件信息(上传时写入,run_step 自动注入 data_source 用)
|
||||
dataOssKey: string | null;
|
||||
|
||||
// 执行结果(run_step 每完成一步追加)
|
||||
// ⚠️ Token 控制:只保留当前生效计划的结果,废弃试错结果
|
||||
stepResults: StepResult[];
|
||||
|
||||
// 结论报告(write_report 输出后写入)
|
||||
report: ReportResult | null;
|
||||
|
||||
// QPER 执行记录(反思机制需要)
|
||||
// ⚠️ Token 控制:注入 LLM 前只保留最近 3 次关键事件,做摘要压缩
|
||||
qperTrace: Array<{
|
||||
timestamp: string;
|
||||
tool: string;
|
||||
input: any;
|
||||
output: any;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Token 控制策略
|
||||
|
||||
Session 黑板会随着对话和试错不断膨胀。如果全量注入 LLM,会导致 Token 爆炸和注意力涣散。
|
||||
|
||||
**规则 1:stepResults 只保留当前生效计划的结果**
|
||||
- 用户确认新的 analysis_plan 后,清空旧的 stepResults
|
||||
- 反思迭代产生新方案时,旧方案的结果标记为 `deprecated`,不注入 LLM
|
||||
|
||||
**规则 2:qperTrace 滑动窗口**
|
||||
- 注入 LLM 前,只保留最近 3 次关键事件(tool 调用 + 错误 + 修复动作)
|
||||
- 早期的 trace 条目压缩为一行摘要(如"第 1 轮:T 检验成功")
|
||||
|
||||
**规则 3:R 引擎原始输出不入 LLM**
|
||||
- stepResults 中的 `raw_output` 只存储在黑板中供前端展示
|
||||
- 注入 LLM 时只提取结构化摘要(关键数值、p 值、置信区间),不灌入完整日志
|
||||
|
||||
### 5.4 数据流
|
||||
|
||||
```
|
||||
get_data_overview
|
||||
→ 输出写入 blackboard.dataOverview
|
||||
→ LLM 基于输出推断 PICO
|
||||
|
||||
ask_user(确认 PICO)
|
||||
→ 用户选择写入 blackboard.confirmedPico
|
||||
|
||||
method_consult
|
||||
→ 读取 blackboard.dataOverview + confirmedPico
|
||||
→ 返回推荐方法(不写入 blackboard,只是对话中的回复)
|
||||
|
||||
analysis_plan
|
||||
→ 读取 blackboard 全部上下文
|
||||
→ 输出写入 blackboard.currentPlan
|
||||
|
||||
run_step ×N
|
||||
→ 读取 blackboard.currentPlan 中的 step 定义
|
||||
→ 每步输出追加到 blackboard.stepResults
|
||||
|
||||
write_report
|
||||
→ 读取 blackboard.stepResults + dataOverview
|
||||
→ 输出写入 blackboard.report
|
||||
|
||||
反思(自动/手动)
|
||||
→ 读取 blackboard.qperTrace(完整记录)
|
||||
→ 生成新的 analysis_plan → 覆盖 blackboard.currentPlan
|
||||
```
|
||||
|
||||
### 5.5 持久化策略
|
||||
|
||||
| 层级 | 策略 | 理由 |
|
||||
|------|------|------|
|
||||
| 内存缓存 | 当前会话有效,进程重启丢失 | 快速读写,满足当前需求 |
|
||||
| 数据库持久化 | 跨会话保留,支持历史回溯 | 后续扩展,用于分析记录和审计 |
|
||||
| 当前实现 | 先用内存缓存(已有 DataProfileService 缓存机制) | 快速落地 |
|
||||
|
||||
---
|
||||
|
||||
## 六、安全梯度与阶段性工具可见性
|
||||
|
||||
### 6.1 安全梯度
|
||||
|
||||
```
|
||||
READ 层: get_data_overview / get_variable_detail / method_consult
|
||||
→ 零风险,调用 100 次也无副作用
|
||||
→ 对话阶段主要使用,支持自由探索
|
||||
→ LLM 不确定时的安全默认选择
|
||||
|
||||
INTERACT 层: ask_user
|
||||
→ 零风险,让用户参与决策
|
||||
→ 贯穿全流程:PICO 确认、方案审查、结果追问
|
||||
|
||||
THINK 层: analysis_plan
|
||||
→ 低风险,只生成方案不执行
|
||||
→ 用户审查确认后才进入 ACT 层
|
||||
|
||||
ACT 层: run_step / write_report
|
||||
→ 有成本(R 计算资源、LLM Token)
|
||||
→ 产生真实结果,但可重新执行
|
||||
```
|
||||
|
||||
### 6.2 阶段性工具可见性
|
||||
|
||||
不同阶段向 LLM 暴露不同的工具子集:
|
||||
|
||||
| 阶段 | 可见工具 | 隐藏工具 |
|
||||
|------|---------|---------|
|
||||
| **数据探索** | get_data_overview, get_variable_detail, ask_user | method_consult, analysis_plan, run_step, write_report |
|
||||
| **需求梳理** | 全部 READ + ask_user | analysis_plan, run_step, write_report |
|
||||
| **方案规划** | method_consult, ask_user, analysis_plan | run_step, write_report |
|
||||
| **分析执行** | run_step, ask_user | 其他 |
|
||||
| **报告生成** | write_report, ask_user | 其他 |
|
||||
| **自由对话** | 全部 READ + INTERACT | THINK + ACT |
|
||||
|
||||
---
|
||||
|
||||
## 七、典型调用链
|
||||
|
||||
### 场景 1:完整分析旅程
|
||||
|
||||
```
|
||||
用户上传数据
|
||||
→ get_data_overview [自动] → 写入 Session 黑板
|
||||
→ "您的数据有 200 行 15 列,推断为横截面..." [LLM 回复]
|
||||
→ 用户: "帮我看看 BMI 这个变量"
|
||||
→ get_variable_detail("bmi") [LLM 调用]
|
||||
→ ask_user("推断结局变量为 bp_change, [LLM 调用]
|
||||
分组变量为 group,是否正确?")
|
||||
→ 用户确认 → 写入 Session 黑板 confirmedPico
|
||||
→ 用户: "帮我比较两组血压差异"
|
||||
→ method_consult(...) [LLM 调用,从 Session 读上下文]
|
||||
→ "建议独立样本 T 检验,理由..." [LLM 回复]
|
||||
→ ask_user("确认使用 T 检验分析?") [LLM 调用]
|
||||
→ 用户确认
|
||||
→ analysis_plan(...) [LLM 调用] → 写入 Session 黑板
|
||||
→ "分析计划:步骤1 描述统计 → 步骤2 T 检验" [展示给用户]
|
||||
→ 用户: "确认,开始执行"
|
||||
→ run_step(step_1) [逐步执行] → 追加到 Session
|
||||
→ run_step(step_2) [逐步执行] → 追加到 Session
|
||||
→ write_report(mode: "generate") [生成报告] → 写入 Session
|
||||
```
|
||||
|
||||
### 场景 2:用户只是了解数据
|
||||
|
||||
```
|
||||
用户: "这个数据有什么特点?"
|
||||
→ get_data_overview [LLM 调用]
|
||||
→ "您的数据包含 200 例患者,15 个变量..." [LLM 基于缓存回复]
|
||||
|
||||
用户: "age 的分布怎样?"
|
||||
→ get_variable_detail("age") [LLM 调用]
|
||||
|
||||
用户: "哪些变量缺失比较严重?"
|
||||
→ [LLM 从 Session 黑板缓存直接回答,无需再调工具]
|
||||
```
|
||||
|
||||
### 场景 3:方法咨询
|
||||
|
||||
```
|
||||
用户: "BMI 和血压有关系吗?应该怎么分析?"
|
||||
→ method_consult(...) [LLM 调用]
|
||||
→ "建议 Pearson 相关分析,因为两个变量均为连续型..."
|
||||
→ ask_user("是否需要控制混杂因素?",
|
||||
options: ["不需要", "控制年龄", "控制年龄和性别"])
|
||||
→ 用户选择后 LLM 更新建议
|
||||
```
|
||||
|
||||
### 场景 4:结果不满意,反思迭代
|
||||
|
||||
```
|
||||
用户: "结果不对,p 值不应该这么大"
|
||||
→ [编排层] 注入完整 qperTrace 到 LLM 上下文
|
||||
→ LLM: "可能原因:样本量不足导致检验效能低..."
|
||||
→ ask_user("建议:1)换用非参数检验 2)排除极值后重新分析 3)合并亚组")
|
||||
→ 用户选择 "换用非参数检验"
|
||||
→ analysis_plan(...) [生成新方案]
|
||||
→ run_step(...) [重新执行]
|
||||
→ write_report(mode: "generate") [新报告]
|
||||
```
|
||||
|
||||
### 场景 5:执行出错,自动修正
|
||||
|
||||
```
|
||||
→ run_step(step_2) [执行]
|
||||
→ R 返回 error: "Column 'Blood_Pressure' not found"
|
||||
→ [编排层] 错误分类 → 可自愈(列名拼写)
|
||||
→ [编排层] 自动注入错误 + 数据概览 → LLM 修正参数(bp_change)
|
||||
→ run_step(step_2, 修正后参数) [静默重试]
|
||||
→ 成功 → 用户无感知
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、R 工具路由策略
|
||||
|
||||
### 当前方案:决策表四维匹配
|
||||
|
||||
100 个 R 脚本通过 `run_step` 调用时,方法选择在 `method_consult` / `analysis_plan` 阶段已确定:
|
||||
|
||||
```
|
||||
用户需求 → method_consult 调用 DecisionTableService
|
||||
→ 四维匹配(Goal × OutcomeType × PredictorType × Design)
|
||||
→ 命中规则:primaryTool = ST_T_TEST_IND, fallbackTool = ST_MANN_WHITNEY
|
||||
→ 用户确认 → analysis_plan 填充参数 → run_step 傻瓜式执行
|
||||
```
|
||||
|
||||
决策表(`decision_tables.json`)当前有 ~10 条规则,覆盖 7 个 R 工具。规则驱动的确定性匹配比语义检索更精准、更可控。
|
||||
|
||||
### 未来扩展:RAG 增强
|
||||
|
||||
当 R 工具扩展到 30+ 且四维规则无法穷举时,可在 `method_consult` 内部增加 RAG 层:
|
||||
|
||||
```
|
||||
method_consult 内部路由:
|
||||
1. 先走决策表匹配 → 命中则直接返回
|
||||
2. 未命中 → pgvector 语义检索 Top-5 工具 → LLM 从中选择 → 返回推荐
|
||||
```
|
||||
|
||||
当前阶段不需要实现 RAG,决策表已足够。
|
||||
|
||||
---
|
||||
|
||||
## 九、与意图识别架构的关系
|
||||
|
||||
工具体系与意图路由器(详见《SSA-Pro 意图识别与对话架构设计》)配套工作:
|
||||
|
||||
```
|
||||
意图路由器输出 可见工具 典型调用
|
||||
───────────── ────────── ────────
|
||||
chat(自由对话) → READ + INTERACT data_overview / variable_detail
|
||||
explore(数据探索) → READ + INTERACT data_overview / variable_detail / ask_user
|
||||
consult(方法咨询) → READ + INTERACT method_consult / ask_user
|
||||
analyze(分析执行) → THINK + ACT + INTERACT analysis_plan → run_step → write_report
|
||||
discuss(结果讨论) → ACT(write_report) + READ write_report(interpret)
|
||||
feedback(不满意) → 反思编排 → THINK + ACT analysis_plan → run_step → write_report
|
||||
```
|
||||
|
||||
**两个设计合在一起**:
|
||||
- **意图路由器**决定"用户想干什么"→ 控制工具可见性
|
||||
- **工具体系**决定"系统能干什么"→ 提供能力边界
|
||||
- **Session 黑板**贯穿全流程 → 所有工具共享上下文,避免重复生成
|
||||
|
||||
---
|
||||
|
||||
## 十、与现有 QPER 代码的映射
|
||||
|
||||
| 工具 | 现有组件 | 改造程度 |
|
||||
|------|---------|---------|
|
||||
| get_data_overview | DataProfileService(已有) | **低** — 封装为 Tool 接口 + 新增 PICO 推断字段 |
|
||||
| get_variable_detail | DataProfileService(需扩展) | **中** — 新增单列查询 API |
|
||||
| method_consult | DecisionTableService(已有) | **中** — 封装为 Tool 接口 + LLM 推理补充 |
|
||||
| ask_user | ClarificationCard 前端组件(已有) | **低** — 标准化后端接口 |
|
||||
| analysis_plan | Q 层 + P 层(FlowTemplateService 已有) | **低** — 封装为 Tool 接口 |
|
||||
| run_step | E 层 WorkflowExecutorService(已有) | **已有** — 当前就是傻瓜式执行 |
|
||||
| write_report | R 层 ReflectionService(已有) | **中** — 新增 interpret 模式 |
|
||||
| Session 黑板 | DataProfileService 缓存(部分已有) | **中** — 扩展为完整 Blackboard |
|
||||
| 反思编排 | 无 | **新增** — 错误分类器 + 自动重试 + 手动触发 |
|
||||
|
||||
**核心结论:QPER 四层的底层实现基本可复用,主要工作是封装 Tool 接口 + 新增 READ/INTERACT 层 + Session 黑板 + 意图路由。**
|
||||
|
||||
---
|
||||
|
||||
## 十一、落地路线
|
||||
|
||||
| Phase | 工作内容 | 依赖现有组件 | 核心产出 |
|
||||
|-------|---------|-------------|---------|
|
||||
| **Phase 1** | **READ + INTERACT 层** | DataProfileService | get_data_overview + get_variable_detail + ask_user + Session 黑板 |
|
||||
| | 目标:让系统能陪用户聊天,能看懂数据 | | 即使不能跑分析,用户也能感受到 AI 的价值 |
|
||||
| **Phase 2** | **意图路由器 + method_consult** | DecisionTableService | 意图分类 + 方法咨询 + 阶段性工具可见性 |
|
||||
| | 目标:系统能区分对话/探索/咨询/分析意图 | | |
|
||||
| **Phase 3** | **THINK + ACT 对接** | QPER 全链路 | analysis_plan + run_step + write_report 封装为 Tool 接口 |
|
||||
| | 目标:把已有 QPER 挂载到新工具体系上 | | |
|
||||
| **Phase 4** | **反思编排 + 高级特性** | 全部 | 双轨反思 + write_report interpret 模式 + 持久化 |
|
||||
|
||||
---
|
||||
|
||||
**附:参考文档**
|
||||
- `00-系统设计/SSA-Pro 意图识别与对话架构设计.md` — 意图路由器设计
|
||||
- `00-系统设计/SSA-Pro 严谨型智能统计分析架构设计方案V4.md` — QPER 架构
|
||||
- `00-系统设计/SSA-Pro 理想状态与智能化愿景设计.md` — 智能化愿景
|
||||
- `06-开发记录/J技术报告审核评估与建议/SSA-Pro 智能体边界与工具生态规划报告.md` — 团队架构评审
|
||||
628
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 意图识别与对话架构设计.md
Normal file
628
docs/03-业务模块/SSA-智能统计分析/00-系统设计/SSA-Pro 意图识别与对话架构设计.md
Normal 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
|
||||
**下一步:** 评审确认后制定详细开发计划
|
||||
Reference in New Issue
Block a user