Files
AIclinicalresearch/docs/02-通用能力层/06-R统计引擎/01-R统计引擎架构与部署指南.md
HaHafeng 3446909ff7 feat(ssa): Complete Phase I-IV intelligent dialogue and tool system development
Phase I - Session Blackboard + READ Layer:
- SessionBlackboardService with Postgres-Only cache
- DataProfileService for data overview generation
- PicoInferenceService for LLM-driven PICO extraction
- Frontend DataContextCard and VariableDictionaryPanel
- E2E tests: 31/31 passed

Phase II - Conversation Layer LLM + Intent Router:
- ConversationService with SSE streaming
- IntentRouterService (rule-first + LLM fallback, 6 intents)
- SystemPromptService with 6-segment dynamic assembly
- TokenTruncationService for context management
- ChatHandlerService as unified chat entry
- Frontend SSAChatPane and useSSAChat hook
- E2E tests: 38/38 passed

Phase III - Method Consultation + AskUser Standardization:
- ToolRegistryService with Repository Pattern
- MethodConsultService with DecisionTable + LLM enhancement
- AskUserService with global interrupt handling
- Frontend AskUserCard component
- E2E tests: 13/13 passed

Phase IV - Dialogue-Driven Analysis + QPER Integration:
- ToolOrchestratorService (plan/execute/report)
- analysis_plan SSE event for WorkflowPlan transmission
- Dual-channel confirmation (ask_user card + workspace button)
- PICO as optional hint for LLM parsing
- E2E tests: 25/25 passed

R Statistics Service:
- 5 new R tools: anova_one, baseline_table, fisher, linear_reg, wilcoxon
- Enhanced guardrails and block helpers
- Comprehensive test suite (run_all_tools_test.js)

Documentation:
- Updated system status document (v5.9)
- Updated SSA module status and development plan (v1.8)

Total E2E: 107/107 passed (Phase I: 31, Phase II: 38, Phase III: 13, Phase IV: 25)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-22 18:53:39 +08:00

36 KiB
Raw Blame History

R 统计引擎架构与部署指南

版本: v1.3
更新日期: 2026-02-22
维护者: SSA-Pro 开发团队
状态: 生产就绪Phase Deploy 完成 — 12 工具 + Block-based 标准化输出)


📋 目录

  1. 概述
  2. 架构设计
  3. Docker 镜像构建
  4. 部署指南
  5. API 参考
  6. 开发指南
  7. 运维指南
  8. 常见问题
  9. 测试指南

1. 概述

1.1 什么是 R 统计引擎

R 统计引擎是平台的专用统计计算服务,基于 Docker 容器化部署,提供:

  • 🧮 严谨的统计分析能力T 检验、方差分析、回归等)
  • 🛡️ 统计护栏(正态性检验、方差齐性检验等)
  • 📊 可视化输出Base64 编码的图表)
  • 📝 可复现代码生成APA 格式的 R 脚本)

1.2 定位

┌─────────────────────────────────────────────────────────────┐
│                      业务模块层                              │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                     │
│  │ SSA-Pro │  │  其他   │  │  其他   │                     │
│  │ 智能统计 │  │  模块   │  │  模块   │                     │
│  └────┬────┘  └─────────┘  └─────────┘                     │
├───────┼─────────────────────────────────────────────────────┤
│       ▼           通用能力层                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              R 统计引擎 (Docker)                     │   │
│  │  • /health         健康检查                          │   │
│  │  • /api/v1/tools   工具列表                          │   │
│  │  • /api/v1/skills  技能执行                          │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

1.3 技术栈

组件 版本 说明
R 4.3.3 统计计算核心
plumber 1.2.1 REST API 框架
ggplot2 最新 数据可视化
car 3.1-2 高级统计检验
dplyr/tidyr 最新 数据处理
gtsummary 最新 基线特征表生成Phase Deploy 新增)
gt/broom 最新 表格渲染/模型整理Phase Deploy 新增)
scales/gridExtra 最新 坐标轴格式化/多图排版Phase Deploy 新增)
Docker 24+ 容器化部署

2. 架构设计

2.1 Brain-Hand 模型

R 统计引擎采用 Brain-Hand 分离架构

┌──────────────────┐          ┌──────────────────┐
│    Node.js       │          │    R Docker      │
│    (Brain)       │          │    (Hand)        │
├──────────────────┤          ├──────────────────┤
│ • 业务逻辑       │  HTTP    │ • 统计计算       │
│ • 认证鉴权       │ ───────> │ • 数据处理       │
│ • OSS 签名       │          │ • 图表生成       │
│ • 结果解释       │ <─────── │ • 代码生成       │
└──────────────────┘  JSON    └──────────────────┘

2.2 数据传输协议

支持两种数据传输方式:

