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
5.5 KiB
架构优化方案:R 代码的本地复现与导出包装 (Export Wrapper)
问题诊断: 大模型在 Agent 管线中生成的 R 代码是高度依赖“平台沙箱环境”的。它缺失了数据读取操作 (df),且引用了平台专属的 UI 辅助函数 (make_table_block_from_df 等)。
解决目标: 用户点击“下载 R 代码”时,系统必须动态注入“本地兼容层”,使得代码可以在任何一台普通的 RStudio 中一键运行。
一、 根本解决方案:导出包装器 (Export Wrapper)
当用户点击“下载 R 代码”时,前端不能仅仅把 Agent 生成的代码原样保存为 .R 文件。我们需要像编译器一样,将代码包裹在一个标准的本地复现模板中。
完整的导出文件应该由 3 个部分拼接而成:
- 数据加载层 (Data Loading)
- 函数兼容层 (Polyfills / Mock Helpers)
- 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;
}
三、 为什么必须这么做?(架构收益)
- 防患于未然的数据读取: 我们不仅注入了 read.csv,还加入了 file.exists 检查。如果医生把 R 脚本发给另一个没有原始数据的统计师,代码会自动生成一组 Mock(模拟)数据。这样 R 脚本无论如何都能跑通,极大提升了产品的专业感。
- 优雅的 Polyfills (兼容层) 技术:
这是前端工程(如适配老版本浏览器)最常用的技术。我们在脚本头部用普通的 print() 和 cat() 重写了 make_table_block_from_df 等函数。这样,大模型生成的复杂 UI 代码在本地 RStudio 中执行时,不仅不会报错,还会把结果整齐地打印在本地控制台上。 - 保持 Agent Prompt 的纯净:
我们不需要去修改 CoderAgent 的 System Prompt 让它“记得写读取文件的代码”。因为每次让 LLM 动态写数据读取逻辑,它很容易因为编码(UTF-8 vs GBK)或文件路径错误而翻车。把这种死板的工作交给前端的字符串拼接(Wrapper),是最稳定、最省 Token 的做法。
四、 实施建议
请前端团队接手此任务:
在 AgentCodePanel.tsx 或 SSACodeModal.tsx 中,找到处理**“导出 R 脚本 (Export/Download .R)”**的 onClick 函数。在生成 Blob 对象并触发浏览器下载之前,将原有的代码字符串通过上述包装器函数处理一遍即可。这只需半小时即可实现。