feat(ssa): Agent channel UX optimization (Solution B) + Plan-and-Execute architecture design

SSA Agent channel improvements (12 code files, +931/-203 lines):
- Solution B: left/right separation of concerns (gaze guiding + state mutex + time-travel)
- JWT token refresh mechanism (ensureFreshToken) to fix HTTP 401 during pipeline
- Code truncation fix: LLM maxTokens 4000->8000 + CSS max-height 60vh
- Retry streaming code generation with generateCodeStream()
- R Docker structured errors: 20+ pattern matching + format_agent_error + line extraction
- Prompt iron rules: strict output format in CoderAgent System Prompt
- parseCode robustness: XML/Markdown/inference 3-tier matching + length validation
- consoleOutput type defense: handle both array and scalar from R Docker unboxedJSON
- Agent progress bar sync: derive phase from agentExecution.status
- Export report / view code buttons restored for Agent mode
- ExecutingProgress component: real-time timer + dynamic tips + step pulse animation

Architecture design (3 review reports):
- Plan-and-Execute step-by-step execution architecture approved
- Code accumulation strategy (R Docker stays stateless)
- 5 engineering guardrails: XML tags, AST pre-check, defensive prompts, high-fidelity schema, error classification circuit breaker

Docs: update SSA module status v4.1, system status v6.7, deployment changelist
Made-with: Cursor
This commit is contained in:
2026-03-07 22:32:32 +08:00
parent 87655ea7e6
commit 52989cd03f
18 changed files with 1334 additions and 230 deletions

View File

@@ -192,28 +192,39 @@ function(req) {
message(glue::glue("[ExecuteCode] session={session_id}, code_length={nchar(code)}, timeout={timeout_sec}s"))
# 在隔离环境中执行,预加载 block_helpers 和 data_loader
sandbox_env <- new.env(parent = globalenv())
# 如果有 session_id尝试预设数据路径变量
if (!is.null(session_id) && nchar(session_id) > 0) {
sandbox_env$SESSION_ID <- session_id
}
start_time <- proc.time()
# 捕获输出和结果
collected_warnings <- list()
collected_messages <- character(0)
output_capture <- tryCatch(
withTimeout(
{
# 捕获打印输出
captured_output <- utils::capture.output({
result <- eval(parse(text = code), envir = sandbox_env)
result <- withCallingHandlers(
eval(parse(text = code), envir = sandbox_env),
warning = function(w) {
collected_warnings[[length(collected_warnings) + 1]] <<- w$message
invokeRestart("muffleWarning")
},
message = function(m) {
collected_messages <<- c(collected_messages, conditionMessage(m))
invokeRestart("muffleMessage")
}
)
})
list(
result = result,
output = captured_output,
warnings = collected_warnings,
messages = collected_messages,
error = NULL
)
},
@@ -221,10 +232,14 @@ function(req) {
onTimeout = "error"
),
error = function(e) {
error_info <- format_agent_error(e, code, collected_warnings, collected_messages)
list(
result = NULL,
output = NULL,
error = e$message
warnings = collected_warnings,
messages = collected_messages,
error = error_info$message,
error_detail = error_info
)
}
)
@@ -232,39 +247,54 @@ function(req) {
elapsed_ms <- round((proc.time() - start_time)["elapsed"] * 1000)
if (!is.null(output_capture$error)) {
detail <- output_capture$error_detail
message(glue::glue("[ExecuteCode] ERROR after {elapsed_ms}ms: {output_capture$error}"))
console_lines <- c(
output_capture$output,
if (length(output_capture$warnings) > 0) paste0("Warning: ", output_capture$warnings),
if (length(output_capture$messages) > 0) output_capture$messages
)
return(list(
status = "error",
error_code = "E_EXEC",
error_code = if (!is.null(detail)) detail$error_code else "E_EXEC",
error_type = if (!is.null(detail)) detail$error_type else "runtime",
message = output_capture$error,
user_hint = paste0("R 代码执行出错 (", elapsed_ms, "ms): ", output_capture$error),
user_hint = if (!is.null(detail)) detail$user_hint else output_capture$error,
error_line = if (!is.null(detail)) detail$error_line else NULL,
error_context = if (!is.null(detail)) detail$error_context else NULL,
console_output = console_lines,
duration_ms = elapsed_ms
))
}
message(glue::glue("[ExecuteCode] SUCCESS in {elapsed_ms}ms"))
# 将结果标准化
final_result <- output_capture$result
# 如果结果是 list 且包含 report_blocks直接返回
console_lines <- c(
output_capture$output,
if (length(output_capture$warnings) > 0) paste0("Warning: ", output_capture$warnings),
if (length(output_capture$messages) > 0) output_capture$messages
)
if (is.list(final_result) && !is.null(final_result$report_blocks)) {
return(list(
status = "success",
result = final_result,
console_output = output_capture$output,
console_output = console_lines,
duration_ms = elapsed_ms
))
}
# 否则包装为通用结果
return(list(
status = "success",
result = list(
data = final_result,
report_blocks = list()
),
console_output = output_capture$output,
console_output = console_lines,
duration_ms = elapsed_ms
))