方式 条件 字段
inline 数据 < 2MB data_source.data (JSON)
oss 数据 >= 2MB data_source.oss_url (预签名 URL)
// 方式 1: inline
{
  "data_source": {
    "type": "inline",
    "data": [{"group": "A", "value": 10}, ...]
  }
}

// 方式 2: oss (预签名 URL)
{
  "data_source": {
    "type": "oss",
    "oss_url": "https://bucket.oss.com/data.csv?signature=xxx"
  }
}

2.2.1 inline 数据格式详解

R 数据加载器 (utils/data_loader.R) 支持两种 JSON 数据格式:

格式 说明 示例
行格式 JSON 对象数组,每个对象是一行 [{"sex": 1, "age": 25}, {"sex": 2, "age": 30}]
列格式 JSON 对象,每个属性是一列 {"sex": [1, 2], "age": [25, 30]}

推荐:使用行格式,与 JavaScript/TypeScript 的数据处理习惯一致。

Node.js 调用示例:

// 推荐行格式Array of Objects
const data = [
  { sex: 1, age: 25, bmi: 22.5 },
  { sex: 2, age: 30, bmi: 24.1 },
  // ...
];

const response = await axios.post('http://localhost:8082/api/v1/skills/ST_T_TEST_IND', {
  data_source: {
    type: 'inline',
    data: data  // 直接传入数组
  },
  params: {
    group_var: 'sex',
    value_var: 'age'
  }
});

2.3 安全设计

安全措施 实现方式
非特权用户 USER appuser
路径遍历防护 tool_code 正则白名单 ^[A-Z][A-Z0-9_]*$
OSS 密钥隔离 Node.js 生成预签名 URLR 无需持有密钥
健康检查 Docker HEALTHCHECK

3. Docker 镜像构建

3.1 完整 Dockerfile

FROM rocker/r-ver:4.3

LABEL maintainer="dev-team@aiclinicalresearch.com"
LABEL version="1.0.1"
LABEL description="SSA-Pro R Statistics Service"

# 安装系统依赖(包括 R 包编译所需的库)
RUN apt-get update && apt-get install -y \
    libcurl4-openssl-dev \
    libssl-dev \
    libxml2-dev \
    libsodium-dev \
    zlib1g-dev \
    libnlopt-dev \
    liblapack-dev \
    libblas-dev \
    gfortran \
    pkg-config \
    cmake \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 直接安装 R 包(含 Phase Deploy 新增依赖)
RUN R -e "install.packages(c( \
    'plumber', \
    'jsonlite', \
    'ggplot2', \
    'glue', \
    'dplyr', \
    'tidyr', \
    'base64enc', \
    'yaml', \
    'car', \
    'httr', \
    'scales', \
    'gridExtra', \
    'gtsummary', \
    'gt', \
    'broom' \
), repos='https://cloud.r-project.org/', Ncpus=2)"

# 安全加固:创建非特权用户
RUN useradd -m -s /bin/bash appuser

WORKDIR /app

# 复制应用代码
COPY plumber.R plumber.R
COPY utils/ utils/
COPY tools/ tools/
COPY tests/ tests/

# 设置目录权限
RUN chown -R appuser:appuser /app

# 切换到非特权用户
USER appuser

EXPOSE 8080

# 环境变量
ENV DEV_MODE="false"

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

# 启动服务
CMD ["R", "-e", "plumber::plumb('plumber.R')$run(host='0.0.0.0', port=8080)"]

3.2 系统依赖说明

依赖包 用途
libcurl4-openssl-dev httr 包HTTP 请求)
libssl-dev openssl 包(加密)
libxml2-dev xml2 包XML 解析)
libsodium-dev sodium 包(加密)
zlib1g-dev httpuv 包Web 服务器)
libnlopt-dev nloptr 包(优化算法)
liblapack-dev 线性代数计算
libblas-dev 基础线性代数
gfortran Fortran 编译器(部分 R 包需要)
cmake nloptr 包构建
curl 健康检查

3.3 构建命令

# 本地构建
cd r-statistics-service
docker build -t ssa-r-statistics:1.0.1 .

# 查看镜像
docker images ssa-r-statistics

# 预期输出
REPOSITORY         TAG       IMAGE ID       CREATED         SIZE
ssa-r-statistics   1.0.1     xxxxxxxxxxxx   x minutes ago   1.81GB

3.4 构建时间参考

阶段 耗时
基础镜像下载 ~2 分钟(首次)
系统依赖安装 ~1 分钟
R 包安装15 个包含 gtsummary/gt ~10 分钟
总计 ~13 分钟

4. 部署指南

4.1 开发环境

使用 docker-compose

