作者:互联网 时间: 2026-06-30 09:03:58
最近在使用 Hermes Agent(一个基于 Electron 的 AI Agent 桌面应用)时,遇到了一个棘手的问题:桌面端启动后窗口闪一下就消失,每次启动都需要手动运行修复工具,自动更新后问题还会循环复发。

经过深入排查,发现这不是一个单独的 bug,而是四个问题相互交织形成的死循环。整个排查过程涉及 Electron 底层机制、Windows 进程模型、Python 虚拟环境、以及 node-pty 终端模拟器等多个技术领域,记录下来供大家参考。
| 项目 | 版本 |
|---|---|
| 操作系统 | Windows 10 Build 10.0.22621 |
| 显卡 | NVIDIA GeForce RTX 2060 |
| 显卡驱动 | 32.0.16.1062 (2026-06-11) |
| Hermes | v0.17.0 |
| Electron | 40.10.2 |
| Python | 3.11.0 (venv) |
查看 desktop.log,第一条线索很明确:
复制代码[hermes] [renderer] render-process-gone reason=crashed exitCode=-2147483645
exitCode=-2147483645 即 0x80000003,是 Windows 的断点异常(BREAKPOINT EXCEPTION)。这是 Electron 的 GPU 进程崩溃了。
之前的修复工具设置了这些环境变量:
复制代码set ELECTRON_DISABLE_GPU=1
set ELECTRON_DISABLE_GPU_SANDBOX=1
set ELECTRON_NO_SANDBOX=1
看起来没问题对吧?但当我读了 main.cjs 的源码后发现——这些变量根本没被检查。
在 main.cjs 第 74 行,Hermes 检查的是:
复制代码const override = String(env2.HERMES_DESKTOP_DISABLE_GPU || "").trim().toLowerCase();
if (GPU_OVERRIDE_ON.has(override)) return "override (HERMES_DESKTOP_DISABLE_GPU)";
正确的变量名是 HERMES_DESKTOP_DISABLE_GPU,不是 ELECTRON_DISABLE_GPU。
即使设置了 HERMES_DESKTOP_DISABLE_GPU=1,它调用的是:
复制代码app.disableHardwareAcceleration();
app.commandLine.appendSwitch("disable-gpu-compositing");
实测发现,app.disableHardwareAcceleration() 对 RTX 2060 不够用,GPU 进程仍然崩溃。只有 --disable-gpu 这个 Chromium 命令行参数才能彻底阻止 GPU 进程启动。
复制代码# 测试对比
HERMES_DESKTOP_DISABLE_GPU=1 ./Hermes.exe # GPU 仍然崩溃
./Hermes.exe --disable-gpu # GPU 不崩溃
这是 Electron/Chromium 层面的问题,disableHardwareAcceleration() 只是禁用了硬件加速合成,但 GPU 进程本身还是会启动。--disable-gpu 才是彻底禁用 GPU 进程的开关。
GPU 崩溃只是第一个问题。即使 GPU 修好了,桌面端还是无法正常工作。
查看 main.cjs 的启动逻辑:
复制代码function resolveHermesBackend(dashboardArgs) {
// 第一步:检查 bootstrap 是否完成
if (isBootstrapComplete()) {
return createActiveBackend(dashboardArgs); // 正常路径
}
// 第二步:在 PATH 上找 hermes CLI
const hermesCommand = findOnPath("hermes");
if (hermesCommand) {
// 尝试探测 CLI 是否可用
if (verifyHermesCli(hermesCommand)) {
return { /* 正常后端 */ };
}
// 探测失败 → 认为需要 bootstrap
}
// 第三步:触发 bootstrap 流程
return { kind: "bootstrap-needed" };
}
isBootstrapComplete() 检查一个标记文件 .hermes-bootstrap-complete:
复制代码function isBootstrapComplete() {
const marker = readBootstrapMarker();
if (!marker || typeof marker !== "object") return false;
if (marker.schemaVersion !== 1) return false;
if (typeof marker.pinnedCommit !== "string" || marker.pinnedCommit.length < 7) return false;
return isHermesSourceRoot(ACTIVE_HERMES_ROOT) && fileExists(getVenvPython(VENV_ROOT));
}
这个标记文件不存在。 所以桌面端每次都走"从未完成初始化"的路径,触发 bootstrap 流程。
标记文件不存在,代码降级到 PATH 探测。它在 PATH 上找到了 hermes.EXE:
复制代码[hermes] Ignoring existing Hermes CLI at C:...venvScriptshermes.EXE:
--version probe failed; falling through to bootstrap.
有意思的是,我在终端里直接运行 hermes.EXE --version 完全正常。但在 Electron 进程内探测失败。
查看探测代码:
复制代码function verifyHermesCli(hermesCommand, opts = {}) {
try {
execFileSync(hermesCommand, ["--version"], {
stdio: "ignore",
timeout: 5000, // 5秒超时
shell: false,
windowsHide: true
});
return true;
} catch {
return false; // 超时或异常都返回 false
}
}
探测失败的可能原因:
当 CLI 探测也失败后,代码触发 handOffWindowsBootstrapRecovery():
复制代码async function handOffWindowsBootstrapRecovery(reason) {
const updater = resolveUpdaterBinary();
const child = spawn(updater, ["--update", "--branch", "main"], {
detached: true,
stdio: "ignore",
windowsHide: false // 注意:这里是 false!
});
child.unref();
setTimeout(() => app.quit(), UPDATE_HANDOFF_DWELL_MS);
return true;
}
这个函数做了两件事:
hermes-setup.exe --update(一个终端程序,windowsHide: false,所以会弹黑窗口)hermes-setup.exe 更新完成后会重启桌面端,但不带 --disable-gpu 参数,于是 GPU 又崩了,又触发探测失败,又触发 bootstrap……死循环形成。
复制代码GPU 崩溃 → CLI 探测失败 → 触发 bootstrap
→ hermes-setup.exe 弹出(闪一下的黑窗口)
→ 更新完成 → 重启桌面端(不带 --disable-gpu)
→ GPU 又崩 → 循环 ️
解决了上述四个问题后,桌面端能正常启动了。但又发现:每次 agent 执行终端命令时,都会有一个 CMD 黑窗口闪烁一下。
排查发现是 node-pty(Electron 内嵌的终端模拟器)在 Windows 上的行为:
复制代码const ptyProcess = nodePty.spawn(command, args, {
cols, cwd, env: terminalShellEnv(),
name: "xterm-256color", rows
});
node-pty 在 Windows 上使用 conpty(Windows Console API)创建伪终端,每次都会创建一个 conhost.exe 进程,表现为短暂的 CMD 窗口闪烁。
这是 node-pty 的已知限制——windowsHide: true 对它无效,因为它不走 Node.js 的 child_process.spawn,而是直接调用 Windows Console API。
复制代码{
"schemaVersion": 1,
"pinnedCommit": "7cd5eaa646f1...",
"pinnedBranch": "main",
"completedAt": "2026-06-26T08:50:00.000Z",
"desktopVersion": "0.17.0"
}
放到 C:UserscjAppDataLocalhermeshermes-agent.hermes-bootstrap-complete。
复制代码Set WshShell = CreateObject("WScript.Shell")
WshShell.Run """...Hermes.exe"" --disable-gpu --disable-software-rasterizer --no-sandbox --no-update-check", 0, False
WshShell.Run 的第二个参数 0 表示完全隐藏窗口。
复制代码# 唯一被代码检查的变量
[Environment]::SetEnvironmentVariable('HERMES_DESKTOP_DISABLE_GPU', '1', 'User')
之前的修复工具设置了 9 个代码根本不检查的变量,全部删除。
ELECTRON_DISABLE_GPU 看起来很官方,但 Hermes 检查的是 HERMES_DESKTOP_DISABLE_GPU。读源码比猜变量名靠谱。
app.disableHardwareAcceleration() 和 --disable-gpu 不是一回事前者只禁用硬件加速合成,GPU 进程仍然会启动;后者才彻底禁用 GPU 进程。对某些显卡来说,只有后者才有效。
desktop.log 最后写入时间是 6 月 21 日,但问题发生在 6 月 26 日。说明新版本的崩溃比旧版本更严重——连日志都来不及写就崩了。
很多应用用标记文件记录初始化状态。当出现"重复初始化"的问题时,先检查标记文件是否存在。
node-pty 在 Windows 上会弹窗如果 Electron 应用内嵌了终端功能,node-pty 在 Windows 上会短暂显示 CMD 窗口。这是底层限制,需要用 CREATE_NO_WINDOW 标志或替代方案解决。
这个问题已向 Hermes 上游提交: github.com/NousResearc…