夜雨聆风学习资料网

ARTICLE · 1184398

我把 AI 助手整体搬了一次家:九个动作,一份可以直接照抄的清单

我把 AI 助手整体搬了一次家:九个动作,一份可以直接照抄的清单

先说清楚对象。Hermes 是一个可以自托管的 AI 助手:你把它装在自己的服务器上,用 Telegram 这类聊天工具指挥它干活。它和普通脚本最大的区别是「会长」—— 技能、记忆、会话记录、知识库都会随时间沉淀下来。

打个比方:装 Hermes 像租个店面开业,搬 Hermes 像整店搬迁。桌椅好搬,难的是租约、账本,以及那些你忘了自己签过的东西。

所以这篇不讲道理,讲动作:这次整机迁移从头到尾做了九件事。每一步做什么、检查什么、哪里会翻车,都写清楚,最后给你一份可以照着走的清单。

一、全景:九个动作

①  盘点资产        →  分出「三层」 ②  建搬运清单      →  默认全搬 + 显式排除,落成单一文件 ③  过审计门        →  六项检查,全绿才许动 ④  新机初始化      →  时区 / 依赖 / 字体 / 浏览器内核 ⑤  只读差异报告    →  新机 vs 旧机,不动任何一边 ⑥  分两次传        →  先小状态,后全量(不停机,可反复跑) ⑦  停机窗口切换    →  只切状态,不动数据 ⑧  新机验收        →  同一个审计脚本再跑一遍 ⑨  加固与保留回滚  →  防火墙 / SSH / 自动回滚;旧机不格盘 

顺序不是随便排的:①②③ 决定你会不会丢东西,④⑤⑥ 决定切换窗口有多短,⑦⑧⑨ 决定你敢不敢回滚。

下面逐个说。另外先说一句:下面每一版的清单都不是一次成型的,第四节专门讲它被补了七轮的过程。

图:九个动作,三段三色 —— 前三步决定会不会丢东西,中间三步决定切换窗口有多短,最后三步决定敢不敢回滚。

二、动作①②:盘点,然后分三层

先分类,再动手。 要搬的东西分三层:

•A 层 —— 丢不起:凭据、配置、技能、会话记录、知识库、定时任务定义。
•B 层 —— 能重装但费事:运行时、工具链、浏览器内核。体积大,但可再生。
•C 层 —— 按需:冷备、实验产物。

判定标准只有一条:能不能重建。 能重建的可以排除;不能重建的,再小也要搬。

然后重点找三类最容易漏的东西,这三类上次全漏了:

一是和主目录同级的文件。 凭据文件、 shell 配置、 SSH 目录 —— 它们不在「 Hermes 目录」里,而在它的旁边。按目录搬运的思路天然扫不到。上次的后果是每次登录报错、 20 个凭据全部重配。

二是软链的目标。 技能目录里有上百个软链,目标却在另一个兄弟目录里。软链本身就是一份依赖声明,但几乎没人把它当清单项看。上次 113 个软链断了 97 个。

三是状态目录。 比如某个抓取任务的历史基线。这类东西丢了不会报错 —— 服务照样起来,任务照样跑,只是数据静默归零,而且不可恢复。

三、动作③:写一张「默认全搬」的清单

这是整套流程里最重要的一个决定。

不要写「要搬哪些目录」,要写「根目录默认全搬,以下除外」。

原因很简单:允许清单天生会漂移 —— 你新增一个目录就忘一次。排除清单不会:忘记加,最多只是多搬一点。

第二个要求:清单必须是一个文件。 它散在多个脚本里的时候,没有任何一处能回答「到底搬什么」。这一条我是被自己打出来的:审计时只对着其中一个脚本看,得出「 11 项没覆盖」的结论,实际上其中三项写在另一个脚本里。

格式可以照抄:

[roots]/root[exclude]                    # 整目录排除,且必须写理由/root/.npm                   # 163M npm 缓存,可再生/root/.cache                 # 通用缓存(例外见 include)[exclude_glob]               # 通配排除,作用于任意深度.venv/__pycache__/*_request_dump_*.json[include]                    # 被排除目录里必须搬的例外/root/.cache/ms-playwright   # 浏览器内核,重下极慢

