Files
HaHafeng ac724266c1 feat(ssa): SSA Agent mode MVP - prompt management + Phase 5A guardrails + UX enhancements
Backend:
- Agent core prompts (Planner + Coder) now loaded from PromptService with 3-tier fallback (DB -> cache -> hardcoded)
- Seed script (seed-ssa-agent-prompts.ts) for idempotent SSA_AGENT_PLANNER + SSA_AGENT_CODER setup
- SSA fallback prompts added to prompt.fallbacks.ts
- Phase 5A: XML tag extraction, defensive programming prompt, high-fidelity schema injection, AST pre-check
- Default agent mode migration + session CRUD (rename/delete) APIs
- R Docker: structured error handling (20+ patterns) + AST syntax pre-check

Frontend:
- Default agent mode (QPER toggle removed), view code fix, analysis result cards in chat
- Session history sidebar with inline rename/delete, robust plan parsing from reviewResult
- R code export wrapper for local reproducibility (package checks + data loader + polyfills)
- SSA workspace CSS updates for sidebar actions and plan display

Docs:
- SSA module doc v4.2: Prompt inventory (2 Agent active / 11 QPER archived), dev progress updated
- System overview doc v6.8: SSA Agent MVP milestone
- Deployment checklist: DB-5 (seed script) + BE-10 (prompt management)

Made-with: Cursor
2026-03-08 15:23:09 +08:00

5.5 KiB
Raw Permalink Blame History

架构优化方案R 代码的本地复现与导出包装 (Export Wrapper)

问题诊断: 大模型在 Agent 管线中生成的 R 代码是高度依赖“平台沙箱环境”的。它缺失了数据读取操作 (df),且引用了平台专属的 UI 辅助函数 (make_table_block_from_df 等)。

解决目标: 用户点击“下载 R 代码”时,系统必须动态注入“本地兼容层”,使得代码可以在任何一台普通的 RStudio 中一键运行。

一、 根本解决方案:导出包装器 (Export Wrapper)

当用户点击“下载 R 代码”时,前端不能仅仅把 Agent 生成的代码原样保存为 .R 文件。我们需要像编译器一样,将代码包裹在一个标准的本地复现模板中。

完整的导出文件应该由 3 个部分拼接而成:

  1. 数据加载层 (Data Loading)
  2. 函数兼容层 (Polyfills / Mock Helpers)
  3. Agent 生成的核心代码 (Core Logic)

二、 包装器代码实现范例

前端或后端在生成下载文件时,请使用以下字符串拼接逻辑:

// 伪代码:在前端 SSACodeModal 或后端导出 API 中实现
function generateDownloadableRCode(agentCode: string, fileName: string): string {

const headerAndPolyfills = `
# =====================================================================
# SSA-Pro 智能统计分析 - 本地复现脚本
# =====================================================================

# 1. 自动安装缺失的包 (本地复现安全保障)
required_packages <- c("dplyr", "gtsummary", "base64enc", "ggplot2")
new_packages <- required_packages[!(required_packages %in% installed.packages()[,"Package"])]
if(length(new_packages)) install.packages(new_packages)

suppressPackageStartupMessages({
library(dplyr)
library(gtsummary)
library(base64enc)
library(ggplot2)
})

# =====================================================================
# 2. 数据读取 (请确保数据文件与本脚本在同一目录下)
# =====================================================================
# 系统已将您的原始数据名填入,如果路径不同请手动修改:
file_name <- "${fileName}"

if (file.exists(file_name)) {
if (grepl("\\\\.csv$", file_name, ignore.case = TRUE)) {
df <- read.csv(file_name, stringsAsFactors = FALSE)
} else if (grepl("\\\\.xlsx?$", file_name, ignore.case = TRUE)) {
library(readxl)
df <- read_excel(file_name)
}
} else {
# 如果找不到文件,生成测试数据以防代码直接崩溃
warning(paste("找不到数据文件:", file_name, "。将使用模拟数据进行演示。"))
df <- data.frame(
root_curve = sample(c(1, 2), 100, replace = TRUE),
Yqol = sample(c(0, 1), 100, replace = TRUE)
)
}

# =====================================================================
# 3. 平台 UI 辅助函数本地兼容层 (Polyfills)
# 这使得平台专用的 make_*_block 函数在本地控制台优雅地输出,而不报错
# =====================================================================
make_markdown_block <- function(text) {
cat("\\n========================================\\n")
cat(text, "\\n")
}

make_table_block_from_df <- function(data, title="", footnote="") {
cat("\\n---", title, "---\\n")
print(data)
if(footnote != "") cat("注:", footnote, "\\n")
return(list(type="table"))
}

make_image_block <- function(base64_data, title="", alt="") {
cat("\\n[图形已生成:", title, "- 请查看 RStudio 的 Plots 面板]\\n")
return(list(type="image"))
}

make_kv_block <- function(items, title="") {
cat("\\n---", title, "---\\n")
print(unlist(items))
return(list(type="key_value"))
}

# =====================================================================
# 4. 核心分析代码 (由 AI Agent 生成)
# =====================================================================
`;

return headerAndPolyfills + "\n" + agentCode;
}

三、 为什么必须这么做?(架构收益)

  1. 防患于未然的数据读取: 我们不仅注入了 read.csv还加入了 file.exists 检查。如果医生把 R 脚本发给另一个没有原始数据的统计师,代码会自动生成一组 Mock模拟数据。这样 R 脚本无论如何都能跑通,极大提升了产品的专业感。
  2. 优雅的 Polyfills (兼容层) 技术:
    这是前端工程(如适配老版本浏览器)最常用的技术。我们在脚本头部用普通的 print() 和 cat() 重写了 make_table_block_from_df 等函数。这样,大模型生成的复杂 UI 代码在本地 RStudio 中执行时,不仅不会报错,还会把结果整齐地打印在本地控制台上
  3. 保持 Agent Prompt 的纯净:
    我们不需要去修改 CoderAgent 的 System Prompt 让它“记得写读取文件的代码”。因为每次让 LLM 动态写数据读取逻辑它很容易因为编码UTF-8 vs GBK或文件路径错误而翻车。把这种死板的工作交给前端的字符串拼接Wrapper是最稳定、最省 Token 的做法。

四、 实施建议

请前端团队接手此任务:

在 AgentCodePanel.tsx 或 SSACodeModal.tsx 中,找到处理**“导出 R 脚本 (Export/Download .R)”**的 onClick 函数。在生成 Blob 对象并触发浏览器下载之前,将原有的代码字符串通过上述包装器函数处理一遍即可。这只需半小时即可实现。