从同步到备份:AI Agent 数据安全的缺失半程
上周我写了一篇关于 AI Agent 技能多机同步问题的文章 —— 如何让 agent 的技能、提示词和上下文在 MacBook、Linux 工作站和云服务器之间保持一致。答案是三层架构:内置技能随 agent 更新、第三方技能通过 manifest + bootstrap 安装、个人技能通过 git + external_dirs 同步。
那篇文章详尽覆盖了同步维度。但还有一个同步本身无法解决的维度:机器本身没了怎么办?
同步保证一致性,备份保证存活率。它们是同一目标——数据安全——的两个阶段,缺一不可。
我们架构中的缺口
同步架构落地后,我审视了一遍哪些数据受保护、哪些没有:
| 数据 | 同步覆盖? | 备份覆盖? | 风险 |
|---|---|---|---|
| 技能 (Layer 1) | 是 (agent update) | 否 | 机器挂了就丢 |
| 技能 (Layer 2) | 是 (manifest + 重装) | 否 | 可重装,但存在配置漂移 |
| 技能 (Layer 3) | 是 (git push) | 部分 (git 历史) | repo 存活,但未 push 的本地编辑会丢 |
| config.yaml | 否 | 否 | 完全丢失 |
| state.db | 否 | 否 | 完全丢失——所有会话历史清零 |
| memories/ | 否 | 否 | 完全丢失——所有习得偏好清零 |
| .env (API keys) | 否 | 否 | 完全丢失——需要手动重新签发 |
| scripts/ | 否 | 否 | 完全丢失 |
Hermes 确实会在更新前创建状态快照(~/.hermes/state-snapshots/),但这些只是薄的配置副本,不是完整的数据归档,而且只在 agent 版本变更时才触发,不是定期执行。
同步架构就像机器间的 RAID 1:一份副本损坏,另一份还在。但如果你的云服务器被释放了,或者你不小心 rm -rf ~/.hermes,那两份就都没了——因为本地机器是运行时状态的唯一真相来源。
出场:hermes-agent-core-backup
社区技能 hermes-agent-core-backup(art-solutions 出品)用一种极其直接的方式解决备份问题:
- 一个 shell 脚本把所有关键 Hermes 状态 ZIP 打包
- 一个 cron job 每晚定时执行
- git push 到私有远程仓库
脚本备份:config.yaml、state.db(SQLite 大脑)、memories/、skills/、scripts/、.env 和可选的数据目录。产出时间戳命名的 ZIP 文件(如 backup_yourname_2026_05_04.zip),然后提交到私有 git 仓库。
它做对了什么
覆盖运行时状态缺口。 git 仓库里的技能有版本控制,但 state.db、memories 和 .env 没有。这个脚本把所有关键数据抓进一个归档。
零逻辑、零依赖。 就是 zip + git。不需要数据库 dump 命令、不需要 API 调用、不需要云 SDK。服务器有 git 和 zip 就能跑。
私有仓库要求前置。 README 明确声明 .env 含 API 密钥会进入归档,所以远程仓库必须私有。这是很多 DIY 备份脚本忽略的安全考量。
恢复只需三条命令。 clone、unzip、copy。无需特殊工具。
它缺了什么
没有增量策略。 每次备份是完整 ZIP。30 天后你有 30 个 ZIP 在 git 里,每个可能 50-200MB。Git 不是为二进制大文件设计的——仓库会迅速膨胀。
没有自动清理。 脚本只添加新 ZIP,从不删除旧的。备份仓库无限增长。你需要手动 git rm 或单独的清理 cron。
没有校验。 脚本创建并推送 ZIP,但从不验证 ZIP 能否解压、state.db 是否损坏、远程是否真正收到了推送。
没有差分感知。 不检查上次备份以来是否有任何变化。即使你零改动,每晚也生成新的完整 ZIP。
技能被冗余备份。 Layer 3 技能已经在 git 仓库里。Layer 2 技能可通过 manifest 重装。在 ZIP 里再备份一遍所有技能,是 git 已经更好地处理了的事情(有历史、有 diff、可选择性回滚)。
六维评估:同步 vs 备份
| 维度 | 同步(我们的三层架构) | 备份(Core-Backup 技能) | 理想组合 |
|---|---|---|---|
| 目标 | 多机一致性 | 灾难恢复 + 时间点回溯 | 两者兼备 |
| 粒度 | 逐文件、逐技能 | 整个 .hermes/ 作为一个 blob |
技能细粒度,状态粗粒度 |
| 历史 | Git 历史(diff、revert) | 完整快照(ZIP 之间无 diff) | 文本用 git,二进制度快照 |
| 增量 | 是(git 天然增量) | 否(每次完整 ZIP) | 技能增量,状态快照 |
| 密钥处理 | .gitignore 排除 .env | .env 包含(需私有仓库) | 独立密钥管理 |
| 存储代价 | 低(git object 压缩) | 高(git 仓库存二进制 blob) | 代码用 git,非代码用归档 |
核心洞察:同步和备份有不同的最优存储格式。 技能是文本文件——git 给它们 diff 历史、选择性回滚和分支语义。运行时状态(SQLite、.env)是二进制或敏感数据——需要加密快照,不需要逐行 diff。
补齐缺口:我们的集成策略
与其照搬备份技能,我将其最优秀的思路整合到现有架构中,同时保留已经工作的部分。
采纳的部分
-
运行时状态的每日 cron 备份。 我们需要这个。脚本的 cron + zip + git push 方案对 state.db、memories、config.yaml 和 .env 是简单且正确的。
-
密钥的私有仓库要求。 任何包含 .env 或 API 密钥的归档必须存放在私有仓库。没有例外。
-
显式的恢复流程。 我们当前设置没有文档化的恢复路径。需要补上。
修改的部分
-
从备份 ZIP 中排除技能。 技能已经在 git 中受版本控制(Layer 3)或可通过 manifest 重装(Layer 2)。包含在 ZIP 中只会给 git 仓库增加冗余体积。备份应只覆盖 git 未保护的数据。
-
添加保留清理。 保留最近 N 个备份(可配置,默认 7),推送前自动清理更早的。不做清理的 git 仓库含二进制 blob 会无限增长。
-
添加备份校验。 创建 ZIP 后,解压到临时目录验证 state.db 可正常打开、config.yaml 可解析、memories/ 非空。无法恢复的备份比没有备份更糟——它给你虚假的安全感。
-
备份仓库与技能仓库分离。 我们的 personal-skills 仓库有 198 个技能。把每天 50MB+ 的 ZIP 混进技能仓库会严重拖慢 clone 和搜索性能。备份仓库应该是独立的私有仓库。
保持不变的部分
-
三层同步用于技能。 这是让技能跨机一致的正确架构,备份不替代它。
-
基于 manifest 的 Layer 2 重装。 在新机器上,从 manifest 安装第三方技能而非从旧 ZIP 恢复。这总能拿到最新版本。
-
external_dirs 用于个人技能。 逐技能的 git diff 和选择性回滚比"从上周二恢复整个 ~/.hermes/skills/"实用得多。
完整图景
数据安全 = 同步 + 备份
┌─────────────────────────────────────────────────┐
│ 同步(跨机器) │
│ Layer 1: 内置技能 → hermes update │
│ Layer 2: 第三方技能 → manifest + bootstrap │
│ Layer 3: 个人技能 → git + external_dirs │
├─────────────────────────────────────────────────┤
│ 备份(灾难恢复) │
│ 运行时状态 (db, config, .env, memories) │
│ → 每晚 cron: zip + verify + git push │
│ → 保留策略: 最近 7 份,自动清理 │
│ → 私有仓库,与技能仓库分离 │
│ → 恢复: clone → unzip → copy → restart │
└─────────────────────────────────────────────────┘
两层都不可或缺。同步给你一致性——任何机器都可以作为主机。备份给你存活率——即使所有机器都挂了,你的 agent 大脑也能重建。
实现要点
备份脚本应位于 ~/.hermes/scripts/backup_state.sh,cron 每日 01:00 触发。关键设计决策:
- 只 ZIP 运行时状态,不含技能。 技能有自己的同步机制。
- 推送前校验。 解压到
/tmp,验证 SQLite 完整性,检查文件数量。 - 清理旧备份。 保留最近 7 个 ZIP,commit 前删除其余。
- 独立仓库。
git@github.com:USER/hermes-state-backup.git——私有,与技能仓库分离。 - 幂等恢复。 恢复脚本应能在只有 git 和 unzip 的全新机器上正常工作。
对生态的启示
hermes-agent-core-backup 技能揭示了一个大多数 agent 生态共有的盲区:我们优化了日常使用模式,却忽视了灾难场景。 我调研过的所有 agent 框架——Claude Code、Cursor、Copilot、Codex CLI、OpenCode、Aider——都专注于跨机同步配置,但没有一个内置了"我的服务器挂了,一切都没了"的应对方案。
这可以理解。同步是用户日常能感受到的(不同机器行为不同)。备份在灾难来临前是隐形的。但你使用 AI agent 越久,它积累的不可替代状态就越多——习得偏好、长期记忆、定制工作流。丢失这些比丢失任何单个项目的源代码更严重,因为源代码在 git 里,而 agent 状态往往不在。
解决方案并不新奇。这是保护生产数据库几十年的同一模式:定期快照、异地存储、验证恢复。社区技能正确地应用了这个模式。我们的贡献在于认识到备份和同步是互补而非竞争关系——并让两个系统覆盖不同数据类型的最优存储格式。
同步管变化,备份管生死。不可丢失的数据,两者缺一不可。