【免费下载链接】desktop
Browser Use Desktop App
Browser Use Desktop 是一款 AI Agent 浏览器桌面应用:你把任务丢给它,它会自己打开网页、点击、填写、抓取数据。但这类任务动辄跑几分钟甚至更久——如果应用在任务中途重启或崩溃,会话记录会不会全部丢失?答案是:不会。Browser Use Desktop 用一套基于 SQLite 的会话持久化(Session Persistence)机制,把会话状态、完整事件流和附件文件全部落盘到本地数据库,应用重启后会话列表、暂停状态、引擎对话 ID 都能原样找回。本文带你从零看懂这套设计的核心原理。
为什么选 SQLite 做会话持久化
桌面应用要持久化"会话"这种复杂数据(状态、消息流、二进制附件、成本统计),可选方案其实不少:JSON 文件、内存数据库、远程 API……而 Browser Use Desktop 选择了SQLite,原因很实在:
| 考量 | SQLite 给出的答案 |
|---|---|
| 部署复杂度 | 单个.db文件,无需任何数据库服务器 |
| 崩溃安全 | WAL(预写日志)模式,进程被杀也不会丢已提交数据 |
| 并发读写 | WAL 模式下读写互不阻塞,主进程可高频写入事件 |
| 数据形态 | 关系表 + BLOB,天然适合"元数据 + 事件流 + 附件"三层结构 |
| 迁移能力 | user_version+ 增量 migration,老库自动升级 |
数据库文件位于 Electron 的标准用户数据目录下,应用启动时由 index.ts 一行代码创建:
const sessionManager = new SessionManager(path.join(app.getPath('userData'), 'sessions.db'));也就是说,你在 macOS 上可以在~/Library/Application Support/下找到这个sessions.db文件,它就是全部会话历史的"家"。
三层表结构:会话、事件、附件各司其职
打开 SessionDb.ts 的建表逻辑,数据库共有三张表,职责划分非常清晰:
1.sessions表:每个会话一行
这是"会话名片"。每个会话一行记录,列出了你关心的几乎所有元数据:
- 身份与来源:
id(UUID 主键)、prompt、origin_channel、origin_conversation_id - 状态机:
status,取值限定为 6 种——draft / running / stuck / paused / idle / stopped(见 db-constants.ts),非法状态在写入前就会被拦截 - 现场信息:
primary_site(主要访问的站点,如example.com)、last_url(最后一个可恢复的 URL) - 引擎指纹:
engine、engine_session_id、model、auth_mode——重启后凭这些字段让会话"回到原来的引擎里继续聊" - 成本统计:
cost_usd、input_tokens、output_tokens、cached_input_tokens
2.session_events表:完整的事件流日志
会话执行过程中产生的每一条事件(用户输入、Agent 输出、通知、错误……)都以 JSON 形式追加进来,UNIQUE(session_id, seq)保证每个会话内序号唯一有序。这张表有两个巧妙设计:
- 外键级联删除:
REFERENCES sessions(id) ON DELETE CASCADE,删掉会话时事件自动清理,绝不留孤儿数据 - 事件即回放素材:重启后 UI 要恢复聊天记录,直接按
seq顺序读回事件流即可(hydrateOutput,SessionManager.ts)
3.session_attachments表:图片/PDF 等附件的 BLOB 存储
用户在对话里上传的附件不做外部文件管理,直接以BLOB存进数据库,同时记录name、mime、size和turn_index(属于第几轮对话)。同样配置了级联删除,会话删除即附件删除。
💡 一个直观的对应关系:
sessions管"是什么",session_events管"发生了什么",session_attachments管"带了什么"。
启动即恢复:重启后的两步修复
真正让"任务不怕重启丢"成立的,是 SessionManager 在启动时执行的恢复流程。
第一步:把"假死"会话标记为已停止
上次应用是被强杀还是意外退出的?数据库里可能残留着状态还是running/stuck的会话。启动时recoverStaleSessions()会一条 UPDATE 把所有这类会话批量置为stopped,并附上错误信息:
App exited unexpectedly(常量定义见 db-constants.ts)。于是重启后你在界面里看到的是"上次任务因应用退出而停止",而不是一个永远不会动的"运行中"假象。
第二步:重建内存中的会话
随后loadPersistedSessions()从库里读出最近的 100 个会话,逐一重建内存对象,并重点恢复三类"续命"信息:
- 引擎与对话 ID:
engine_session_id是服务商侧的会话标识(如 Codex 的thread_id),恢复它,追问就能继续原对话而不是开新对话; - 现场 URL:
last_url+primary_site,重启后能定位到 Agent 上次停在哪; - 暂停状态:
paused的会话重启后仍是paused——你暂停的任务不会因为重启而"复活"或"蒸发"。
事件流则采用懒加载:只有当 UI 真正打开某个会话时,才从session_events读回完整记录(hydrateOutput),避免启动时把所有历史一次灌进内存。
WAL 模式:性能与崩溃安全的平衡
打开数据库的那一刻,SessionDb 构造函数 就设置了三个 pragma:
PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;- WAL(Write-Ahead Logging):写入先进追加式日志再合并,读写不再互相锁表。Agent 高频吐事件时,UI 的查询不会被卡住;
- synchronous = NORMAL:在 WAL 模式下这是性能与安全的甜点——崩溃最多丢最后一个 checkpoint,而不会丢已提交事务;
- foreign_keys = ON:SQLite 默认不启用外键,这里手动打开,才能让"删会话级联删事件/附件"真正生效。
此外,所有 SQL 语句在 prepareStatements() 中启动时一次性预编译(30 多条语句全部缓存为 prepared statement),之后每次写入都走缓存的语句对象,省掉反复解析编译的开销。批量写入事件时用db.transaction()把多条 INSERT 包进一个事务(appendEventsBatch),一次性提交,原子又快。
应用正常退出前还会执行wal_checkpoint(TRUNCATE)(close()),把 WAL 日志合并回主库并清空日志文件——下次启动更干净。
Schema 版本与指纹:数据库不会"越界"
数据库比应用新怎么办?老应用打开新数据库是危险的。runMigrations() 的策略很硬核:
if (current > DB_SCHEMA_VERSION) → 直接抛错,拒绝启动即:只允许数据库比应用旧(触发迁移升级),绝不允许数据库比应用新(宁可罢工也不读乱)。当前 schema 版本号为 12(DB_SCHEMA_VERSION),migration 从 v1 的三张基础表一路演进到 v12 的last_url/engine_session_id,每个版本都在事务中执行、幂等可重入。
除了版本号,项目还做了一层"指纹"校验:schemaIdentity.ts 把所有表、索引、视图按规范顺序读出,序列化成 JSON 后做 SHA256,生成形如sessions:v12:sha256-xxxx的 schema 身份标识。任何对结构的意外改动(哪怕版本号没变)都能被哈希差异暴露出来——这是比user_version更细一级的保险。
附件怎么存:限制是算出来的
附件大小限制藏在 db-constants.ts,而且注释里写明了推导过程:
- LLM API 对base64 编码后的 payload 有限制(如 5MB),base64 比原始体积大 4:3,所以原始上限要按
floor(api_limit × 3/4) - 1024回推 - 图片约 3.75MB、PDF 约 24MB、纯文本 1MB、其他类型 10MB
- 每个会话最多 200 个附件、每条消息最多 10 个、单条消息附件总量不超过 50MB
这些上限在saveAttachment写入前强制检查,防止"无限制的追问上传"把数据库撑爆。
测试如何守住"重启不丢"这条底线
这套机制不是靠"应该没问题",而是有专门的测试用例守着:
- 持久化单测:SessionManager.persistence.test.ts 模拟"创建 → 销毁 → 重新打开同一个库"的完整重启流程,逐一验证:导航信息(
primarySite/lastUrl)恢复、引擎对话 ID(thread-123)恢复、暂停的会话重启后仍是paused、已停止的会话可以接受新的追问并重置输出…… - E2E 回归:session-restore.spec.ts 用 Playwright 真实启动应用两次,验证"打开 3 个标签 → 退出 → 重启 → 标签数和 URL 原样还在",以及"会话文件损坏时优雅降级而不是崩溃"。
- Schema 校验:SessionDb.schemaIdentity.test.ts 守护 schema 指纹计算的正确性。
另外提一句:浏览器标签页的恢复(session.json)与Agent 会话的恢复(sessions.db)是两套独立机制,前者管"你开着哪些网页",后者管"你和 Agent 聊到哪了",各管一摊、互不干扰。
小结:一套值得借鉴的持久化设计
回看 Browser Use Desktop 的会话持久化方案,它的完整拼图是:
- SQLite 单文件 + WAL:零部署、崩溃安全、读写不互锁
- 三张表分层:元数据 / 事件流 / 附件,外键级联保证一致性
- 启动恢复:先批量修复"假死"会话,再重建内存,事件懒加载
- 双重结构守卫:
user_version迁移 + SHA256 schema 指纹 - 量化限制:附件大小和数量上限有明确推导,防止数据库膨胀
- 测试兜底:单测模拟重启、E2E 真实重启、损坏场景降级验证
对任何需要"会话可恢复"的桌面 Agent 应用来说,这套设计简洁、可验证,也足够坚固——任务跑一半,重启应用,回来继续。
延伸阅读:
- 核心实现:SessionDb.ts、SessionManager.ts、db-constants.ts
- 类型定义:shared/session-schemas.ts
- 性能与启动耗时审计:PERFORMANCE.md
- IPC 事件协议规范:shared/schemas/README.md
【免费下载链接】desktop
Browser Use Desktop App
相关推荐
Browser-Use存储状态:会话数据持久化管理
Browser Use存储状态:会话数据持久化管理 概述 在现代Web自动化场景中,会话状态的持久化管理是确保自动化任务连续性和可靠性的关键。Browser U
人工智能AI Agent浏览器控制MCP 服务终极解决方案:Browser-Use WebUI持久会话技术让浏览器状态永不丢失
终极解决方案:Browser Use WebUI持久会话技术让浏览器状态永不丢失 GitHub推荐项目精选的web ui是一款能够在浏览器中运行AI Agent
人工智能AI 应用大模型AI Agent浏览器控制深度研究本地部署对话不丢失:chatbot-ui会话持久化与恢复全解析
对话不丢失:chatbot ui会话持久化与恢复全解析 你是否遇到过这样的困扰:与AI助手深入交流时意外刷新页面,所有对话历史瞬间消失?作为开源AI聊天界面的佼
人工智能AI 应用前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考