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>
This commit is contained in:
@@ -21,7 +21,7 @@ make_markdown_block <- function(content, title = NULL) {
|
||||
#' @param title 可选表格标题
|
||||
#' @param footnote 可选脚注(如方法说明)
|
||||
#' @return block list
|
||||
make_table_block <- function(headers, rows, title = NULL, footnote = NULL) {
|
||||
make_table_block <- function(headers, rows, title = NULL, footnote = NULL, metadata = NULL) {
|
||||
block <- list(
|
||||
type = "table",
|
||||
headers = as.list(headers),
|
||||
@@ -29,6 +29,7 @@ make_table_block <- function(headers, rows, title = NULL, footnote = NULL) {
|
||||
)
|
||||
if (!is.null(title)) block$title <- title
|
||||
if (!is.null(footnote)) block$footnote <- footnote
|
||||
if (!is.null(metadata)) block$metadata <- metadata
|
||||
block
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,98 @@ run_jit_guardrails <- function(df, tool_code, params) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tool_code == "ST_ANOVA_ONE") {
|
||||
group_var <- params$group_var
|
||||
value_var <- params$value_var
|
||||
|
||||
if (!is.null(group_var) && !is.null(value_var)) {
|
||||
groups <- unique(df[[group_var]])
|
||||
|
||||
for (g in groups) {
|
||||
vals <- df[df[[group_var]] == g, value_var]
|
||||
vals <- vals[!is.na(vals)]
|
||||
if (length(vals) >= 3) {
|
||||
norm_result <- check_normality(vals, alpha = 0.05)
|
||||
checks <- c(checks, list(list(
|
||||
check_name = glue("正态性检验 (组: {g})"),
|
||||
passed = norm_result$passed,
|
||||
p_value = norm_result$p_value,
|
||||
recommendation = if (norm_result$passed) "满足正态性" else "建议使用 Kruskal-Wallis"
|
||||
)))
|
||||
|
||||
if (!norm_result$passed) {
|
||||
suggested_tool <- "Kruskal-Wallis (内置于 ST_ANOVA_ONE)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryCatch({
|
||||
homo_result <- check_homogeneity(df, group_var, value_var, alpha = 0.05)
|
||||
checks <- c(checks, list(list(
|
||||
check_name = "方差齐性检验 (Levene)",
|
||||
passed = homo_result$passed,
|
||||
p_value = homo_result$p_value,
|
||||
recommendation = if (homo_result$passed) "方差齐性满足" else "建议使用 Welch ANOVA"
|
||||
)))
|
||||
}, error = function(e) {
|
||||
message("方差齐性检验失败: ", e$message)
|
||||
})
|
||||
}
|
||||
|
||||
} else if (tool_code == "ST_WILCOXON") {
|
||||
before_var <- params$before_var
|
||||
after_var <- params$after_var
|
||||
|
||||
if (!is.null(before_var) && !is.null(after_var)) {
|
||||
diff_vals <- df[[after_var]] - df[[before_var]]
|
||||
diff_vals <- diff_vals[!is.na(diff_vals)]
|
||||
|
||||
checks <- c(checks, list(list(
|
||||
check_name = "配对样本量检查",
|
||||
passed = length(diff_vals) >= 5,
|
||||
recommendation = if (length(diff_vals) >= 5) "样本量充足" else "配对样本量不足"
|
||||
)))
|
||||
}
|
||||
|
||||
} else if (tool_code %in% c("ST_FISHER", "ST_CHI_SQUARE")) {
|
||||
var1 <- params$var1
|
||||
var2 <- params$var2
|
||||
|
||||
if (!is.null(var1) && !is.null(var2)) {
|
||||
ct <- table(df[[var1]], df[[var2]])
|
||||
expected <- tryCatch(chisq.test(ct)$expected, error = function(e) NULL)
|
||||
|
||||
if (!is.null(expected)) {
|
||||
low_pct <- sum(expected < 5) / length(expected)
|
||||
checks <- c(checks, list(list(
|
||||
check_name = "期望频数检查",
|
||||
passed = low_pct <= 0.2,
|
||||
recommendation = if (low_pct <= 0.2) "期望频数满足卡方检验条件" else "建议使用 Fisher 精确检验"
|
||||
)))
|
||||
|
||||
if (low_pct > 0.2 && tool_code == "ST_CHI_SQUARE") {
|
||||
suggested_tool <- "ST_FISHER"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tool_code == "ST_LINEAR_REG") {
|
||||
outcome_var <- params$outcome_var
|
||||
predictors <- params$predictors
|
||||
|
||||
if (!is.null(outcome_var)) {
|
||||
vals <- df[[outcome_var]][!is.na(df[[outcome_var]])]
|
||||
if (length(vals) >= 3) {
|
||||
norm_result <- check_normality(vals, alpha = 0.05)
|
||||
checks <- c(checks, list(list(
|
||||
check_name = glue("结局变量正态性 ({outcome_var})"),
|
||||
passed = norm_result$passed,
|
||||
p_value = norm_result$p_value,
|
||||
recommendation = if (norm_result$passed) "满足正态性" else "结局变量分布偏态,结果需谨慎解读"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 汇总
|
||||
|
||||
Reference in New Issue
Block a user