Files
Genarrative/apps/ai-game-creator-shell/src-tauri/src/debug/debug_drafts.rs
T
k88936 422c8931d6
Project CI / Repository checks (push) Successful in 2m45s
Project CI / Frontend tests (push) Successful in 3m22s
Project CI / Backend tests (push) Successful in 5m53s
Project CI / Native shell tests (push) Successful in 16m45s
Feat/AGC错误报告 (#240)
实现:
在rust内存里维护错误事件队列, webview调用tauri command传入, 后台任务agent工具等直接插入
把rust , webview console的日志统一写到AppData文件夹下的(滚动保存的)日志文件.
rust对传入的错误进行筛选,脱敏, 防抖,后通知前端提醒用户.
用户提醒是一个不阻塞的小UI, 展开后可以选择错误上报, 可以附加文字描述
上传时附带最近日志, 错误堆栈等信息

元数据存在数据库, 考虑到字符串信息很难查询, 所以在api server打包成zip存在OSS.
管理页面新增错误报告的查看页面

![shotmd-1788328504.jpg](/attachments/641b43c8-deed-47d8-8c78-36adb9a2548f)
![shotmd-1788328495.jpg](/attachments/33c6ada3-dd4d-44b6-9859-f18f944eb653)
![shotmd-1788328222.jpg](/attachments/59267df9-f624-4e47-bb9b-b69d77f595c4)
![shotmd-1788328230.jpg](/attachments/63ea6a86-9a51-412b-a98c-dce454bcc968)

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/240
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-03 10:02:05 +08:00

101 lines
3.9 KiB
Rust
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.
//! 调试落盘:保存 LLM 原始输出 / 失败输入,便于排查截断、空返回等问题。
//!
//! 本模块用 `#[cfg(all(debug_assertions, not(test)))]` 声明——仅在开发(debug
//! 且非测试构建中编入二进制;生产 release 与 cargo test 下整个模块与其调用处一并被剔除,
//! 不会往仓库写任何文件。
use crate::unix_millis;
use std::fs;
use std::path::PathBuf;
// 找到仓库根(包含 apps/ai-game-creator-shell/src-tauri/Cargo.toml 的目录),
// 以便把草案落到仓库内而非 tmp 项目目录。
fn repo_root() -> Option<PathBuf> {
let mut roots = Vec::new();
if let Ok(cwd) = std::env::current_dir() {
roots.push(cwd);
}
if let Ok(exe) = std::env::current_exe() {
if let Some(parent) = exe.parent() {
roots.push(parent.to_path_buf());
}
}
for root in roots {
for directory in root.ancestors().take(8) {
let marker = directory
.join("apps")
.join("ai-game-creator-shell")
.join("src-tauri")
.join("Cargo.toml");
if marker.is_file() {
return Some(directory.to_path_buf());
}
}
}
None
}
// 把生成器原始返回(含被截断、解析失败的内容)保存到仓库 .llm-drafts/
// 便于排查 “EOF while parsing a string” 这类输出截断问题。尽力而为,不阻断主流程。
pub(crate) fn persist_snapshot(raw_content: &str) {
let Some(repo_root) = repo_root() else {
app_log!("llm.draft.snapshot.skip: 未能定位仓库根目录");
return;
};
let dir = repo_root
.join("apps")
.join("ai-game-creator-shell")
.join(".llm-drafts");
if let Err(error) = fs::create_dir_all(&dir) {
app_log!("llm.draft.snapshot.dir.failed: {}: {error}", dir.display());
return;
}
let path = dir.join(format!("draft-{}.txt", unix_millis()));
if let Err(error) = fs::write(&path, raw_content) {
app_log!(
"llm.draft.snapshot.write.failed: {}: {error}",
path.display()
);
return;
}
// 同步更新 latest.txt,方便直接打开最近一次草案。
let latest = dir.join("latest.txt");
let _ = fs::write(&latest, raw_content);
app_log!("llm.draft.snapshot.saved: {}", path.display());
}
// LLM 调用失败(例如返回内容为空)时,把本次发送给模型的输入(system + 完整 user prompt
// 连同错误信息一起落盘到 .llm-drafts/,便于按同样输入复现与定位。尽力而为,不阻断主流程。
pub(crate) fn persist_error_input(system_prompt: &str, user_prompt: &str, error: &str) {
let Some(repo_root) = repo_root() else {
app_log!("llm.draft.error-input.skip: 未能定位仓库根目录");
return;
};
let dir = repo_root
.join("apps")
.join("ai-game-creator-shell")
.join(".llm-drafts");
if let Err(io_error) = fs::create_dir_all(&dir) {
app_log!(
"llm.draft.error-input.dir.failed: {}: {io_error}",
dir.display()
);
return;
}
let body = format!(
"# LLM 生成失败输入快照\n\n## 错误\n{error}\n\n## System Prompt\n{system_prompt}\n\n## User Prompt(含用户需求/短长期记忆/spec/agenda/组 brief/findings\n{user_prompt}\n",
);
let path = dir.join(format!("error-input-{}.txt", unix_millis()));
if let Err(io_error) = fs::write(&path, &body) {
app_log!(
"llm.draft.error-input.write.failed: {}: {io_error}",
path.display()
);
return;
}
// 同步更新 latest-error-input.txt,方便直接打开最近一次失败输入。
let latest = dir.join("latest-error-input.txt");
let _ = fs::write(&latest, &body);
app_log!("llm.draft.error-input.saved: {}", path.display());
}