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:
@@ -213,6 +213,60 @@ cat("Cramer V =", round(cramers_v, 3), "\\n")
|
||||
mosaicplot(contingency_table, main = "Mosaic Plot", color = TRUE)
|
||||
')
|
||||
|
||||
# ===== 构建 report_blocks =====
|
||||
# Block 1: 列联表
|
||||
table_headers <- c(var1, as.character(colnames(contingency_table)))
|
||||
table_rows <- lapply(seq_len(nrow(contingency_table)), function(i) {
|
||||
c(as.character(rownames(contingency_table)[i]), as.character(contingency_table[i, ]))
|
||||
})
|
||||
blocks <- list(
|
||||
make_table_block(table_headers, table_rows, title = "列联表")
|
||||
)
|
||||
|
||||
# Block 2: 检验结果键值对
|
||||
if (use_fisher) {
|
||||
kv_items <- list(
|
||||
"方法" = method_used,
|
||||
"P 值" = output_results$p_value_fmt
|
||||
)
|
||||
if (!is.null(output_results$odds_ratio)) {
|
||||
kv_items[["比值比"]] <- as.character(round(as.numeric(output_results$odds_ratio), 4))
|
||||
}
|
||||
if (!is.null(output_results$conf_int)) {
|
||||
kv_items[["95% 置信区间"]] <- sprintf("[%.4f, %.4f]", output_results$conf_int[1], output_results$conf_int[2])
|
||||
}
|
||||
} else {
|
||||
kv_items <- list(
|
||||
"方法" = method_used,
|
||||
"χ² 统计量" = as.character(round(as.numeric(output_results$statistic), 4)),
|
||||
"自由度" = as.character(output_results$df),
|
||||
"P 值" = output_results$p_value_fmt,
|
||||
"Cramér's V" = as.character(output_results$effect_size$cramers_v),
|
||||
"效应量解释" = output_results$effect_size$interpretation
|
||||
)
|
||||
}
|
||||
blocks[[length(blocks) + 1]] <- make_kv_block(kv_items, title = "检验结果")
|
||||
|
||||
# Block 3: 马赛克图(若有)
|
||||
if (!is.null(plot_base64)) {
|
||||
blocks[[length(blocks) + 1]] <- make_image_block(plot_base64, title = "马赛克图")
|
||||
}
|
||||
|
||||
# Block 4: 结论摘要
|
||||
p_val <- as.numeric(output_results$p_value)
|
||||
conclusion <- if (p_val < 0.05) {
|
||||
glue("在 α=0.05 水平下,{var1} 与 {var2} 之间存在显著关联(P {output_results$p_value_fmt})。")
|
||||
} else {
|
||||
glue("在 α=0.05 水平下,未发现 {var1} 与 {var2} 之间的显著关联(P {output_results$p_value_fmt})。")
|
||||
}
|
||||
if (!use_fisher) {
|
||||
conclusion <- paste0(conclusion, " 效应量为", output_results$effect_size$interpretation,
|
||||
"(Cramér's V = ", output_results$effect_size$cramers_v, ")。")
|
||||
} else if (!is.null(output_results$odds_ratio)) {
|
||||
conclusion <- paste0(conclusion, " 比值比 = ", round(as.numeric(output_results$odds_ratio), 4), "。")
|
||||
}
|
||||
blocks[[length(blocks) + 1]] <- make_markdown_block(conclusion, title = "结论摘要")
|
||||
|
||||
# ===== 返回结果 =====
|
||||
log_add("分析完成")
|
||||
|
||||
@@ -221,6 +275,7 @@ mosaicplot(contingency_table, main = "Mosaic Plot", color = TRUE)
|
||||
message = "分析完成",
|
||||
warnings = if (length(warnings_list) > 0) warnings_list else NULL,
|
||||
results = output_results,
|
||||
report_blocks = blocks,
|
||||
plots = if (!is.null(plot_base64)) list(plot_base64) else list(),
|
||||
trace_log = logs,
|
||||
reproducible_code = as.character(reproducible_code)
|
||||
|
||||
Reference in New Issue
Block a user