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>
55 KiB
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 对话层职责划分表 |