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:
@@ -199,7 +199,83 @@ if (any(categorical_vars)) {{
|
||||
|
||||
# ===== 返回结果 =====
|
||||
log_add("分析完成")
|
||||
|
||||
|
||||
# ===== 构建 report_blocks =====
|
||||
blocks <- list()
|
||||
|
||||
# Block 1: 数据概况
|
||||
kv_items <- list(
|
||||
"总样本量" = as.character(summary_stats$n_total),
|
||||
"变量数" = as.character(summary_stats$n_variables),
|
||||
"数值变量数" = as.character(summary_stats$n_numeric),
|
||||
"分类变量数" = as.character(summary_stats$n_categorical)
|
||||
)
|
||||
if (!is.null(groups)) {
|
||||
kv_items$group_var <- group_var
|
||||
kv_items$groups <- paste(sapply(summary_stats$groups, function(g) paste0(g$name, "(n=", g$n, ")")), collapse = ", ")
|
||||
}
|
||||
blocks[[length(blocks) + 1]] <- make_kv_block(kv_items, title = "数据概况")
|
||||
|
||||
# Block 2: 数值变量汇总表
|
||||
numeric_vars <- names(results_list)[sapply(results_list, function(x) {
|
||||
if (is.list(x) && !is.null(x$type)) x$type == "numeric" else FALSE
|
||||
})]
|
||||
if (length(numeric_vars) > 0) {
|
||||
if (is.null(groups)) {
|
||||
num_headers <- c("变量名", "n", "mean", "sd", "median", "Q1", "Q3", "min", "max")
|
||||
num_rows <- lapply(numeric_vars, function(v) {
|
||||
s <- results_list[[v]]
|
||||
c(v, as.character(s$n), as.character(s$mean), as.character(s$sd),
|
||||
as.character(s$median), as.character(s$q1), as.character(s$q3),
|
||||
as.character(s$min), as.character(s$max))
|
||||
})
|
||||
} else {
|
||||
num_headers <- c("变量名", as.character(groups))
|
||||
num_rows <- lapply(numeric_vars, function(v) {
|
||||
s <- results_list[[v]]
|
||||
row <- c(v)
|
||||
for (g in groups) {
|
||||
gs <- s$by_group[[as.character(g)]]
|
||||
row <- c(row, if (!is.null(gs$formatted)) gs$formatted else "-")
|
||||
}
|
||||
row
|
||||
})
|
||||
}
|
||||
blocks[[length(blocks) + 1]] <- make_table_block(num_headers, num_rows, title = "数值变量汇总表")
|
||||
}
|
||||
|
||||
# Block 3: 分类变量汇总表
|
||||
cat_vars <- names(results_list)[sapply(results_list, function(x) {
|
||||
if (is.list(x) && !is.null(x$type)) x$type == "categorical" else FALSE
|
||||
})]
|
||||
if (length(cat_vars) > 0) {
|
||||
cat_headers <- c("变量名", "水平", "n", "百分比")
|
||||
cat_rows <- list()
|
||||
for (v in cat_vars) {
|
||||
s <- results_list[[v]]
|
||||
if (is.null(groups)) {
|
||||
for (lev in s$levels) {
|
||||
cat_rows[[length(cat_rows) + 1]] <- c(v, lev$level, as.character(lev$n), paste0(lev$pct, "%"))
|
||||
}
|
||||
} else {
|
||||
for (g in groups) {
|
||||
gs <- s$by_group[[as.character(g)]]
|
||||
for (lev in gs$levels) {
|
||||
cat_rows[[length(cat_rows) + 1]] <- c(paste0(v, " (", g, ")"), lev$level, as.character(lev$n), paste0(lev$pct, "%"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (length(cat_rows) > 0) {
|
||||
blocks[[length(blocks) + 1]] <- make_table_block(cat_headers, cat_rows, title = "分类变量汇总表")
|
||||
}
|
||||
}
|
||||
|
||||
# Block 4+: 各图表
|
||||
for (i in seq_along(plots)) {
|
||||
blocks[[length(blocks) + 1]] <- make_image_block(plots[[i]], title = paste0("图表 ", i), alt = paste0("描述性统计图 ", i))
|
||||
}
|
||||
|
||||
return(list(
|
||||
status = "success",
|
||||
message = "分析完成",
|
||||
@@ -208,6 +284,7 @@ if (any(categorical_vars)) {{
|
||||
summary = summary_stats,
|
||||
variables = results_list
|
||||
),
|
||||
report_blocks = blocks,
|
||||
plots = plots,
|
||||
trace_log = logs,
|
||||
reproducible_code = as.character(reproducible_code)
|
||||
|
||||
Reference in New Issue
Block a user