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

@@ -0,0 +1,85 @@
# utils/block_helpers.R
# Block-based 输出协议 — 构造函数
#
# 所有 R 工具通过这些函数构建 report_blocks[]
# 前端 DynamicReport.tsx 根据 block.type 统一渲染。
# 支持 4 种 Block 类型markdown / table / image / key_value
#' 构造 Markdown 文本块
#' @param content Markdown 格式文本(支持标题、列表、加粗等)
#' @param title 可选标题(前端渲染为区块标题)
#' @return block list
make_markdown_block <- function(content, title = NULL) {
block <- list(type = "markdown", content = content)
if (!is.null(title)) block$title <- title
block
}
#' 构造表格块
#' @param headers 列名字符向量,如 c("组别", "均值", "标准差")
#' @param rows 行数据列表,每行为字符向量,如 list(c("A", "5.2", "1.3"), ...)
#' @param title 可选表格标题
#' @param footnote 可选脚注(如方法说明)
#' @return block list
make_table_block <- function(headers, rows, title = NULL, footnote = NULL) {
block <- list(
type = "table",
headers = as.list(headers),
rows = lapply(rows, as.list)
)
if (!is.null(title)) block$title <- title
if (!is.null(footnote)) block$footnote <- footnote
block
}
#' 从 data.frame 构造表格块(便捷方法)
#' @param df data.frame
#' @param title 可选表格标题
#' @param footnote 可选脚注
#' @param digits 数值列保留小数位数,默认 3
#' @return block list
make_table_block_from_df <- function(df, title = NULL, footnote = NULL, digits = 3) {
headers <- colnames(df)
rows <- lapply(seq_len(nrow(df)), function(i) {
lapply(df[i, , drop = FALSE], function(val) {
if (is.numeric(val)) {
format(round(val, digits), nsmall = digits)
} else {
as.character(val)
}
})
})
make_table_block(headers, rows, title = title, footnote = footnote)
}
#' 构造图片块
#' @param base64_data 完整的 data URI如 "data:image/png;base64,..."
#' @param title 可选图片标题
#' @param alt 可选 alt 文本(无障碍 + Word 导出用)
#' @return block list
make_image_block <- function(base64_data, title = NULL, alt = NULL) {
block <- list(type = "image", data = base64_data)
if (!is.null(title)) block$title <- title
if (!is.null(alt)) block$alt <- alt
block
}
#' 构造键值对块
#' @param items 命名列表或 list(list(key=..., value=...), ...)
#' @param title 可选标题
#' @return block list
make_kv_block <- function(items, title = NULL) {
if (!is.null(names(items)) && length(names(items)) > 0 && names(items)[1] != "") {
kv_list <- lapply(names(items), function(k) {
list(key = k, value = as.character(items[[k]]))
})
} else {
kv_list <- items
}
block <- list(type = "key_value", items = kv_list)
if (!is.null(title)) block$title <- title
block
}