0
0

给 Electron Agent 长任务做断点续跑时,我会先把主进程账本建起来

最近做一个 Electron 桌面端 Agent 工具时,我遇到一个典型问题:点击“分析项目”后,后台要连续跑扫描、模型调用和报告生成。任务持续几分钟,只要窗口刷新、应用退出或 worker 报错,前端进度就会失真。后来我改用主进程任务账本,renderer 只负责展示。

Electron Agent 长任务从渲染进程、IPC、主进程账本、Worker 执行到启动恢复扫描的流程图

原创示意图:把 Electron Agent 长任务拆成 UI 命令、主进程账本、独立执行、进度事件和启动恢复扫描。 来源:Codex image generation

问题背景

Electron 的渲染进程适合页面交互,主进程负责应用生命周期和原生能力,两边通过 IPC 通信。官方 IPC 教程也把 renderer 和 main 的通信作为明确边界。长任务绑在页面状态里,窗口销毁后状态会一起丢。我更愿意把任务记录放进主进程持久层,把执行放到 utilityProcess 或 Node worker 里。

踩坑点

第一个坑是把进度事件当事实来源。事件适合驱动 UI,但可能乱序、重复或丢失。能恢复任务的事实应来自 taskId、status、currentStep、checkpoint、attempt 和 lastError。

第二个坑是混用取消、失败和窗口关闭。用户取消要落成终态,窗口关闭只断开订阅,进程崩溃要保留可重试现场,网络超时要判断当前步骤能不能重放。

第三个坑是启动恢复太急。应用启动后直接重跑所有 in_progress 任务,容易重复写文件或重复调用模型。我现在会先做 resume scan,检查输入快照、锁和幂等标记。

解决思路

我把链路拆成四层。renderer command 只提交用户意图。main ledger 写入持久化任务记录。worker execution 跑扫描、模型调用和报告写入。resume reconciler 在启动或 worker 退出后修正未完成任务。

关键原则是单写入口。渲染进程不能直接改状态,worker 也不能绕过账本写最终状态。worker 只上报步骤事件,由主进程按 taskId 和 attempt 推进状态。

关键步骤

落地时我会先定义任务记录:taskIdinputHashstatuscurrentStepattemptcheckpointleaseUntil。如果本地存储用 SQLite,我会打开 WAL 模式,降低读进度和写 checkpoint 的冲突。SQLite 官方文档说明,WAL 模式下读写并发能力更好,适合任务面板和后台执行同时工作。

接着给 IPC 做窄接口。renderer 只能发起命令和订阅事件,命令里必须带 taskId 或 requestId。main 收到命令后先校验状态机,避免重复 start、错误 reopen 或无 attempt 重试。状态推进成功后,再派发给 utilityProcess 或 worker_threads。

最后做恢复扫描。应用启动后,main 读取 ledger,把 running 但 lease 过期的任务标为 recovering。恢复器检查 checkpoint:已经写出报告就补完成状态,可重试步骤重新派发,非幂等步骤转 waiting。

可复用经验

长任务续跑的重点是让任务事实脱离窗口生命周期。窗口可以关,事件可以丢,worker 可以重启,但 task ledger 不能含糊。主进程掌握单写入口,执行单元按 checkpoint 推进,启动时有恢复扫描,桌面端 Agent 工具就能把“跑到一半没了”的体验改成可解释、可继续、可回收的工作流。

我现在会先问四个问题:任务事实存在哪里,谁有权改状态,步骤能不能幂等重放,应用启动时如何处理半完成任务。答案清楚后,再选 utilityProcess、worker_threads 或普通子进程。

主要来源

Electron IPC Tutorial

Electron utilityProcess API

Node.js worker_threads

SQLite Write-Ahead Logging

评论