feat(ssa): Complete QPER architecture - Query, Planner, Execute, Reflection layers

Implement the full QPER intelligent analysis pipeline:

- Phase E+: Block-based standardization for all 7 R tools, DynamicReport renderer, Word export enhancement

- Phase Q: LLM intent parsing with dynamic Zod validation against real column names, ClarificationCard component, DataProfile is_id_like tagging

- Phase P: ConfigLoader with Zod schema validation and hot-reload API, DecisionTableService (4-dimension matching), FlowTemplateService with EPV protection, PlannedTrace audit output

- Phase R: ReflectionService with statistical slot injection, sensitivity analysis conflict rules, ConclusionReport with section reveal animation, conclusion caching API, graceful R error classification

End-to-end test: 40/40 passed across two complete analysis scenarios.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-21 18:15:53 +08:00
parent 428a22adf2
commit 371e1c069c
73 changed files with 9242 additions and 706 deletions

View File

@@ -173,6 +173,62 @@ ggplot(df, aes(x = .data[[group_var]], y = .data[[value_var]])) +
labs(title = paste("Distribution of", value_var, "by", group_var))
')
# ===== 构建 report_blocks =====
log_add("构建 report_blocks")
blocks <- list()
# Block 1: 样本概况(两组 n, median, IQR
g1_label <- as.character(groups[1])
g2_label <- as.character(groups[2])
blocks[[length(blocks) + 1]] <- make_kv_block(
title = "样本概况",
items = list(
list(key = paste0(g1_label, " (n, Median, IQR)"),
value = paste0("n=", n1, ", ", round(median(g1_vals), 3), ", ", round(IQR(g1_vals), 3))),
list(key = paste0(g2_label, " (n, Median, IQR)"),
value = paste0("n=", n2, ", ", round(median(g2_vals), 3), ", ", round(IQR(g2_vals), 3)))
)
)
# Block 2: 检验结果U 统计量, Z 值, P 值, 效应量 r
blocks[[length(blocks) + 1]] <- make_table_block(
title = "Mann-Whitney U 检验结果",
headers = c("U 统计量", "Z 值", "P 值", "效应量 r", "效应量解释"),
rows = list(
list(
round(as.numeric(U), 4),
round(z_value, 4),
format_p_value(result$p.value),
round(effect_r, 4),
effect_interpretation
)
),
footnote = "Wilcoxon rank sum test with continuity correction"
)
# Block 3: 箱线图(如果 plot_base64 不为 NULL
if (!is.null(plot_base64)) {
blocks[[length(blocks) + 1]] <- make_image_block(
base64_data = plot_base64,
title = paste0(value_var, " by ", group_var),
alt = paste("箱线图:", value_var, "按", group_var, "分组")
)
}
# Block 4: 结论摘要
sig <- if (result$p.value < 0.05) "存在统计学显著差异" else "差异无统计学意义"
blocks[[length(blocks) + 1]] <- make_markdown_block(
title = "结果摘要",
content = paste0(
"两组 **", value_var, "** 的比较Mann-Whitney U 检验):",
"U = ", round(as.numeric(U), 2),
"Z = ", round(z_value, 3),
"P ", format_p_value(result$p.value),
",效应量 r = ", round(effect_r, 3), "", effect_interpretation, ")。",
"两组间", sig, "。"
)
)
# ===== 返回结果 =====
log_add("分析完成")
@@ -209,6 +265,7 @@ ggplot(df, aes(x = .data[[group_var]], y = .data[[value_var]])) +
)
)
),
report_blocks = blocks,
plots = if (!is.null(plot_base64)) list(plot_base64) else list(),
trace_log = logs,
reproducible_code = as.character(reproducible_code)