# r-statistics-service/docker-compose.yml
services:
  ssa-r-service:
    build: .
    container_name: ssa-r-statistics
    ports:
      - "8082:8080"  # 主机8082 → 容器8080REDCap占用8080/8081
    environment:
      - DEV_MODE=true
    volumes:
      # 开发环境挂载:支持热重载
      - ./plumber.R:/app/plumber.R  # ⚠️ 重要API 入口也需要挂载
      - ./tools:/app/tools
      - ./utils:/app/utils
      - ./tests:/app/tests
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

启动命令:

cd r-statistics-service
docker-compose up -d

4.1.1 热重载机制详解

文件类型 热重载支持 说明
tools/*.R 自动 DEV_MODE=true 时每次请求重新加载
utils/*.R ⚠️ 需重启 服务启动时加载,修改后需 docker-compose restart
plumber.R ⚠️ 需重启 API 路由定义,修改后需 docker-compose restart

最佳实践:

  • 开发新工具时,只需修改 tools/ 目录,无需重启
  • 修改 utils/plumber.R 后,执行 docker-compose restart
  • 添加新的 API 端点后,需要 docker-compose up -d --force-recreate

4.2 生产环境 (SAE)

# SAE 配置
容器镜像: registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.1
实例规格: 2 vCPU, 4 GB
最小实例: 1
最大实例: 5
端口: 8080

环境变量:
  DEV_MODE: "false"

4.3 环境变量

变量 默认值 说明
DEV_MODE false 开发模式(启用热重载,每次请求重新加载工具脚本)

说明:开发环境和生产环境都使用真实 OSS无需 Mock 数据。

  • 开发环境:ai-clinical-data-dev bucket
  • 生产环境:ai-clinical-data bucket

4.4 端口配置

环境 主机端口 容器端口 说明
开发环境 8082 8080 避免与 REDCap 8080/8081 冲突
生产环境 (SAE) 8080 8080 云端无端口冲突

注意Node.js 后端通过 R_SERVICE_URL 环境变量配置 R 服务地址,默认值为 http://localhost:8082


5. API 参考

5.1 健康检查

GET /health

响应:

{
  "status": "ok",
  "timestamp": "2026-02-19 08:00:00",
  "version": "1.0.1",
  "dev_mode": true,
  "tools_loaded": 1
}

5.2 工具列表

GET /api/v1/tools

响应:

{
  "status": "ok",
  "tools": [
    "anova_one",
    "baseline_table",
    "chi_square",
    "correlation",
    "descriptive",
    "fisher",
    "linear_reg",
    "logistic_binary",
    "mann_whitney",
    "t_test_ind",
    "t_test_paired",
    "wilcoxon"
  ],
  "count": 12
}

已实现的统计工具12 个)

Phase 2A 基础工具7 个)

tool_code 名称 场景
ST_T_TEST_IND 独立样本 T 检验 两组连续变量比较(正态)
ST_MANN_WHITNEY Mann-Whitney U 两组连续变量比较(非参数)
ST_T_TEST_PAIRED 配对 T 检验 前后对比
ST_CHI_SQUARE 卡方检验 分类变量关联
ST_CORRELATION 相关分析 Pearson/Spearman 相关
ST_LOGISTIC_BINARY 二元 Logistic 回归 多因素分析(二分类结局)
ST_DESCRIPTIVE 描述性统计 基线表、数据概况

Phase Deploy 新增工具5 个)

tool_code 名称 场景
ST_FISHER Fisher 精确检验 小样本/稀疏列联表(卡方替代)
ST_ANOVA_ONE 单因素方差分析 三组及以上均值比较(含 Kruskal-Wallis 降级)
ST_WILCOXON Wilcoxon 符号秩检验 配对非参数检验(配对 T 替代)
ST_LINEAR_REG 线性回归 连续结局多因素分析
ST_BASELINE_TABLE 基线特征表(复合工具) 基于 gtsummary 的一键式基线表生成

5.3 执行技能

POST /api/v1/skills/{tool_code}
Content-Type: application/json

请求体:

{
  "data_source": {
    "type": "inline",
    "data": [...]
  },
  "params": {
    "group_var": "group",
    "value_var": "value"
  },
  "guardrails": {
    "check_normality": true
  }
}

成功响应:

{
  "status": "success",
  "message": "分析完成",
  "warnings": null,
  "results": {
    "method": "Welch Two Sample t-test",
    "statistic": -5.196,
    "df": 5.98,
    "p_value": 0.002,
    "p_value_fmt": "p = .002"
  },
  "plots": ["data:image/png;base64,..."],
  "trace_log": [...],
  "reproducible_code": "..."
}

错误响应:

{
  "status": "error",
  "error_code": "E001",
  "error_type": "business",
  "message": "列名 'xxx' 在数据中不存在",
  "user_hint": "请检查变量名是否拼写正确"
}

5.4 JIT 护栏检查Phase 2A 新增)

在执行核心统计工具前,调用此端点检验统计假设(正态性、方差齐性等)。

POST /api/v1/guardrails/jit
Content-Type: application/json

请求体:

{
  "data_source": {
    "type": "inline",
    "data": [...]
  },
  "tool_code": "ST_T_TEST_IND",
  "params": {
    "group_var": "sex",
    "value_var": "age"
  }
}

响应:

{
  "status": "success",
  "checks": [
    {
      "check_name": "正态性检验 (组: 1)",
      "passed": true,
      "p_value": 0.234,
      "recommendation": "满足正态性"
    },
    {
      "check_name": "方差齐性检验 (Levene)",
      "passed": false,
      "p_value": 0.012,
      "recommendation": "建议使用 Welch 校正"
    }
  ],
  "suggested_tool": "ST_MANN_WHITNEY",
  "can_proceed": true,
  "all_checks_passed": false
}

使用场景:

  • 工作流执行器在调用核心统计方法前,先调用 JIT 护栏
  • 根据 suggested_tool 自动切换到更合适的方法
  • checks 结果展示给用户

5.5 复合工具示例基线特征表Phase Deploy 新增)

POST /api/v1/skills/ST_BASELINE_TABLE
Content-Type: application/json

请求体:

{
  "data_source": {
    "type": "inline",
    "data": [
      {"group": "Drug", "age": 45, "sex": "M", "sbp": 130, "bmi": 24.5, "smoking": "Yes"},
      {"group": "Placebo", "age": 47, "sex": "F", "sbp": 128, "bmi": 23.8, "smoking": "No"}
    ]
  },
  "params": {
    "group_var": "group",
    "analyze_vars": ["age", "sex", "sbp", "bmi", "smoking"]
  }
}

成功响应(核心字段):

{
  "status": "success",
  "results": {
    "n_total": 30,
    "n_groups": 2,
    "n_variables": 5,
    "significant_vars": ["sbp"],
    "method_info": [
      {"variable": "age", "method": "Wilcoxon rank sum test"},
      {"variable": "sex", "method": "Fisher's exact test"}
    ]
  },
  "report_blocks": [
    {
      "type": "table",
      "headers": ["Characteristic", "Drug, N = 15", "Placebo, N = 15", "p-value"],
      "rows": [["age", "49 (42, 55)", "47 (41, 54)", "0.6"]],
      "title": "基线特征表 (按 group 分组)",
      "metadata": { "is_baseline_table": true, "group_var": "group", "has_p_values": true }
    }
  ]
}

特点: ST_BASELINE_TABLE 是复合工具,基于 gtsummary::tbl_summary() 自动判断变量类型(连续/分类、选择统计方法T 检验/Mann-Whitney/卡方/Fisher输出标准三线表。report_blocks[0].metadata.is_baseline_table = true 触发前端特殊渲染P 值标星、rowspan 合并行)。


6. 开发指南

6.1 添加新工具

  1. tools/ 目录创建 R 脚本:
# tools/my_analysis.R

#' @tool_code ST_MY_ANALYSIS
#' @name 我的分析工具
#' @version 1.0.0
#' @description 工具描述
#' @author SSA-Pro Team

library(glue)
library(ggplot2)
library(base64enc)

run_analysis <- function(input) {
  # ===== 初始化日志 =====
  logs <- c()
  log_add <- function(msg) { logs <<- c(logs, paste0("[", Sys.time(), "] ", msg)) }
  
  # ===== 数据加载 =====
  log_add("开始加载输入数据")
  df <- tryCatch(
    load_input_data(input),
    error = function(e) {
      log_add(paste("数据加载失败:", e$message))
      return(NULL)
    }
  )
  
  if (is.null(df)) {
    return(make_error(ERROR_CODES$E100_INTERNAL_ERROR, details = "数据加载失败"))
  }
  log_add(glue("数据加载成功: {nrow(df)} 行, {ncol(df)} 列"))
  
  # ===== 参数提取 =====
  p <- input$params
  my_var <- p$my_var
  
  # ===== 参数校验 =====
  if (!(my_var %in% names(df))) {
    return(make_error(ERROR_CODES$E001_COLUMN_NOT_FOUND, col = my_var))
  }
  
  # ===== 护栏检查 =====
  guardrail_results <- list()
  warnings_list <- c()
  
  sample_check <- check_sample_size(nrow(df), min_required = 10, action = ACTION_WARN)
  guardrail_results <- c(guardrail_results, list(sample_check))
  
  guardrail_status <- run_guardrail_chain(guardrail_results)
  if (guardrail_status$status == "blocked") {
    return(list(status = "blocked", message = guardrail_status$reason, trace_log = logs))
  }
  
  # ===== 核心计算 =====
  log_add("执行分析...")
  # result <- your_analysis_function(df, ...)
  
  # ===== 构建 report_blocks必须 =====
  blocks <- list()
  
  # Block: 检验结果key_value
  blocks[[length(blocks) + 1]] <- make_kv_block(
    list("方法" = "Your Method", "统计量" = "1.234", "P 值" = format_p_value(0.05)),
    title = "检验结果"
  )
  
  # Block: 图表image
  plot_base64 <- tryCatch({
    p <- ggplot(df, aes(x = df[[my_var]])) + geom_histogram() + theme_minimal()
    tmp_file <- tempfile(fileext = ".png")
    ggsave(tmp_file, p, width = 7, height = 5, dpi = 100)
    base64_str <- base64encode(tmp_file)
    unlink(tmp_file)
    paste0("data:image/png;base64,", base64_str)
  }, error = function(e) NULL)
  
  if (!is.null(plot_base64)) {
    blocks[[length(blocks) + 1]] <- make_image_block(plot_base64, title = "分析图表")
  }
  
  # Block: 结论markdown
  blocks[[length(blocks) + 1]] <- make_markdown_block("分析结论...", title = "结论摘要")
  
  # ===== 生成可复现代码 =====
  reproducible_code <- glue('
# SSA-Pro 自动生成代码
# 工具: 我的分析工具
# 时间: {Sys.time()}
# ================================

df <- read.csv("data.csv")
# 你的分析代码...
')
  
  # ===== 返回结果 =====
  log_add("分析完成")
  
  return(list(
    status = "success",
    message = "分析完成",
    warnings = if (length(warnings_list) > 0) warnings_list else NULL,
    results = list(
      # 统计结果(使用 jsonlite::unbox 保证单值不被包装成数组)
      statistic = jsonlite::unbox(1.234),
      p_value = jsonlite::unbox(0.05),
      p_value_fmt = format_p_value(0.05)
    ),
    report_blocks = blocks,  # ⚠️ 必须!前端 DynamicReport 依赖此字段渲染
    plots = if (!is.null(plot_base64)) list(plot_base64) else list(),
    trace_log = logs,
    reproducible_code = as.character(reproducible_code)
  ))
}
  1. 开发模式:修改 tools/ 下的文件后,无需重启,下次请求自动加载

  2. 测试:

curl -X POST http://localhost:8082/api/v1/skills/ST_MY_ANALYSIS \
  -H "Content-Type: application/json" \
  -d '{"data_source": {"type": "inline", "data": [{"x": 1}, {"x": 2}]}, "params": {"my_var": "x"}}'

6.2 工具命名规范

项目 规范
文件名 小写下划线:t_test_ind.R
tool_code 大写下划线:ST_T_TEST_IND
入口函数 固定名称:run_analysis

6.3 结果格式规范

return(list(
  status = "success" | "error" | "blocked",
  message = "...",
  warnings = c("...") | NULL,
  results = list(
    # 统计结果(使用 jsonlite::unbox() 保证单值不被包装成数组)
  ),
  report_blocks = list(
    # Block-based 标准化输出Phase E+ 协议),前端 DynamicReport.tsx 统一渲染
    # 支持 4 种 Block 类型markdown / table / image / key_value
    # 通过 utils/block_helpers.R 的辅助函数构建
  ),
  plots = list(
    "data:image/png;base64,..."
  ),
  trace_log = c("..."),
  reproducible_code = "..."
))

6.4 Block-based 输出协议Phase E+ 标准)

所有工具必须通过 utils/block_helpers.R 构建 report_blocks[],前端 DynamicReport.tsx 根据 block.type 统一渲染。

辅助函数 Block 类型 用途
make_markdown_block(content, title) markdown 文本结论、方法说明
make_table_block(headers, rows, title, footnote, metadata) table 统计结果表、系数表、事后比较表
make_table_block_from_df(df, title, footnote, digits) table 从 data.frame 快速构建表格
make_image_block(base64_data, title, alt) image 图表base64 编码 PNG
make_kv_block(items, title) key_value 检验统计量、模型拟合指标

示例:

blocks <- list()
blocks[[length(blocks) + 1]] <- make_kv_block(
  list("检验方法" = "Welch t-test", "统计量" = "t = -2.35", "P 值" = "p = .021"),
  title = "检验结果"
)
blocks[[length(blocks) + 1]] <- make_image_block(plot_base64, title = "组间比较箱线图")
blocks[[length(blocks) + 1]] <- make_markdown_block("两组差异具有统计学意义...", title = "结论")

6.5 各工具参数快速参考

调用 POST /api/v1/skills/{tool_code} 时,params 对象需要的字段速查。

tool_code 必需参数 可选参数
ST_T_TEST_IND group_var, value_var guardrails.check_normality
ST_MANN_WHITNEY group_var, value_var
ST_T_TEST_PAIRED before_var, after_var guardrails.check_normality
ST_CHI_SQUARE var1, var2
ST_CORRELATION var_x, var_y method ("auto" / "pearson" / "spearman")
ST_LOGISTIC_BINARY outcome_var, predictors (数组)
ST_DESCRIPTIVE variables (数组) group_var
ST_FISHER var1, var2
ST_ANOVA_ONE group_var, value_var guardrails.check_normality
ST_WILCOXON before_var, after_var
ST_LINEAR_REG outcome_var, predictors (数组) confounders (数组)
ST_BASELINE_TABLE group_var analyze_vars (数组,不传则自动全选)

6.6 R 语言陷阱速查(从实际 Bug 中总结)

Phase Deploy 开发中实际踩过的坑,后续开发者必读。每条附真实错误信息和修复方法。

陷阱 1JSON 数组参数在 R 中是 list,不是 character 向量

错误信息: invalid subscript type 'list'

原因: plumber 解析 JSON ["age", "sex", "bmi"]R 拿到的是 list("age", "sex", "bmi"),不是 c("age", "sex", "bmi")。对 list 做 %in%[ 等操作都会报错。

# ❌ 错误:直接使用 JSON 传入的数组参数
analyze_vars <- p$analyze_vars
missing <- analyze_vars[!(analyze_vars %in% names(df))]  # 报错!

# ✅ 正确:先转换为字符向量
analyze_vars <- as.character(unlist(p$analyze_vars))
missing <- analyze_vars[!(analyze_vars %in% names(df))]  # 正常

影响范围: 所有接收数组参数的工具(predictorsvariablesanalyze_varsconfounders)。

陷阱 2list() 中不能用表达式做键名

错误信息: unexpected '='

原因: R 的 list() 构造器只接受字面量作为名称,不接受 paste0()glue() 等函数调用。

# ❌ 错误:用表达式做键名
items <- list(
  paste0(var_name, " Median") = "5.2"  # 语法错误!
)

# ✅ 正确:先创建 list 再用 [[ 赋值
items <- list()
items[[paste0(var_name, " Median")]] <- "5.2"

陷阱 3tryCatch 会吞掉 warning 导致结果丢失

错误信息: 无明确错误,但返回 NULL 或非预期结果

原因: tryCatch(expr, warning = function(w) {...}) 捕获第一个 warning 后中断 expr 执行,返回 warning handler 的返回值。gtsummary、car 等包常发 warning导致主计算被中断。

# ❌ 错误tryCatch 捕获 warning 会中断执行
tbl <- tryCatch({
  tbl_summary(df) %>% add_p()  # 如果 add_p() 发 warningtbl 变成 NULL
}, warning = function(w) {
  invokeRestart("muffleWarning")  # 在 tryCatch 中无效!
})

# ✅ 正确withCallingHandlers 处理 warning不中断执行tryCatch 只捕获 error
tbl <- tryCatch(
  withCallingHandlers(
    { tbl_summary(df) %>% add_p() },
    warning = function(w) {
      warnings_list <<- c(warnings_list, w$message)
      invokeRestart("muffleWarning")
    }
  ),
  error = function(e) { return(NULL) }
)

陷阱 4gtsummary table_body 的 p.value 是 list 列

错误信息: invalid subscript type 'list'

原因: gtsummary 的内部数据结构 tbl$table_body$p.value 是 list 列(每个元素可能是 NULL 或 numeric不能直接用 < 比较。

# ❌ 错误:直接对 list 列做比较
p_rows <- body[body$p.value < 0.05, ]  # 报错!

# ✅ 正确:先 unlist + as.numeric
p_vals <- as.numeric(unlist(body$p.value))
sig_idx <- which(!is.na(p_vals) & p_vals < 0.05)

陷阱 5浮点数比较不能用 ==

错误信息: 无明确错误,但条件判断逻辑错误

# ❌ 错误:直接比较浮点数
if (sd(values) == 0) { ... }  # 可能因精度问题漏判

# ✅ 正确:使用容差比较
if (isTRUE(sd(values) < .Machine$double.eps^0.5)) { ... }

陷阱 6变量可能为 NULL 导致 glue/round 崩溃

错误信息: non-numeric argument to mathematical functionsubscript out of bounds

原因: 某些统计结果字段(如 fstatistic)在边界条件下为 NULL。

# ❌ 错误:直接使用可能为 NULL 的值
log_add(glue("F = {round(f_stat[1], 2)}"))  # f_stat 为 NULL 时崩溃

# ✅ 正确:先检查再使用
if (!is.null(f_stat)) {
  log_add(glue("F = {round(f_stat[1], 2)}"))
} else {
  log_add("F = NA")
}

陷阱 7新增 R 包后 utils/ 修改需要重启容器

现象: make_table_block() 新增了 metadata 参数,但调用时报 unused argument

原因: utils/*.R 在服务启动时一次性加载,不像 tools/*.R 有热重载。修改后必须:

cd r-statistics-service
docker-compose restart

6.7 开发环境新增 R 包

当新工具依赖尚未安装的 R 包时,有两种方式:

方式 1临时安装到运行中的容器开发测试用

# 容器以 appuser 运行,无写权限,需用 root
docker exec -u root ssa-r-statistics R -e "install.packages('新包名', repos='https://cloud.r-project.org/', quiet=TRUE)"

注意:容器重启后丢失,仅用于开发验证。

方式 2更新 Dockerfile 并重建镜像(正式方案)

  1. Dockerfileinstall.packages() 中添加新包名
  2. 重建:docker-compose up -d --build

7. 运维指南

7.1 日志查看

# 实时日志
docker logs -f ssa-r-statistics

# 最近 100 行
docker logs --tail 100 ssa-r-statistics

7.2 性能监控

# 容器资源使用
docker stats ssa-r-statistics

7.3 重启服务

# 开发环境
docker-compose restart

# 生产环境 (SAE)
通过 SAE 控制台重启实例

7.4 镜像更新

# 1. 构建新镜像
docker build -t ssa-r-statistics:1.0.2 .

# 2. 推送到镜像仓库
docker tag ssa-r-statistics:1.0.2 registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.2
docker push registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.2

# 3. 更新 SAE 部署

8. 常见问题

Q1: 构建时 httpuv 安装失败

错误: fatal error: zlib.h: No such file or directory

解决: 添加 zlib1g-dev 到系统依赖

Q2: 构建时 nloptr 安装失败

错误: CMAKE NOT FOUND

解决: 添加 cmake 到系统依赖

Q3: /tmp 权限问题

错误: cannot open file '/tmp/Rtmpxxx': No such file or directory

解决: 不要在启动命令中清理 /tmp

Q4: DEV_MODE 热重载不生效

原因: 没有挂载 volumes

解决:

volumes:
  - ./tools:/app/tools

Q5: 容器启动后无法访问

检查:

  1. 端口映射是否正确
  2. 健康检查是否通过
  3. 查看容器日志

Q6: 数据加载失败inline 模式)

错误: 内部错误: 数据加载失败

原因: 数据格式不正确,或数据为空

解决:

  1. 确保 data_source.data 是有效的 JSON 数组
  2. 行格式:[{"col1": val1}, {"col1": val2}]
  3. 检查是否有空数据或全 NA 列

Q7: R 脚本语法错误

错误: unexpected symbollexical error

常见原因:

  1. glue() 字符串中使用 \' 转义(应直接使用 '
  2. 中文注释编码问题
  3. 代码块中的花括号不匹配

解决:

# 错误glue 中的转义
glue("# Cramer\'s V = ...")  # ❌

# 正确:直接使用单引号或避免
glue("# Cramer V = ...")     # ✅

Q8: JSON 序列化失败

错误: No method asJSON S3 class: table

原因: R 的 table 对象无法直接序列化为 JSON

解决:

# 错误
observed = as.matrix(contingency_table)  # ❌ 可能保留 table 属性

# 正确:显式转换为纯数值矩阵
observed = matrix(
  as.numeric(contingency_table),
  nrow = nrow(contingency_table),
  ncol = ncol(contingency_table)
)  # ✅

Q9: 新端点返回 404

原因: 修改 plumber.R 后未重启服务

解决:

# 修改 plumber.R 后必须重启
docker-compose restart

# 如果修改了 docker-compose.yml如添加新 volume
docker-compose up -d --force-recreate

Q10: 变量类型判断错误missing value where TRUE/FALSE needed

原因: 对包含 NA 的数据进行布尔比较

解决:

# 错误
if (var_type == "numeric") { ... }  # var_type 可能是 NA

# 正确
if (identical(var_type, "numeric")) { ... }  # ✅ 处理 NA

Q11: 修改 utils/ 后新参数报 unused argument

原因: utils/*.R(如 block_helpers.R)在服务启动时加载进内存,不支持热重载(与 tools/*.R 不同)。

解决:

docker-compose restart

Q12: Docker 已 build 但包仍不存在(there is no package called 'xxx'

原因: docker-compose.yml 中的 volumes 挂载会覆盖镜像中的文件,但不影响已安装的 R 包。常见场景是更新了 Dockerfile 却只用了 docker-compose up -d 而没有加 --build

解决:

# 确保 rebuild
docker-compose up -d --build

# 或临时装包(开发验证)
docker exec -u root ssa-r-statistics R -e "install.packages('xxx', repos='https://cloud.r-project.org/', quiet=TRUE)"

Q13: 工具返回成功但 report_blocks 为空

原因: 返回结构中没有 report_blocks 字段或 blocks 列表为空。

检查清单:

  1. 确认使用了 utils/block_helpers.R 的辅助函数构建 blocks
  2. 确认 return 中包含 report_blocks = blocks
  3. 确认每个 block 至少包含 type 字段
  4. 用测试脚本验证:node r-statistics-service/tests/run_all_tools_test.js

9. 测试指南

9.1 单工具测试

# 测试 T 检验
curl -s -X POST "http://localhost:8082/api/v1/skills/ST_T_TEST_IND" \
  -H "Content-Type: application/json" \
  -d '{
    "data_source": {
      "type": "inline",
      "data": [
        {"group": "A", "value": 23}, {"group": "A", "value": 25},
        {"group": "B", "value": 30}, {"group": "B", "value": 32}
      ]
    },
    "params": {"group_var": "group", "value_var": "value"}
  }'

9.2 健康检查

curl -s http://localhost:8082/health | jq

9.3 R 工具集中测试脚本12 工具 + JIT

项目提供了 R 统计引擎的全工具测试脚本:

# 仅测试 R 服务层12 工具 + JIT 护栏 + report_blocks 校验)
node r-statistics-service/tests/run_all_tools_test.js

测试覆盖:

  • 12 个统计工具Phase 2A × 7 + Phase Deploy × 5
  • JIT 护栏检查ST_T_TEST_IND / ST_ANOVA_ONE / ST_FISHER / ST_LINEAR_REG
  • report_blocks 协议校验类型、必填字段、metadata

9.4 端到端测试脚本(三层联调)

三层联调测试覆盖 R → Python → Node.js

cd docs/03-业务模块/SSA-智能统计分析/05-测试文档
node run_e2e_test.js

测试覆盖:

  • Layer 1: R 服务12 个统计工具 + JIT 护栏)
  • Layer 2: Python DataProfile API
  • Layer 3: Node.js 后端 API登录 → 会话 → 规划 → 执行)

附录:文件结构

r-statistics-service/
├── Dockerfile              # 生产镜像定义(含 gtsummary/gt/broom/scales/gridExtra
├── docker-compose.yml      # 开发环境编排(含 volume 挂载)
├── renv.lock               # R 包版本锁定(备用)
├── .Rprofile               # R 启动配置(备用)
├── plumber.R               # API 入口(含 JIT 护栏端点,自动发现 tools/ 目录)
├── utils/
│   ├── data_loader.R       # 数据加载(支持行格式/列格式)
│   ├── guardrails.R        # 统计护栏 + JIT 检查12 工具全覆盖)
│   ├── error_codes.R       # 错误映射
│   ├── result_formatter.R  # 结果格式化
│   └── block_helpers.R     # Block-based 输出辅助函数Phase E+ 协议)
├── tools/                  # 统计工具12 个)
│   ├── t_test_ind.R        # 独立样本 T 检验
│   ├── t_test_paired.R     # 配对 T 检验
│   ├── mann_whitney.R      # Mann-Whitney U 检验
│   ├── chi_square.R        # 卡方检验
│   ├── correlation.R       # 相关分析
│   ├── logistic_binary.R   # 二元 Logistic 回归
│   ├── descriptive.R       # 描述性统计
│   ├── fisher.R            # 🆕 Fisher 精确检验Phase Deploy
│   ├── anova_one.R         # 🆕 单因素方差分析Phase Deploy
│   ├── wilcoxon.R          # 🆕 Wilcoxon 符号秩检验Phase Deploy
│   ├── linear_reg.R        # 🆕 线性回归Phase Deploy
│   └── baseline_table.R    # 🆕 基线特征表 — 复合工具Phase Deploy
├── tests/
│   ├── run_all_tools_test.js  # 🆕 全工具自动化测试12 工具 + JIT + blocks 校验)
│   ├── test_t_test.json       # T 检验测试数据
│   ├── test_fisher.json       # Fisher 测试数据
│   ├── test_anova_one.json    # ANOVA 测试数据
│   ├── test_wilcoxon.json     # Wilcoxon 测试数据
│   ├── test_linear_reg.json   # 线性回归测试数据
│   ├── test_baseline_table.json # 基线表测试数据
│   └── fixtures/
│       └── normal_data.csv    # 测试数据
├── metadata/               # 工具元数据(预留)
└── templates/              # 解释模板(预留)

更新日志

版本 日期 更新内容
v1.3 2026-02-22 开发者体验增强:新工具模板补全 report_blocks§6.1)、各工具 params 速查表§6.5、R 语言 7 大陷阱实录§6.6)、新增 R 包操作指南§6.7)、新增 Q11-Q13 常见问题
v1.2 2026-02-22 Phase Deploy 完成:工具 7→12+Fisher/ANOVA/Wilcoxon/线性回归/基线表、Dockerfile 新增 gtsummary 等 5 包、Block-based 输出协议文档化§6.4)、全工具测试脚本
v1.1 2026-02-20 Phase 2A 完成7 个统计工具、JIT 护栏、热重载说明、常见问题补充
v1.0 2026-02-19 初始版本架构设计、部署指南、T 检验工具

文档结束