Files
AIclinicalresearch/r-statistics-service/utils/error_codes.R
HaHafeng 971e903acf chore(deploy): finalize 0309 SAE rollout updates
Sync deployment documentation to the final successful SAE state and clear pending deployment checklist items. Include backend/frontend/R hardening and diagnostics improvements required for stable production behavior.

Made-with: Cursor
2026-03-09 22:27:11 +08:00

210 lines
7.4 KiB
R
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# utils/error_codes.R
# 结构化错误码,便于 LLM 自愈
ERROR_CODES <- list(
# 业务错误(可被 Planner 修复)
E001_COLUMN_NOT_FOUND = list(
code = "E001",
type = "business",
message_template = "列名 '{col}' 在数据中不存在",
user_hint = "请检查变量名是否拼写正确"
),
E002_TYPE_MISMATCH = list(
code = "E002",
type = "business",
message_template = "列 '{col}' 类型应为 {expected},实际为 {actual}",
user_hint = "该列包含非数值数据,请检查数据格式"
),
E003_INSUFFICIENT_GROUPS = list(
code = "E003",
type = "business",
message_template = "分组变量 '{col}' 应有 {expected} 个水平,实际有 {actual} 个",
user_hint = "分组变量的取值个数不符合要求"
),
E004_SAMPLE_TOO_SMALL = list(
code = "E004",
type = "business",
message_template = "样本量 {n} 不足,至少需要 {min_required}",
user_hint = "数据量太少,无法进行统计分析"
),
# 统计计算错误(用户友好映射)
E005_SINGULAR_MATRIX = list(
code = "E005",
type = "business",
message_template = "矩阵计算异常: {details}",
user_hint = "变量之间可能存在多重共线性,建议移除高度相关的变量"
),
E006_CONVERGENCE_FAILED = list(
code = "E006",
type = "business",
message_template = "模型未能收敛: {details}",
user_hint = "算法未能找到稳定解,可能需要调整参数或检查数据"
),
E007_VARIANCE_ZERO = list(
code = "E007",
type = "business",
message_template = "变量 '{col}' 方差为零",
user_hint = "该列的所有值都相同,无法进行比较"
),
# 系统错误(需人工介入)
E100_INTERNAL_ERROR = list(
code = "E100",
type = "system",
message_template = "内部错误: {details}",
user_hint = "系统繁忙,请稍后重试"
),
E101_PACKAGE_MISSING = list(
code = "E101",
type = "system",
message_template = "缺少依赖包: {package}",
user_hint = "请联系管理员"
),
E102_FUNCTION_NOT_FOUND = list(
code = "E102",
type = "business",
message_template = "找不到函数: {func}",
user_hint = "请检查函数名是否正确,或确认已加载相关包"
)
)
# R 原始错误到错误码的映射字典
R_ERROR_MAPPING <- list(
"system is computationally singular" = "E005_SINGULAR_MATRIX",
"did not converge" = "E006_CONVERGENCE_FAILED",
"constant" = "E007_VARIANCE_ZERO",
"object '.*' not found" = "E001_COLUMN_NOT_FOUND",
"undefined columns" = "E001_COLUMN_NOT_FOUND",
"subscript out of bounds" = "E100_INTERNAL_ERROR",
"cannot coerce" = "E002_TYPE_MISMATCH",
"non-numeric argument" = "E002_TYPE_MISMATCH",
"not meaningful for factors" = "E002_TYPE_MISMATCH",
"missing value where TRUE/FALSE needed" = "E100_INTERNAL_ERROR",
"replacement has" = "E100_INTERNAL_ERROR",
"could not find function" = "E102_FUNCTION_NOT_FOUND",
"there is no package called" = "E101_PACKAGE_MISSING",
"cannot open the connection" = "E100_INTERNAL_ERROR",
"singular gradient" = "E005_SINGULAR_MATRIX",
"rank deficien" = "E005_SINGULAR_MATRIX",
"contrasts can be applied only to factors" = "E002_TYPE_MISMATCH",
"need at least 2 observations" = "E004_SAMPLE_TOO_SMALL",
"not enough observations" = "E004_SAMPLE_TOO_SMALL",
"sample size must be" = "E004_SAMPLE_TOO_SMALL",
"groups with fewer than" = "E004_SAMPLE_TOO_SMALL"
)
# 构造错误响应(含用户友好提示)
make_error <- function(error_def, ...) {
params <- list(...)
msg <- error_def$message_template
for (name in names(params)) {
msg <- gsub(paste0("\\{", name, "\\}"), as.character(params[[name]]), msg)
}
return(list(
status = "error",
error_code = error_def$code,
error_type = error_def$type,
message = msg,
user_hint = error_def$user_hint
))
}
#' Agent 通道专用:结构化错误格式
#' 提取行号、错误分类、修复建议,方便 LLM 理解并修复
format_agent_error <- function(e, code, warnings = list(), messages = character(0)) {
raw_msg <- conditionMessage(e)
error_line <- NULL
line_match <- regmatches(raw_msg, regexpr("(line \\d+|at \\d+:\\d+|:( *)\\d+)", raw_msg))
if (length(line_match) > 0 && nchar(line_match[1]) > 0) {
nums <- regmatches(line_match[1], gregexpr("\\d+", line_match[1]))[[1]]
error_line <- as.integer(nums[1])
}
error_context <- NULL
if (!is.null(error_line) && !is.null(code)) {
code_lines <- strsplit(code, "\n")[[1]]
start <- max(1, error_line - 3)
end <- min(length(code_lines), error_line + 3)
context_lines <- paste0(
ifelse(start:end == error_line, ">>> ", " "),
sprintf("%3d| ", start:end),
code_lines[start:end]
)
error_context <- paste(context_lines, collapse = "\n")
}
error_code <- "E_EXEC"
error_type <- "runtime"
user_hint <- raw_msg
for (pattern in names(R_ERROR_MAPPING)) {
if (grepl(pattern, raw_msg, ignore.case = TRUE)) {
key <- R_ERROR_MAPPING[[pattern]]
info <- ERROR_CODES[[key]]
error_code <- info$code
error_type <- info$type
user_hint <- paste0(info$user_hint, " | ", raw_msg)
break
}
}
friendly_msg <- paste0(
"[", error_code, "] ", raw_msg,
if (!is.null(error_line)) paste0(" (约第 ", error_line, " 行)") else "",
if (length(warnings) > 0) paste0("\n[Warnings] ", paste(utils::head(warnings, 5), collapse = "; ")) else ""
)
list(
message = friendly_msg,
raw_error = raw_msg,
error_code = error_code,
error_type = error_type,
error_line = error_line,
error_context = error_context,
user_hint = user_hint,
warnings = if (length(warnings) > 0) warnings else NULL
)
}
# 尝试将 R 原始错误映射为友好错误码
map_r_error <- function(raw_error_msg) {
for (pattern in names(R_ERROR_MAPPING)) {
if (grepl(pattern, raw_error_msg, ignore.case = TRUE)) {
error_key <- R_ERROR_MAPPING[[pattern]]
# E101: 提取缺失包名there is no package called 'xxx'
if (error_key == "E101_PACKAGE_MISSING") {
pkg <- "unknown"
m <- regexec("there is no package called ['\"]([^'\"]+)['\"]", raw_error_msg, ignore.case = TRUE)
mm <- regmatches(raw_error_msg, m)[[1]]
if (length(mm) >= 2) pkg <- mm[2]
return(make_error(ERROR_CODES[[error_key]], package = pkg))
}
# E102: 提取找不到的函数名could not find function "xxx"
if (error_key == "E102_FUNCTION_NOT_FOUND") {
func <- "unknown"
m <- regexec("could not find function ['\"]([^'\"]+)['\"]", raw_error_msg, ignore.case = TRUE)
mm <- regmatches(raw_error_msg, m)[[1]]
if (length(mm) >= 2) func <- mm[2]
return(make_error(ERROR_CODES[[error_key]], func = func))
}
# E001: 尝试提取缺失对象名object 'xxx' not found
if (error_key == "E001_COLUMN_NOT_FOUND") {
col <- "unknown"
m <- regexec("object ['\"]([^'\"]+)['\"] not found", raw_error_msg, ignore.case = TRUE)
mm <- regmatches(raw_error_msg, m)[[1]]
if (length(mm) >= 2) col <- mm[2]
return(make_error(ERROR_CODES[[error_key]], col = col))
}
return(make_error(ERROR_CODES[[error_key]], details = raw_error_msg))
}
}
# 无法映射,返回通用内部错误
return(make_error(ERROR_CODES$E100_INTERNAL_ERROR, details = raw_error_msg))
}