加一条规则:排除项必须写理由。 不写理由的排除项,就是下一个漏项。

四、但我要老实说:这张清单不是写出来的,是被事故喂大的

如果你只看到上面那张格式,会以为它一开始就设计好了。不是。

我把这次迁移的提交历史翻了一遍 —— 清单被补了七轮:

轮次
补了什么
触发它的事故
第 0 版
初始 28 条
——
第 1 轮
加一个工具目录、补参数
命令在新机上「存在但用不了」
第 2 轮
追加一条禁令:绝不整体同步 .ssh
差点覆盖目标机 authorized_keys,把自己锁在门外
第 3 轮
新建「补漏」脚本,补齐主流程没覆盖的外围
主流程跑完,发现还漏了一圈
第 4 轮
补「不可再生但不在清单里」的东西
一个兄弟技能目录没搬,97 个技能软链全断
第 5 轮
一次性加 11 条状态目录
抓取任务的历史基线被静默重置,一天后才发现
第 6 轮
清单反转(默认全搬 + 显式排除)+ 审计门
终于承认:再补下去没有尽头
第 7 轮
反转之后仍在补:补一层嵌套排除;纠正一处凭印象的错判
新机制本身也有盲区 —— 它只覆盖一级条目

看这个形状:28 → +1 → +1 → …… → +11。

前五轮,每一轮都是「出了事 → 去补一条」。这才是真正的坑 —— 不是某个目录忘了搬,而是修问题的方式:我在用「再补一条」,去修一个「清单天生会漏」的问题。

而且注意第 5 轮:一次补进 11 条。这说明漏掉的不是零星的,是整整一类 —— 只要你按目录去枚举,你就会把一整类东西整体忘掉。

所以第六轮我没再补目录,而是把清单反转了:默认全搬、显式排除、排除项必须写理由。这样下一次「补」,不再需要谁先出一次事故。

而第 7 轮是最值得记的:反转之后仍然补了两次。 一次是机制本身的盲区(新版排除只作用于根目录一级,排不掉嵌套文件);一次是我凭印象判断某个目录里大头是调试日志,实测才发现大头是会话记录本体。换了方法,不等于不会犯错 —— 只是错得更小、更容易发现。

能带走的一条:判断一个迁移方案好不好,不要看它列了多少条,要看「漏了以后,补起来要花多大代价」。如果补一条要改三个脚本、重跑一遍流程,它一定会继续漏;如果补一条只需要改一行、或者根本不用改,它才算是活的。

五、动作③续:加一道审计门

迁移之前跑,六项全绿才允许开始。每一项都对应一类真实翻车:

检查项
它抓什么
目录差集
有没有清单没提到的新条目(漂移检测)
全库断链
每个断链 = 某个外部目录没被搬
状态目录读回
状态文件能不能解析,而不是「在不在」
依赖可导入
定时任务脚本的第三方依赖,目标环境能否导入
服务与加固一致性
服务是否 enabled 、 SSH 是否仅密钥、防火墙规则文件是否在

把它理解成机场安检:它不保证飞机不出事,但它把已知的危险物品拦在地面上。

验收的诀窍:同一个脚本在新机器上再跑一遍,就是验收 —— 不需要写两套检查。

六、动作④⑤⑥:初始化、差异、分两次传

新机初始化(动作④):时区必须与旧机一致 —— 定时任务是按墙钟跑的;然后是系统依赖、中文字体(渲染要用)、浏览器内核。

只读差异报告(动作⑤):新机与旧机对比一遍,纯只读,随时可跑,不动任何一边。

分两次传(动作⑥)。传输用 rsync ,参数别省:

rsync -aHAX --numeric-ids --compress --partial --stats --human-readable --mkpath 

-aHAX 是在保留权限、扩展属性和硬链接 —— 状态文件丢权限会直接读不出来。

三条禁忌,每条都是血泪:

