更新日志
开发日志
2026-08-28 · 发芽启动器 v1.1.0 — 僵尸代码清理与功能接入
改动文件:D:\AI\P\fy\main.py(17811 行 → 17131 行,净减 680 行)
一、接上的功能(原「代码完整、只差入口」)
- 多账号管理入口
- 设置页「离线玩家名 → 保存」按钮后新增
👥 多账号管理按钮,打开_acct_show_dialog弹窗(添加 / 切换 / 删除离线账号)。 - 同时删除多账号的第二套重复定义(L9351 区):它与第一套同名且覆盖后者,导致弹窗里
self._acct_add()命中需要name参数的版本而抛 TypeError。保留 L9141 完整版(含弹窗输入与列表 UI 重建),该隐患随删除消除。
- 微软正版皮肤上传
- 皮肤页「📦 安装 CSL」后新增
⬆ 上传到正版账号按钮 →_ms_change_skin→_ms_upload_skin(Mojang API PUT multipart)。 - 未登录正版账号时内部已有提示,不会出错。
- 首次运行欢迎引导(后台自动)
__init__中self.after(1200, self._show_first_setup),仅在launcher_config.json不存在时弹窗,老用户静默。
- AI 助手首用引导(后台自动)
- AI 页构建末尾调用
_check_first_run(),ai_config.json不存在时给出模型源 / Key 配置提示。
- 加载器自修复(后台自动)
- 启动流程
_launch_flow中,在load_version_json之前调用_repair_loader_shell+_rebuild_forge_shell,fabric / quilt / forge 版本 json 缺加载器参数时自动从官方 meta 源重建(顺序不能颠倒,否则读到的是损坏 json)。
二、删除的代码(共 696 行)
| 类别 | 内容 |
|---|---|
| 拖放安装(已证实不可用) | _enable_drag_drop(WndProc 子类化钩子,70 行)、_on_drop_files、_poll_queue 里 _drop_pending 投递块、__init__ 的初始化与提示日志 |
| 历史兼容壳(对齐 0.2.11 命名) | 15 个纯转发方法:_net_e4mc_worker _net_srv_worker _net_srv_java _net_srv_copy _net_room_detect_servers _net_room_mods_list _on_net_room _on_net_ts _on_ai_test_result _make_default_skin_png _skins_ensure_defaults _skins_set_active _skins_refresh_lib _ts_copy_ip _ts_down _net_room_check_dialog(转发目标均存活,零风险) |
| 重复实现(有活替代品) | _ai_test_conn + _ai_test_conn_worker、_gen_error_report、_ai_open_skills、_ai_clear_chat、_ai_open_memory、_ai_show_signup、_save_mem_mode、_nav_hover、_ai_net_task、_hall_post/_hall_fetch、_net_room_stop、_tc_room_code_display、_tc_backend、_osx_only_lib、_save_client_id、_show_cid_help、_make_readonly_text、_ai_is_rate_limited、多账号第二套定义 |
| 开发者模式旧入口 | _dev_lock_click(「关于页标题连点 5 次」版本,从未绑定控件) |
| 死类 | CardView(约 144 行,全项目从未实例化) |
保留说明:拖放的识别/安装核心
_drop_identify、_on_drop_pack、_drop_install_legacy、_drop_install_modrinth是「导入整合包」按钮的共用链路,已保留,导入功能不受影响。开发者模式现唯一入口为关于页标题鼠标中键(_dev_unlock_dialog)。
三、验证结果
- 语法编译:通过
- 方法清单差分对比(删除前 604 项 → 删除后 550 项):新增 0 项,消失项与计划完全一致,无误删
- 冒烟测试
test_smoke.py:143 通过 / 0 失败
四、遗留备注
- 冒烟测试「CF 翻页」项存在偶发失败:同一份代码连续运行 4 次,首次报 142/1、后 3 次 143/0。与本次改动无关(改动前基线也是 143/0),推测是
_MR_CACHE缓存状态在用例间共享导致,建议后续单独排查。 - 开发者模式入口目前是隐藏的(关于页标题鼠标中键),如需显式入口可在设置页加按钮。
- 项目根目录仍有大量开发期残留脚本(
recon*.py、main_rebuild.py、main_bad/main_recovered/main_restored.py、ui_refine.py、kanban-work/等),非产品代码,可另行清理。
2026-08-28(2)· 修复 Forge 1.20.1 启动崩溃(线上事故)
事故现象:下载 1.20.1 Forge 版本(版本目录 D:\发芽启动器\versions\AI测试)后无法启动。
一、已修复:启动器自身缺陷(根因 1)
问题:MinecraftLauncher._resolve_libraries() 对继承型版本(整合包/Forge)会沿继承链把原版 jar 加入 classpath,最终混入 versions\1.20.1\1.20.1.jar。
后果:Forge 1.20.1 的 Minecraft 类实际由 libraries\net\minecraftforge\forge\1.20.1-47.0.50\forge-1.20.1-47.0.50-client.jar 提供(FML 的 MinecraftLocator 将其加载为 minecraft 模块)。原版 jar 同时存在时会被 ModLauncher 转成模块 _1._20._1,两个模块导出同名包,启动即中止:
java.lang.module.ResolutionException: Modules _1._20._1 and minecraft
export package net.minecraft.util.profiling.jfr.event to module customskinloader
补充:Forge json 的
-DignoreList=…,AI测试.jar本意是跳过版本自身 jar,但启动器实际混入的是继承来的1.20.1.jar,名字对不上导致 ignoreList 失效。
修复:_resolve_libraries() 中,当 mainClass 含 bootstraplauncher(Forge 47+)且存在对应的 forge-<mc>-<ver>-client.jar 时,跳过原版 jar(Forge client jar 由 FML 自行加载)。client jar 不存在时仍走原逻辑兜底,不影响其他版本。
验证:classpath 已不含 1.20.1.jar;移走 mods 后游戏正常启动(资源/纹理/声音引擎全部就绪)。冒烟测试 143 通过 / 0 失败。
二、未修复(非启动器缺陷,需决策)
- CustomSkinLoader 与 Forge 1.20.1 不兼容
- 启动器为「离线显示皮肤」自动安装的
CustomSkinLoader_Universal-15.0.1.jar,其运行时释放的CustomSkinLoader-Common.jar内含net/minecraft/client/renderer/IImageBuffer.class,被转成customskinloader模块后导出net.minecraft.client.renderer,与minecraft模块冲突。 - 已验证:单独保留 CSL、移走匠魂仍必崩,即 CSL 自身即可搞崩 Forge 1.20.1。
- 已验证无效的规避手段:把
customskinloader追加进-DignoreList(Common jar 为 CSL 运行时释放,不受启动时 ignoreList 控制)。
- 匠魂缺少前置模组
TConstruct-1.20.1-3.11.2.166.jar依赖mantle(要求>=1.11.97),当前 mods 目录未安装,Forge 会因强制依赖缺失拒绝启动。
三、建议后续动作
- CSL:启动器在 Forge 1.20.1+ 场景应不再自动安装 CSL,并在启动前检测已装 CSL 时提示/自动禁用(待确认方案后再实施)。
- 模组依赖:可增加启动前依赖检测,提示缺失的强制前置并提供一键补装。
2026-08-28(3)· CSL 与 Forge 47+ 冲突防护(落实上节遗留方案)
改动文件:D:\AI\P\fy\main.py
一、改动内容
- 新增
_is_forge_module_launch(vi, data=None)
- 判断版本是否为 Forge 47+ 模块化启动(
mainClass含bootstraplauncher)。 - 该场景下 Minecraft 类由
forge-<mc>-<ver>-client.jar提供,CSL 与minecraft模块导出同名包net.minecraft.client.renderer,混用必崩(对应上节根因 2 的 CSL 部分)。 - 可复用
_launch_flow已加载的data,避免重复读 json。
_skins_ensure_csl(gd, vi=None)增加 Forge 47+ 守卫
- 自动安装 CSL 前先判定
_is_forge_module_launch,命中则直接return False跳过自动安装。 - 仅拦截「皮肤同步触发的后台自动安装」;用户手动点「📦 安装 CSL」仍可强制安装,不拦截。
- 新增
_forge_csl_guard(vi)(启动前自动禁用已装 CSL)
- 在
_launch_flow皮肤同步前调用;命中 Forge 47+ 时扫描游戏目录mods/,把customskinloader*.jar改名<name>.jar.disabled(可手动恢复),并写日志warn+ toast 提示。 - 解决「CSL 自身即可搞崩 Forge 1.20.1」的遗留问题(对应上节根因 2)。
_launch_flow插入防护调用
- 皮肤同步(3.6)前新增 3.5 步
self._forge_csl_guard(vi)。
二、验证
- 语法编译:
python -m py_compile main.py通过 - IDE 静态检查:0 错误 / 0 警告
三、遗留
- 模组强制依赖检测(如匠魂缺
mantle)仍未实施,留待后续。
2026-08-28(4)· 错误报告正则诊断(AI 更好分析 / 玩家更易看懂)
需求:错误报告加正则匹配,让 AI 分析更简单,玩家页更容易看懂。改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、新增:崩溃诊断规则库(模块级,位于 version_has_loader 之后)
ERROR_PATTERNS(16 类,按顺序匹配,越具体越靠前)每类含 name(机器标识)/ title(玩家可读标题)/ pat(正则)/ cause(通俗原因)/ fix(可执行建议):
| name | 玩家看到的标题 | 典型命中 |
|---|---|---|
module_conflict |
模组模块冲突(Forge 模块化启动) | java.lang.module.ResolutionException、Modules X and Y export package |
mod_missing_dep |
模组缺少前置依赖 | Missing or unsupported mandatory dependencies、Mod X requires Y |
mod_duplicate |
模组重复安装 | DuplicateModsFoundException、Found duplicate mod |
mod_load_fail |
模组加载失败 | ModLoadingException、Failed to load mod |
mixin_fail |
模组注入失败(Mixin 错误) | MixinApplyError、Mixin apply for ... failed |
java_version |
Java 版本不匹配 | UnsupportedClassVersionError、class file version 61.0 |
jvm_crash |
Java 虚拟机崩溃 | EXCEPTION_ACCESS_VIOLATION、SIGSEGV、hs_err_pid |
out_of_memory |
内存不足 | OutOfMemoryError、Could not reserve enough space for object heap |
opengl |
显卡 / OpenGL 错误 | GLException、Pixel format not accelerated、GLFW error |
natives_missing |
本地库(natives)缺失 | no xxx in java.library.path、Failed to locate library |
java_not_found |
Java 未找到或无法启动 | CreateProcess error=2、Could not find or load main class |
auth_fail |
登录验证失败 | Invalid session、ForbiddenOperationException |
download_fail |
游戏文件下载 / 校验失败 | Failed to download、SHA-1 mismatch |
mod_incompatible |
模组与游戏版本不匹配 | NoSuchMethodError、NoClassDefFoundError |
port_in_use |
端口被占用(联机失败) | Address already in use |
runtime_error |
游戏内部错误(空指针等) | NullPointerException 等 |
配套三个函数:
extract_error_evidence(log_text, pat)— 抽关键日志:命中处上下文窗口(前 2 后 14)+Caused by根因链(≤6 条),去重、单行截 300 字符、上限 24 行;无命中时退化为「尾部含错误关键词的行」,过滤掉 INFO 噪声。diagnose_crash(log_text, exit_code)— 返回{name,title,cause,fix,match,evidence};未命中时name='unknown'并按退出码给兜底标题(1=启动失败、-1=被强制结束、0=异常退出、其他=带退出码)。format_diagnosis(diag)— 格式化为纯文本段(类型 / 通俗原因 / 建议操作 / 关键日志片段),弹窗、报告正文、AI 输入三处复用。
统一忽略大小写:规则定义后统一
re.compile(pat, re.I)重编译。实测 MC / Forge 日志大小写不固定(如missing or unsupported mandatory dependencies),不加re.I会漏判。
二、接入点(4 处)
_show_error_report(玩家页弹窗)
- 标题由「游戏异常退出(退出码 1)」改为分类标题(如「模组缺少前置依赖」);
- 顶部两行新增:🔍 通俗原因(灰色)、🛠 建议操作(绿色),自动换行;
- 正文由「原始日志尾部 4000 字符」改为
format_diagnosis(结构化摘要 + 去噪关键片段); - 窗口 560x360 → 620x500;原始提示(含退出码)降级显示在版本/日志信息区。
_build_error_report_content(导出报告正文)
- 在【AI 诊断】之前新增【错误分类】(启动器自动识别)整段,
_last_diag缺失时回读game-latest.log重新诊断。
_ai_auto_analyze(AI 请求)
- user 消息在日志前插入三行结构化摘要(启动器自动识别 / 可能原因 / 建议操作);
- 日志正文改为「去噪关键片段 +
--- 原始日志片段 ---+ 原文前 4000 字符」,上限 8000(原 5000); - 提示语改为「请结合上面的自动识别结果分析日志」。
game_exit分支(崩溃触发)
- 读
game-latest.log→diagnose_crash(log, code)→ 结果缓存到self._last_diag,供弹窗 / 导出 / AI 三处复用同一份结论; - 弹窗标题、AI 分析标题、崩溃历史
error_type统一用分类标题; - 崩溃历史新增
error_kind(机器标识,便于日后统计)与exit_code字段(详情面板为 JSON 全量展示,无需改 UI)。
三、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 规则库自测(临时脚本,已删除):9 条典型日志用例(模块冲突 / 缺依赖 / Java 版本 / 内存 / OpenGL / 模组重复 / Mixin / natives / 无匹配)全部命中预期类别,退出码兜底 4 种(0 / 1 / -1 / 3221225477)标题正确。
- 过程中发现并修复:规则未加
re.I导致「缺少前置依赖」被「模组加载失败」抢先命中(顺序在前但大小写不匹配),已统一重编译为忽略大小写。
- 过程中发现并修复:规则未加
四、备注
- 规则表是数据驱动的,后续新增错误类型只需往
ERROR_PATTERNS追加一条,无需改其他代码。 - 匹配采用「首个命中」而非打分,因此新增规则时要注意顺序:更具体的错误必须排在泛化规则(如
mod_load_fail、runtime_error)之前。
2026-08-28(5)· AI 自动修复新增 fix_mixin 专用工具(解决"没有 mixin 工具")
现象:用 AI 助手修复「模组注入失败」(Mixin 错误)的版本时,模型回复"没有 mixin 工具"——AGENT_TOOLS 工具列表里确实没有任何 Mixin 相关工具,弱模型只会找名字带 "mixin" 的工具,无法完成修复。改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、新增工具 fix_mixin
-
工具定义(
AGENT_TOOLS):位于get_mods之后。描述明确指引:"遇到 MixinApplyError / Mixin apply failed / Mixin transformation of ... failed / InvalidMixinException 等注入类错误时务必用此工具,不要手动猜测模组"。参数:log(必填,崩溃日志/错误报告文本)、version(可选,版本 ID,默认当前选中版本)。 -
实现
_tool_fix_mixin(args)(模块级,位于_tool_get_mods之后):
- 解析:正则提取
Mixin apply for mod X failed、from mod X(InvalidMixinException 格式)命中的模组 id(去重、清理特殊字符);另提取Mixin transformation of <类> failed的目标类作为参考信息。 - 定位 mods 目录:优先用版本 json 的
game_dir/mc_dir(经_APP_REF.versions),兜底LAUNCHER_DIR\versions\<vid>\game\mods与\mods;vid 为空时用当前选中版本。 - 匹配并禁用:按模组 id 与 jar 文件名做忽略大小写 / 忽略连字符匹配,把命中 jar 重命名为
.disabled;同一文件去重,避免重复禁用。 - 返回:禁用成功的文件列表、点名但未匹配的模组 id、Mixin 注入目标类、mods 目录路径;完全无法解析时给出明确提示(不是让模型自己猜)。
- 注册:
AGENT_TOOL_IMPL加"fix_mixin": _tool_fix_mixin;AGENT_TOOL_LEVEL加"fix_mixin": 1(高级权限,会修改 mods 目录;普通模式弹窗确认,高级模式全自动)。
二、_ai_auto_fix 提示词引导(错误报告弹窗的「AI 自动修复」)
- system prompt 追加:"遇到 Mixin 注入失败(MixinApplyError / Mixin apply failed / Mixin transformation of ... failed / InvalidMixinException)时,必须调用 fix_mixin 工具(log 传崩溃报告文本,version 传版本 ID),由它自动定位并禁用问题模组,不要自行猜测模组。"
- user 消息开头带上当前版本 ID:
当前修复的版本 ID:<vid>,让模型调用 fix_mixin 时能填对 version 参数。 _on_ai_fix_confirm调用_ai_auto_fix时补传vid=self.selected or ""。
普通 AI 对话(
_ai_agent_run)与子代理同样加载AGENT_TOOLS,因此用户直接说"修复模组注入失败"时模型也能看到并调用 fix_mixin,无需改其他链路。
三、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 _tool_fix_mixin自测(临时脚本,已删除;在D:\发芽启动器\versions\__mixin_test__造真实目录与假 jar):- 命中
Mixin apply for mod create failed→ 正确匹配create-0.5.1-fabric.jar并禁用(改名 .disabled)✓ - 点名
unknownmod(mods 目录无该 jar)→ 明确报告未匹配 + 给出 mods 目录 ✓ - 非 Mixin 日志(OutOfMemoryError)→ 提示"未能从日志解析出 Mixin 相关模组",不会误禁用 ✓
- 缺 log 参数 → 提示缺参 ✓
- 重复点名同一模组 → 去重,只禁用一次 ✓
- 命中
四、备注
- 工具按高级权限处理是刻意的:禁用模组会改动玩家 mods 目录,普通模式必须弹窗确认。
- 若日志点名的 mod id 与 jar 文件名差异过大(极端情况)会落入「未匹配」分支并提示 mods 目录,AI 可结合
get_mods/rename_file手动兜底。
2026-08-28(6)· 前置模组补全:新增一键按钮 + 修复下载后自动补装
反馈:① 版本管理页找不到「一键补全前置模组」按钮;② 下载新模组时不会自动下载前置模组。改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、新增依赖解析基础方法(_mr_deps_of_version 之前)
| 方法 | 作用 |
|---|---|
_mr_sha1_of_file(path) |
计算文件 sha1,供 Modrinth version_file 反查模组身份 |
_mr_resolve_dep_file(pid, version_id, mc, loader) |
把依赖项目解析成可下载文件 {project_id, slug, title, url, filename, sha1, size};锁定版本时直接取,否则按 (MC 版本, 加载器) 取最新兼容版;只取 primary 文件(避开 javadoc / sources) |
_mr_deps_by_sha1(sha1, mc, loader) |
用 /version_file/<sha1>?algorithm=sha1 反查该文件的 required 依赖。与下载来源无关:从 CurseForge 下载的模组只要文件被 Modrinth 收录,同样能查出前置 |
_mr_wait_file_ready(dest, timeout=90) |
等下载线程把主文件写完整(大小连续两次稳定且非空),避免并发算出半截 sha1 |
_mr_deps_of_version 同步重构为复用 _mr_resolve_dep_file(去重 + 支持环境筛选)。
二、修复「下载后自动补装前置」(_mr_auto_deps_after_download)
原实现三个问题,逐一修复:
- 只认 Modrinth version id → 从 CurseForge 来源下载的模组,其 id 查 Modrinth
/version/<id>必然 404,依赖查不到。修复:改为优先用下载完成后的文件 sha1 反查,反查不到再退回 version_id 查询。对两种来源都有效。 - 与下载线程并发:原实现与
_mr_download_worker同时 start,补装时主文件可能还没落盘。修复:先用_mr_wait_file_ready等待落盘完成。 - 全程静默:成功只写日志,用户感知不到,会以为功能没生效。修复:补装成功给 toast「✅ 已自动补装 N 个前置模组:…」,失败也给提示并引导点「补全前置模组」重试。
三、新增「一键补全前置模组」
- 入口 1:版本管理页按钮网格新增
🔗 补全前置模组(第 8 项,网格 3 列 → 4 行;无模组加载器时随mods/mods_folder一起隐藏)。 - 入口 2:模组管理页搜索行,紧邻「🔍 校验兼容性」右侧。
- 实现
_mods_deps_complete()→ 后台线程_mods_deps_worker(vi, jars):- 逐个对已启用 jar 算 sha1 → 反查 Modrinth 项目 id,收集各自的 required 依赖;
- BFS 展开依赖(前置的前置也补,深度上限 3、总数上限 20,防失控);
- 每个依赖先按 (MC 版本, 加载器) 解析出文件,再按 project_id 与文件名双重判重,已装则跳过;
- 汇总弹窗:已补装 / 已存在无需安装 / 未成功 / 无法识别(未被 Modrinth 收录)/ 前置齐全。
- 新增队列消息
mods_deps与_on_mods_deps_msg(主线程弹窗),照mods_compat的既有模式接入。
四、过程中发现并修复的真实缺陷
_mr_resolve_dep_file 按环境过滤查询时,参数直接拼成 ?game_versions=["1.18.2"]&loaders=["forge"]——方括号与双引号未编码,Modrinth 返回 HTTP 400,异常被 except 吞掉后静默返回 None,导致「依赖未锁版本时」这条路径 100% 失效。修复:改用 urllib.parse.urlencode 编码;并加兜底——过滤结果为空时退回不过滤取最新版本,避免筛选过严而完全失败。
五、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;真实 API + mock 网络混合):
- sha1 计算正确 ✓
_mr_resolve_dep_file(Flywheel + 1.18.2/forge)→ 命中flywheel-forge-1.18.2-0.6.8.a.jar,且正确排除 javadoc/sources ✓(此项修复前返回 None)_mr_deps_by_sha1(create forge 1.18.2 样本 sha1)→ 反查出 Flywheel ✓_mods_deps_worker完整流程(mock 网络与下载)→ 正确补装缺失前置、文件落到 mods 目录、汇总文本正确 ✓- 二次运行(前置齐全)→ 不重复下载、提示「🎉 前置齐全,无需补装」✓
六、遗留
- 依赖识别依赖 Modrinth 收录:极少数仅发布于 CurseForge 的模组无法识别(汇总里会归入「无法识别」并提示手动确认)。
- 启动前的强制依赖检测(
mod_missing_dep类崩溃 → 自动提示补装)仍未做,可复用本节方法实现。
2026-08-28(7)· 补全前置模组的下载进度接入「下载管理」页
需求:一键补全前置模组时,能在下载管理页看到每个前置的下载进度。
改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、机制说明(既有基础设施)
下载管理页(dlmgr)渲染 _dl_tasks(tid → {name, text, pct}),由主线程方法 _dl_task_start / _dl_task_update / _dl_task_done 维护。模组下载走的 mr_progress_show/hide 消息还附带模组下载页的内联进度条(_mr_show_progress),对「补全前置」不适用(用户不在下载页,且 frame 不属于模组管理页)。
二、实现
- 新增
_mr_dep_progress_cb(tid, name)(_mr_auto_deps_after_download之前)
_simple_download进度回调适配器:接收(done_bytes, total_bytes),节流 0.25s,换算成「🔗<文件名>:x.x / x.x MB | xx%」+ pct,发dl_dep_progress队列消息。
- 新增 3 个队列消息分支(
mr_progress_show之后,只操作下载管理任务、不碰内联进度条):
dl_dep_start(tid, name)→_dl_task_startdl_dep_progress(tid, pct, text)→_dl_task_updatedl_dep_done(tid, final_text)→_dl_task_update(100%, final)+_dl_task_done(移入下载管理历史)
- 两条补装链路都接入:
- 一键补全(
_mods_deps_worker):每个前置下载前tid = self._mr_new_tid()+ 发dl_dep_start(名称「🔗 补装前置:<文件名>」),下载带progress_cb与known_size(省一次探测),结束发dl_dep_done(✅ 已就位 / ❌ 下载失败)。 - 下载页自动补装(
_mr_auto_deps_after_download):同样处理,名称「🔗 自动补装前置:<文件名>」。
- tid 唯一性:复用
_mr_new_tid()(mr:前缀自增,预登记_dl_stats),与模组下载共用序号,互不冲突。
三、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;mock 网络 + 模拟下载回调):
- 完整流程产生
dl_dep_start → dl_dep_progress×3 → dl_dep_done,tid 唯一同源 ✓ - 进度数值递增(0% → 50% → 100%),文字含文件名与百分比 ✓
- 回调节流生效(0.25s 内连续调用只发 1 条)✓
- 下载失败同样发
dl_dep_done且文案为失败 ✓
- 完整流程产生
四、备注
- 刻意不走
mr_progress_show/hide:其附带的内联进度条只属于模组下载页,补全前置场景下会错位显示,故新增独立消息类型,只刷新下载管理任务列表与历史。 - 下载完成的任务会进入下载管理「历史记录」(保留 30 条),失败也能看到留痕。
2026-08-28(8)· 修复「默认(内置 Key)」预设仍显示 API Key 栏且无法编辑
反馈:默认模型页面不应该有 API Key 栏,但显示了;里面有无效 key,且无法编辑/删除。
一、根因
原 _ai_apply_preset 对内置预设的处理是「填入占位文字 + 设为 readonly」:
self._ai_key_entry.insert(0, "(内置 Key,无需填写)")
self._ai_key_entry.configure(state="readonly")
Key 输入框带 show="●",占位文字被遮成一串黑点——视觉上就是一个「已存在的 Key」,而且 readonly 导致无法编辑、无法删除。这正是用户描述的「里面有无效 key,还无法编辑删除 key」。
连带 3 个问题:
- 占位文字被落盘:
_ai_save_cfg无条件cfg["key"] = self._ai_key_entry.get().strip(),切换/保存内置预设时把占位文字(或残留的旧自填 key)写进ai_config.json,下次启动又被插回输入框。 pack_forget()后pack()打乱布局:_ai_custom_row(Base URL 行)在「自定义模型」预设下恢复时,没有before锚点,会被追加到卡片末尾(跑到 MCP/技能按钮行之后)。- 内置预设无法测连接:
_ai_test_from_settings直接读 Key 框(内置时为空),会误报「请先填写 Base URL 与 API Key」。
二、修复(仅 main.py)
- 内置预设整行隐藏 API Key(用户诉求)
- 设置页把 Key 行 / 模型名行 / 按钮行分别存为
self._ai_key_row、self._ai_model_row、self._ai_row3; _ai_apply_preset:内置预设 → 清空 Key 框 +self._ai_key_row.pack_forget();其它预设 → 带锚点恢复pack(fill="x", pady=3, before=self._ai_model_row)。
- 布局顺序修复
_ai_custom_row恢复时改用pack(fill="x", pady=3, before=self._ai_row3),不再追加到末尾。
- 占位文字不再落盘
_ai_save_cfg:内置预设(或值为占位文字)时_pk = "",cfg["key"]与keys[预设名]都不写入;非内置预设照常落盘并绑定供应商。
- 启动清理脏数据
- 设置页恢复配置时,若
cfg["key"]是旧版写入的占位文字,直接清空并回写cfg。
- 测试连接支持内置预设
_ai_test_from_settings:内置预设用preset["key"]测试,不再误报未填 Key。
三、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;用桩件替代 tk 组件,验证纯逻辑)18 项全过:
- 内置预设:Key 行隐藏 ✓ / Key 内容清空(清掉残留旧 key)✓ / Base URL 行隐藏 ✓ / 回填内置模型名 ✓
- 落盘:内置预设不落盘占位文字 ✓ / 不绑 keys ✓;DeepSeek 正常落盘并绑定 ✓
- 切换:内置 → DeepSeek → 内置,Key 行正确隐藏/恢复,不残留上一家 key ✓
- 布局:Key 行恢复锚定在模型行之前 ✓ / 自定义 Base URL 行锚定在按钮行之前 ✓
- 落盘文件无占位文字 ✓
四、备注
- 内置预设隐藏 Key 行后,用户切到「千问 / DeepSeek / 智谱」等任意自带 Base URL 的预设即可恢复 Key 行并填自己的 Key;选「自定义模型」会同时恢复 Key 行与 Base URL 行。
2026-08-28(9)· AI 助手上下文窗口:按模型自适应 + 75% 水位自动压缩
需求:① 上下文窗口长度根据模型自行调整;② 历史会话压缩;③ 上下文达到窗口 75% 时自动压缩。
改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、模型上下文窗口表(AI_CTX_WINDOWS)
补全常见模型窗口(按模型名子串匹配,越靠前优先;未收录默认 128K):
| 模型 | 窗口 |
|---|---|
| deepseek | 1M |
| qwen | 256K |
| gemini / bard / gemma | 1M |
| claude / command | 200K |
| glm / chatglm / gpt- / o1 / o3 / grok / kimi / moonshot / sense / hunyuan / doubao / ernie / wenxin / spark / xfyun / xinghuo / llama / internlm / yi- / mistral 家族 | 128K |
_ai_ctx_window(model) 根据当前模型名自动返回对应窗口——即「长度根据模型自行调整」。
二、新增 token 占用统计(模块级)
_ai_messages_tokens(messages):累加每条消息的估算 token(_est_tokens+ 每条 16 token 固定开销;content 为数组/对象时按序列化文本估算,兼容多模态/工具结果)。_ai_ctx_usage(messages, model):返回(已用 tokens, 窗口大小, 占用比例 0~1)。
三、75% 水位自动压缩(_ai_maybe_compress 改造)
原实现按「条数 ≥40」触发,与上下文窗口无关。改造为按 token 水位:
- 计算
system(内置提示词) + 全部历史的估算 token; ratio >= 75%(窗口的 75%)→ 触发压缩;或条数 ≥60 兜底触发(小窗口模型条数涨得快,固定开销也占窗口);- 至少 20 条历史才压缩(保留最近 10 条原文 + ≥10 条早期可压),避免无谓的 AI 摘要调用;
- 触发后后台
_ai_compress_worker把早期对话压成中文摘要(保留用户偏好/决策/进度/关键事实/待办),历史替换为[摘要, ...最近10条原文]; - 返回是否已触发,供调用方判断;
_ai_compressing防止重复触发。
四、发送前检查(_ai_ask)
组装请求前调用 _ai_maybe_compress()——历史水位达到 75% 时在发送前启动后台压缩,而不是等回复后才压缩。压缩完成前本次请求由 _ai_trim_history(60% 预算硬截断)兜底,保证永不超窗。回复后 _on_ai_reply 仍会再检查一次,为下一轮准备。
五、阈值对齐
_ai_maybe_compress触发要求历史 ≥20 条;_ai_compress_worker的早期消息下限从 20 调整为 10(与新触发条件匹配,避免触发后 worker 又因 old 太少直接返回而无限重复触发)。
六、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除)25 项全过:
- 12 组模型名 → 窗口识别全部正确(含新增 gemini/llama/mistral/spark 等)✓
_ai_ctx_usage计算正确,content 数组也计入 ✓- 历史 <20 条不触发 ✓;小窗口 + 长历史水位 >75% 触发压缩且启动后台线程 ✓;压缩中不重复触发 ✓
- 条数 ≥60 兜底触发 ✓;水位低且条数未超不触发 ✓
七、备注
- 压缩是「AI 摘要式」(保留最近 10 条原文 + 早期摘要),不是简单丢弃;摘要会随下次请求进入上下文,可支撑接力续聊。
- 75% 阈值隐含为输出/工具结果预留 25% 空间;硬截断(60%)作为压缩完成前的安全网,两层保证不超窗。
2026-08-28(10)· 上下文水位指示:发送按钮旁的空心圆
需求:把上下文水位以空心圆 ⚪ 形式做到 AI 助手发送按钮旁边。
改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、实现
- 空心圆指示器(AI 页输入行,发送按钮与语音按钮之间)
tk.Canvas30×30 绘制:灰色整圈(空心圆外观)+ 彩色水位弧(从 12 点方向顺时针,弧长 =system+历史估算 token / 模型窗口)+ 中心百分比数字。- 颜色分区:<50% 绿
#7BC47F、50–75% 灰(COL_SUB)、75–90% 黄#E0A83A(即将压缩)、≥90% 红#E05A5A。 - 百分比与弧长在超窗时封顶 100%(自测中发现并修复:超窗曾显示 291%)。
-
_ai_update_ctx_ring():计算当前水位(_ai_ctx_usage(system+历史, 模型))并重绘;模型名优先取设置页输入框,未构建时回退ai_config.json的 model。缓存_ai_ctx_ratio / _ai_ctx_used / _ai_ctx_window_size供提示用。 -
悬停详情(
_ai_ctx_tip):鼠标悬停显示 Toplevel 提示——「上下文水位:xx% / 约 x.xK / 128.0K tokens(模型窗口)/ 达到 75% 自动压缩历史对话」。 -
新增
_fmt_tokens():token 数人性化(1234567 → 1.2M、12345 → 12.3K)。 -
刷新时机:AI 回复后、历史压缩完成后、新建会话后、加载历史会话后,以及 AI 页构建完成时(初始 0%)。
二、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;Canvas 用桩件记录绘制调用)16 项全过:
_fmt_tokens边界(0 / 999 / 12.3K / 1.2M / None)✓- 空历史:system 占用被如实反映(极小水位弧)、显示 0% ✓
- 低水位弧长与比例精确一致、颜色正确 ✓
- 中水位黄 / 超 90% 红 / 超窗时百分比与弧长封顶 100% ✓(过程中发现并修复超窗显示 291% 的小缺陷)
三、备注
- 指示器与 75% 自动压缩共用同一套水位计算(
_ai_ctx_usage),显示值与实际触发阈值一致;黄色阶段即「接近压缩阈值」的预告。
2026-08-28(11)· 修复「内置 Key 不生效」(key 文件注释污染)
现象:选择「默认(内置 Key)」预设对话报 401,内置 Key 完全不可用。
一、根因(两层叠加)
-
key 文件注释污染(主因):千问内置 key 文件
mm_eer的内容是「3 行#使用说明注释 + 1 行逗号分隔的 3 个 key」,而注入逻辑直接read().strip()把整坨文字当成 key:QWEN_BUILTIN_KEY = "# 千问(阿里百炼)内置 API Key —— 打包时注入…\nsk-ws-…,…"
于是默认预设(优先千问)变成「千问 base + 注释污染的 key」→ Authorization: Bearer # 千问(阿里百炼)… → 401。
2. 兜底换 key 不换 base(次因):_ai_chat_once 发现 key 含非 ASCII(中文注释)会换成 AI_BUILTIN_KEY(智谱 key),但 base_url 仍是千问的 → 智谱 key 调千问 API → 照样 401。兜底反而掩盖了真正病因。
二、修复(仅 main.py)
新增模块级 _read_key_file(path):读 key 文件时跳过 # 注释行与空行,剩余行拼接为逗号分隔的 key 串(多 key 轮询语义不变)。GLM(ai_key.txt)与千问(mm_eer)两处注入统一改用它——今后 key 文件里写使用说明不会再污染 key。
三、验证
- 语法编译通过;IDE 静态检查 0 错误。
- 端到端实测(临时诊断脚本,已删除;仅打印 key 前 8 位不泄露):
- 修复后
QWEN_BUILTIN_KEY= 351 字符干净 3-key 串(headsk-ws-H.,不再是注释文字)✓ - 默认预设 = 千问 base + qwen-turbo + 干净 key ✓
- 真实调用 API 返回 "OK",内置 Key 生效 ✓
- 修复后
- 附带发现:用户
ai_config.json里残留旧版占位文字 key(13 字符),启动加载时的清理逻辑(见 2026-08-28(8)节)会自动清空,无需手动处理。
四、备注
- key 文件今后仍可写
#注释说明(方便维护),解析层已免疫;但key 本身不要带引号/空格。 _ai_chat_once的「非 ASCII key → 换内置 key」兜底保留(防御占位文字类脏数据),但根治在源头解析。
2026-08-29(1)· 模组管理:前置下载提速 + 进度可见 + 筛选按钮 + 更新检查提速
反馈:① 前置下载太慢且看不到进度;② 从版本管理页直接点「补全前置模组」误报"该版本没有模组 jar 文件",必须先打开模组管理页;③ 模组列表上方需要 全部/禁用/启用/可更新 四个筛选按钮;④ 优化模组更新检查速度。
改动文件:D:\AI\P\fy\main.py(仅 main.py)
一、修复「版本管理页直接补全前置」误报(Bug)
根因:_mods_deps_complete 读的是 self._mods_entries,而该列表只在打开模组管理页时由 _refresh_mods_list() 填充。从版本管理页直接点按钮时它还是空的 → 误判"没有已启用的模组 .jar"。
修复:新增 _mods_scan_jars(vi),直接读 version_mods_dir(vi) 目录返回 [(文件名, 完整路径)](自动排除 .jar.disabled)。_mods_deps_complete 改用它,两个入口行为一致。
二、前置下载提速(串行 → 并发)
原 _mods_deps_worker 是逐项串行:识别 1 次网络 + 解析 2 次网络 + 1 次下载,全部排队。改造为分层并发:
- 并发识别(
_mods_identify+ 8 线程池):N 个模组的 sha1 反查同时进行,不再串行等待。 - 并发解析(8 线程池):本层所有前置的下载信息(
_mr_resolve_dep_file)同时解析。 - 并发下载(4 线程池):每个前置一个独立下载任务,各自持有
tid与进度回调。 - 依赖仍按层展开(前置的前置),层数上限 3、总数上限 20,防失控。
另加 sha1 缓存(_mr_sha1_of_file):按 (路径, 修改时间, 大小) 缓存,更新检查/依赖检查反复调用不再重复哈希大文件;缓存在 App.__init__ 预初始化(避免属性缺失时 getattr 走 tkinter __getattr__)。
实测提速:mock 两个各耗时 0.35s 的下载,串行约 0.7s,并发实测 0.36s。
三、进度可见(页面内实时状态)
- 模组管理页筛选行右侧新增状态标签
self._mods_status_lbl,后台通过队列消息mods_status实时更新:🔗 正在识别 N 个模组…→🔗 正在解析 N 个前置的下载信息…→⬇ 正在下载 N 个前置模组…→⬇ 正在下载前置 1/2…→✅ 前置补全完成:新增 2 个 - 每个前置仍是下载管理页的独立任务(进度条 + MB + 百分比)+ 完成后进入历史。
四、筛选按钮(全部 / 已启用 / 已禁用 / 可更新)
- 搜索行下方新增筛选行,4 个圆角按钮,当前模式高亮(强调色)。
_apply_mods_filter支持self._mods_filter_mode(all/enabled/disabled/updatable),与搜索关键词叠加生效。- 卡片状态位在可更新时显示
[可更新 → 新版本号](强调色),与[正常]/[已禁用]区分。
五、模组更新检查提速
新增 _mods_check_updates / _mods_updates_worker / _mods_one_update:
- 并发 8 线程,每个模组:sha1 反查 Modrinth → 取当前 version_id → 按 (MC 版本, 加载器) 查该项目最新版本 → id 不同即判定可更新(记录 当前版本 / 最新版本 / url / 文件名)。
- 结果缓存
self._mods_updates,_mods_updates_done标记:同一版本重复点「可更新」直接复用结果,不重复请求;切换版本/刷新列表时重置。 - 依赖 sha1 缓存,避免重复哈希大文件。
六、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本)19 项全过:
- 4 种筛选模式 + 关键词叠加 ✓
_mods_scan_jars正确排除.disabled✓- sha1 缓存命中 / 内容变化后重算 ✓
- 并发补装:2 个前置均落盘、耗时 0.36s(串行需 0.7s)✓、进度消息序列完整 ✓
- 更新检查:识别可更新并记录版本号 ✓、未收录模组不误报 ✓
七、遗留
- 「可更新」目前只做检测与标记,一键更新沿用右键菜单的「更新(下载页)」入口;如需直接一键下载替换可后续补。
2026-08-29(2)· 修复「内置 Key 又失效」:跨源 401(base 与 key 不配套)
现象:上一轮修复后主对话可用,但「AI 自动修复」等路径仍报 401「API Key 无效或已过期」。
一、诊断(先排除 key 本身)
逐个实测:千问 3 个 key 全部可用(3/3)、智谱 key 可用、默认预设连续调用 5 次全部成功。→ key 没失效,是调用路径用错了 key。
二、根因:base 与 key 跨源
_ai_sub_agent(子代理)与 _ai_auto_fix(AI 自动修复)是这么取凭据的:
base = cfg.get("base") or ... # 千问 base(内置预设保存的)
key = cfg.get("key") or globals()["AI_BUILTIN_KEY"] # cfg["key"] 为空 → 智谱 key
内置预设的 key 不落盘(上一轮修复),于是 cfg["key"] 为空 → 回退成智谱 key,而 base 仍是千问 → 拿智谱 key 请求千问 → 401。
对照实验直接证实了这点:
| 组合 | 结果 |
|---|---|
| 千问 base + 千问 key(修复后) | ✅ 成功 |
| 千问 base + 智谱 key(旧逻辑) | ❌ 401「API Key 无效或已过期」 |
正常对话走 _ai_ask(用 preset["key"])所以没问题,只有这两条路径踩坑——表现就是"对话能用,一点自动修复就说 key 失效"。
三、修复(仅 main.py)
新增统一凭据解析 _ai_resolve_creds() → 返回 (base, key, model):
- 内置预设:整套使用预设值(
preset["base"]/["key"]/["model"]),保证 base 与 key 同源; - 其它预设:用设置页输入框的值;
- UI 尚未构建时回退
ai_config.json的保存值。
6 处调用点全部改为使用它:_ai_ask、子代理 _ai_sub_agent、_ai_auto_fix、崩溃自动分析、_ai_maybe_compress、设置页测试连接。key 与 base 从此只有一处解析逻辑,不会再出现跨源组合。
四、验证
- 语法编译通过;IDE 静态检查 0 错误 / 0 警告。
- 自测(临时脚本,已删除)10 项全过:
- 内置预设返回千问 base + 千问 key(同源),不再是智谱 key ✓
- 端到端实测:AI 自动修复路径调用成功 ✓
- 对照:旧组合(千问 base + 智谱 key)确实 401 ✓(证实病因)
- 非内置预设用自填 key ✓;UI 未构建时正确回退 cfg ✓
五、备注
- 临时脚本
d:/AI/P/fy/_mods_opt_test.py(上一轮遗留)已一并清理。 - 后续新增任何调用 AI 的地方,都应走
_ai_resolve_creds(),不要自己拼 base/key。
2026-08-29(3)· AI 技能体系扩充 + 崩溃修复手册自动注入
反馈:AI 助手需要更多 skill,知识库的修复方法要更详细,现在根本修不好一个 bug。
一、为什么之前修不好(根因)
- 技能只有 1 个:仅《崩溃诊断知识库》(41 条索引表),且每条"修复要点"只有一句话(如"更新显卡驱动"、"删除损坏的 launcher 配置文件")——AI 拿到也不知道具体改哪个文件、怎么改。
- 工具描述与实物不符:
get_skill描述声称有"崩溃修复、数据包制作、配置调优、整合包制作、搜索"5 类技能,实际只有 1 个,AI 尝试加载其余的必然失败。 - 依赖 AI 主动加载:崩溃流程写在 system prompt 里让 AI 自己
get_skill,弱模型经常不调用。 - 自动修复完全没提技能:
_ai_auto_fix的 prompt 里只字未提技能/知识库。 - 诊断结果没喂给 AI:
ERROR_PATTERNS(16 类正则诊断)的结果只在弹窗显示,没进入 AI 上下文。
二、新增《崩溃修复手册》(详细操作版)
按 diagnose_crash() 的 16 个类别逐一成节,每节结构统一:
- 判定:该类错误的日志特征
- 根因:为什么会这样(讲清原理,不是只给结论)
- 步骤:3-5 条可执行操作(读哪个日志、看哪一行、改哪个文件、用哪个工具、命令怎么写)
- 验证:如何确认修好了
- 坑:常见误区(如本机可能有两个 mods 目录)
含 16 类:module_conflict / mod_missing_dep / mod_duplicate / mod_load_fail / mixin_fail /java_version / jvm_crash / out_of_memory / opengl / natives_missing / java_not_found /auth_fail / download_fail / mod_incompatible / port_in_use / runtime_error,另加「通用排查流程」与「兜底流程」两节。
其中 mixin_fail 一节明确要求优先调用 fix_mixin 工具(上次新增的自动定位+禁用能力)。
三、另补 2 个实用技能
- 《模组与依赖管理》:先确认 mods 目录(隔离 vs 共享)、禁用=改名 .disabled、download_mod 一步安装、前置补全入口、二分法排查冲突、同类优化模组不共存。
- 《性能优化》:客户端优化按性价比排序、内存分配对照表(物理内存 → 建议值)、服务器优化、如何判断瓶颈在客户端还是服务端。
内置技能从 1 个 → 4 个(崩溃诊断知识库、崩溃修复手册、模组与依赖管理、性能优化)。
四、关键改进:知识自动注入,不再靠 AI 主动取
新增模块级 skill_section(kind):按 ## <kind> 标题从手册中截取对应整节。
_ai_auto_analyze(崩溃自动分析):diagnose_crash得到类别后,把该类别整节手册塞进 user prompt 的「详细修复手册」块。_ai_auto_fix(AI 自动修复):同样先对报告文本分类,注入对应手册章节,并在 system prompt 里写明"严格按手册执行,不要自由发挥"、缺失时再get_skill兜底。
即:AI 还没开口,正确的操作手册已经摆在它面前——不再依赖它记得去调工具。
五、配套修正
get_skill描述改为真实可用的 4 个技能名 + 提示可用list_skills查看。- system prompt【修崩溃流程】改写:告知手册已自动注入、不要重复 get_skill、并强调"get_mods 返回的才是实际 mods 目录"这类具体坑。
六、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;用临时目录避免动用户真实 skills)16 项全过:
- 4 个内置技能全部写入 ✓
- 16 个崩溃类别 100% 有对应手册章节,无遗漏 ✓
- 章节够详细(≥200 字符)且含操作步骤 ✓
- 章节提取准确(标题正确、不串节、未收录类别返回空、不误命中兜底节)✓
get_skill能加载手册全文与模糊匹配 ✓
七、备注
- 新技能在启动器启动时写入
D:\发芽启动器\skills,需重启一次生效。 - 用户可在该目录自行增改 .md 技能文件;
_ensure_builtin_skills只覆盖内置 4 个(内容变化时才重写),用户自建技能不会被删。
2026-08-29(4)· AI 工具支持任意磁盘版本 + 去除崩溃后自动修复弹窗
反馈:① AI 修不了 G 盘的版本,说"找不到版本";② 去除崩溃 AI 自动修复(崩溃弹窗上已有入口,重复打扰)。
一、AI 只认启动器目录的版本(Bug 根因)
get_versions / get_game_log / get_mods 三个工具全部硬编码 LAUNCHER_DIR:
get_versions只列D:\发芽启动器\versionsget_game_log只找D:\发芽启动器\logs\<vid>.logget_mods只在D:\发芽启动器\versions\<vid>下找 mods
而启动器本体支持全盘扫描版本,G 盘的版本明明在 self.versions 里,AI 却看不见。
修复:新增 _ai_find_version(vid)(在已扫描的全部版本里按 id 查找,大小写/空格容错)与 _ai_version_names():
get_versions→ 列出全部已扫描版本 + 所在路径(含其他磁盘),并在工具描述里写明"不确定版本名时先调这个";get_game_log→ 优先版本自身logs/latest.log(game_dir→ 版本目录 → mc_dir 逐级尝试);get_mods→ 用version_mods_dir(vi)取实际 mods 目录,同时列出启用/禁用数量与目录路径;- 版本不存在时的失败提示会附上可用版本列表,让 AI 能自我纠正(而不是瞎猜版本名)。
过程中发现并修复的语义缺陷:最初 _ai_find_version 在"指定版本不存在"时会悄悄回退到当前选中版本——AI 会拿着另一个版本的日志/模组去修用户指定的版本,修错对象。已改为:vid 为空才回退选中版本;vid 非空且不存在时明确报"未找到版本「X」+ 可用版本列表"。同理,get_game_log 在版本不存在时不再用全局 game-latest.log 冒充该版本日志(那是上次启动的任意版本的日志)。
二、去除崩溃后自动弹出的「AI 自动修复」询问
原行为:崩溃 → 错误报告弹窗 + 自动 AI 分析 → 分析完成后再弹一次 askyesno 问"是否接受 AI 自动修复" → 自动改动 mods 目录。
改动:删除该自动弹窗(_on_ai_fix_confirm 调用与方法一并移除)。崩溃报告弹窗上的「🤖 发送 AI 分析」入口保留;_ai_auto_fix 能力本身保留(供 AI 助手页/后续手动入口调用),但不再自动弹窗、不再未经确认就动玩家的 mods 目录。
三、验证
- 语法编译:
python -m py_compile main.py通过;IDE 静态检查 0 错误 / 0 警告。 - 自测(临时脚本,已删除;用临时目录模拟 G 盘版本)16 项全过:
get_versions能看到启动器目录之外的版本并列出路径 ✓get_game_log读到 G 盘版本自身日志并报出文件路径 ✓get_mods列出启用/禁用模组与实际 mods 目录 ✓- 版本名大小写/空格容错 ✓
- 不存在的版本:mods 与日志均给出"未找到 + 可用版本列表"(不再误回退/误用全局日志)✓
- 未传版本时回退当前选中版本 ✓
_on_ai_fix_confirm已移除、分析完成后不再触发自动修复 ✓、_ai_auto_fix能力保留 ✓
四、备注
- AI 工具修复的对象范围与启动器一致了:版本选择页扫描到哪,AI 就能修到哪。
- 需重启启动器生效。
2026-08-29(5)· 崩溃后不再自动分析;主动分析后直接自动修复
澄清需求(更正上一轮的理解):要取消的是「自动分析」而非「自动修复」——崩溃后不要自动发起 AI 分析;但用户在弹窗点了「发送 AI 分析」,分析完成后就应直接自动修复。
一、行为变更
| 场景 | 旧行为 | 新行为 |
|---|---|---|
| 游戏崩溃(game_exit) | 弹错误报告 + 自动发起 AI 分析 + 分析完弹窗问是否修复 | 只弹错误报告,是否分析由用户决定 |
| 弹窗点「发送 AI 分析」 | 受「自动分析」开关限制;分析完弹窗问是否修复 | 按钮改名 「🤖 AI 分析并修复」,不受开关限制(用户明确意图);分析完成直接自动修复,无二次弹窗 |
| 下载失败(dl_fail) | 自动分析(受开关控制) | 不变,仍受开关控制 |
二、实现
game_exit:删除_ai_auto_analyze(...)自动调用(_show_error_report、崩溃历史保留)。_ai_auto_analyze(..., auto_fix=False)新增参数:
auto_fix=True(用户主动):跳过「自动分析」开关检查,并置self._ai_analyze_auto_fix = True;auto_fix=False(下载失败等自动场景):行为不变,受开关控制,标志置 False。
_on_ai_analyze_result:末尾读取_ai_analyze_auto_fix标志,True 则直接self._ai_auto_fix(reply, vid=当前选中)并消费标志(不弹窗确认——普通模式下危险工具仍有确认弹窗兜底)。- 弹窗按钮文字 →「🤖 AI 分析并修复」,调用带
auto_fix=True。 - 设置页开关文字更新:「下载失败时 AI 自动诊断(崩溃需在弹窗点「AI 分析并修复」)」。
App.__init__预初始化_ai_analyze_auto_fix = False。
三、过程中修复的连带问题
改 _ai_auto_analyze 开头时,函数剩余部分(原 try 块内 12 空格缩进)出现悬挂;同时发现该函数的凭据解析仍是旧代码(注释不同导致上轮 replace_all 漏掉,未走 _ai_resolve_creds())。已一并修复:补 try: 恢复结构 + 凭据解析统一走 _ai_resolve_creds()。
四、验证
- 语法编译通过;IDE 静态检查 0 错误 / 0 警告。
- 自测(临时脚本,已删除)12 项全过:
- 开关关 + 自动场景 → 不发起 ✓;开关开 + 自动场景 → 发起但不标记修复 ✓
- 用户主动(auto_fix=True)→ 不受开关限制、标记自动修复 ✓
- 分析完成:标志 True → 调用
_ai_auto_fix(正确的 reply/vid)并消费标志 ✓;标志 False → 不修复 ✓ - 静态检查:
game_exit不再调_ai_auto_analyze、仍弹错误报告 ✓;按钮文案与auto_fix=True✓;开关文字已更新 ✓
五、备注
- 修复动作会改 mods 目录(如 fix_mixin 改名禁用),普通模式下工具执行前仍有用户确认弹窗(
_confirm_tool_exec),安全兜底不变。 - 需重启启动器生效。