一、绝不整体同步 .ssh 目录。 会覆盖目标机上的 authorized_keys,把自己锁在门外。只单独同步私钥和公钥。

二、绝不直接复制正在运行的数据库。 会话库在 WAL 模式下直接 cp 会得到损坏文件。要用 sqlite3 <db> ".backup <目标>",而且要等写者停下来。

三、排除运行时残留。 -wal、-shm、锁文件 —— 跟着数据一起过去只会制造假状态。

第一次传小状态( A 层,体积小、落地快、能提早验证),第二次传全量(清单驱动)。全程不停机、可反复跑, rsync 只补增量。

七、动作⑦:停机窗口只切状态

核心原则:把停机时间压到只剩「必须停的事」。

数据在预同步阶段早就传完了,所以停机窗口里只做四件事:

一、停旧机服务。 顺序要固定。

二、切机器人。 同一个 bot 绝不能在两台机器上同时运行 —— 会互相抢占消息,丢消息是必然的。必须单向「停旧 → 启新」,回滚则是「停新 → 启旧」。

三、补最后一段增量。 从停下来到现在产生的那点变化。

四、启新机服务,核对。

⚠️ 一个很容易踩的坑:切换脚本必须在 AI 助手之外的独立终端里跑。因为助手的会话本身就是服务的子进程 —— 你把服务停了,脚本会跟着一起被杀掉,切到一半就断在那里。

八、动作⑧⑨:验收、加固、保留回滚

验收(动作⑧):审计脚本在新机复跑 + 状态读回 + 依赖可导入 + 真实发一条消息看机器人回不回。

加固(动作⑨)。新机在加固之前是「裸奔」的:密码登录开着、没有防火墙 —— 而 22 端口从开机那一刻就在被扫描(实测 fail2ban 一启用就抓到正在爆破的 IP )。顺序是:

先 fail2ban ,再防火墙,最后 SSH 仅密钥。

凡是「可能把自己关在门外」的操作,先挂一个自动回滚定时器。 这样即使配置写错、连接断了,几分钟后系统会自动恢复 —— 你还有机会回去改。

旧机不格盘,保留七天作回滚窗口。验证通过之前,旧机一个字都不删。

九、照抄清单

[ ] 1  盘点资产,分 A/B/C 三层;重点找:同级文件 / 软链目标 / 状态目录 [ ] 2  写清单:根目录默认全搬 + 显式排除(单一文件,排除项必须写理由)           └ 并且接受一件事:它一定还会被补。所以把「补起来便宜」当成硬指标 [ ] 3  跑六项审计门,全绿才允许开始 [ ] 4  新机初始化:时区与旧机一致、系统依赖、字体、浏览器内核 [ ] 5  出只读差异报告(不动任何一边) [ ] 6  第一次预同步:先传 A 层小状态,提早验证 [ ] 7  第二次预同步:清单驱动全量,不停机、可反复跑 [ ] 8  停机窗口:停旧 → 切机器人(单向)→ 补增量 → 启新 [ ] 9  新机跑同一个审计脚本 = 验收 [ ] 10 加固(fail2ban → 防火墙 → SSH 仅密钥)+ 自动回滚 + 旧机保留 7 天 

十、实测数据与边界

这次的真实数字:清单驱动传输 23 条、约 21.26 GiB;其中一个 313M 的项目,排除掉可再生的虚拟环境和会话数据之后,实际只有 3.1M 需要搬。审计第一次运行就抓出 3 个断链,其中一个来自一个早已删掉的工具。

「看起来很大」和「真正不可再生」,是两件事。

边界:这套流程适用于单机自建 agent + 自建知识库,不适用于集群、多租户或要求零中断的业务 —— 停机窗口仍然需要几分钟。另外要说清楚:审计保证的是源机器这一侧的完整,它不替代目标机器的基础初始化。


参考资料

•本次迁移实施与六项审计的真实执行记录(清单解析、审计输出、断链检查)
•自建知识库 03_ops 目录下的迁移脚本集与运维日志(含上一次迁移的复盘发现)

本文由 AI 辅助创作,作者进行了实测验证和编辑修改。

相关学习资料