‹ 返回笔记 · Back to notes

现场手记

12 套 OpenClaw 副本之后:路径和根目录是怎么变成风险的

Twelve OpenClaw Copies Later: When Paths and Root Directories Become Risk

扫了一下 home 目录,发现 12 套带 openclaw 字样的根。只有 2 套真在跑,剩下 10 套是历史残片。副本不危险,被当成真根用才危险——五种典型风险 + 五步处理原则。

OpenClaw路径治理副本管理系统审计public-safe
水彩手绘:桌面上一排资料,被铅笔圈出真相源的位置

OpenClaw 路径治理手记

某天下午我闲着没事,敲了一行 find,想看看 home 目录里到底有几个带 openclaw 字样的根目录。屏幕上慢慢滚出来一长串,我数了一下,整整 12 套。

那一刻不是惊讶,是有点恍惚。我心里清楚生产上真正在跑的是哪几套——一套挂着虚拟公司,一套挂着纪嫣然——加起来 2 套。剩下的 10 套,每一套都还带着 openclaw 这个名字、还有一份看起来完整的目录结构、还有配置文件、还有 .env、还有 token、还有过去某个时间点我亲手写下的 README。它们都还在那里,安静地占着磁盘,没坏,也没人在用。

问题不是"我攒了 10 套垃圾"。问题是这 10 套副本里,有几套曾经是真根。它们被新版替代的那一刻,并没有自动从磁盘上消失;我也没有主动去删——每删一套都得先确认"现在没有任何调用方还指着它",确认这件事本身就很麻烦。它们就这么留了下来,时间一长就堆成了 12 套。

我以前总觉得,AI 项目里最大的风险是新功能跑不通、是模型抽风、是 prompt 写错。做久了我才明白,活到一定年龄的项目,最大的风险是旧副本还在跑——你不知道哪一份是真的,AI 不知道,调用方更不知道。每一份副本看起来都长得像真根,每一份副本里的配置看起来都能用,每一份副本里的 token 都还在有效期里。这不是垃圾问题,是真相源(canonical source / 权威来源)问题。

12 套根目录中只有 2 套是真根,其余 10 套是历史残片

这 12 套是怎么慢慢攒出来的

我后来把这 12 套按性质分了一下,发现它们不是一次性堆出来的,是这一年里我自己一锹一锹挖出来的。每一锹挖的时候我都觉得是必要的,每一锹挖完我都没顺手填上,于是就攒成了今天这个样子。

最上面 2 套是 authoritative root(权威根目录 / 真根):一套是虚拟公司的生产根,挂着审批、调度、agent 配置;另一套是纪嫣然的生产根,挂着她自己的 MCP(Model Context Protocol,模型上下文协议)服务、声音工作台、本地状态。这两套是当前真在跑的,所有有效流量都打在这里。它们本身没问题。

顺便说一句,"OpenClaw"这个关键字在我机器上覆盖的远不止这 12 个根。如果把搜索范围从根目录扩到所有 .openclaw* / .clawdbot / .clawai 这种系列前缀,再加上各种带 openclaw 字样的子目录、配置缓存、运行时日志、agent 身份文件,整台机器上能扫到 13 个独立目录、几百个相关文件。我只盯根目录这一层——根目录是治理意义上的"地基",其他层都附着在根上。地基理顺了,上面的东西才能跟着理。

问题出在下面这 10 套。

第 3 套是旧主配置残片,曾经是真根,被新版接替以后就留在那里没动。它的目录名几乎和现在的生产根一样,少了一个后缀;它的 .env 里还有去年某个时间点拿的 token;它的 scheduler 配置文件还活着,只是没人调用了。这是最危险的一类副本,因为它和真根长得最像。

第 4 套是拼写错误的残片:某天我手抖,把 openclaw 敲成了 openclag,回车下去就建了一个空目录,后来又顺手往里塞了几个测试文件。这种残片最容易识别——名字本身就错的;但也最容易被忽略——我自己都忘了它存在。

第 5 套是实验副本,挂在某个第三方平台的 projects 目录下面。当时我在那个平台上跑过一阵子扩展实验,把 openclaw 整个目录复制了过去做适配测试。实验结束以后,整个副本就留在那里,连同它自己那一份带着真实 token 的配置一起。

第 6 套是审计副本。某次做 audit(审计)的时候,为了不污染生产根,我把整套目录复制到了 Desktop 上的一个隔离目录里跑审计脚本。脚本跑完了,审计报告也出了,但那份复制出来的副本没人提醒我删,就这么留了下来。

第 7 到第 10 套是 4 份历史版本,命名都带着版本号——v3_openclaw_agents 这种格式。它们是我做大版本切换的时候留下来的"以防回滚"备份;切换稳定以后我没回过头去清理,每一份都还在原地,目录名清晰、内容完整、再也没被打开过。

第 11 套是归档副本,藏在年度归档目录的深处,是某次整理硬盘的时候我把整个 openclaw 主目录整体搬过去的。当时是为了腾空间,但搬过去之后我又在原地重新建了一套——我"不放心归档目录"——结果归档区也留下了一份完整副本。

第 12 套是压缩备份,一个 tar.gz 文件,藏在某个 cleanup-backup 目录里。这个我连什么时候打的包都想不起来了,文件名里带着一个日期,打开来看是去年某个版本的完整快照。压缩包最讨厌的地方在于它和目录不一样——它不会被普通的 find 直接列出来,要主动加 *.tar.gz 这种 pattern 才能扫到。也就是说,一个项目治理到一半,最容易漏的就是这种压缩态副本。它没有目录结构,它不出现在 ls 里,它只在某次磁盘清理的时候才会被想起来。

把这 10 套挨个数完,我才意识到:每一类副本背后都有一个当时合理的理由。备份、审计、实验、回滚、归档、手抖——没有一个理由是错的。错的是我每次留下副本的时候,都没有顺手做一件事——给它打一个标签,说明它是什么性质、什么时候过期、谁有责任清。它们以"我以后再处理"的姿态留了下来,"以后"却一直没来。

副本本身不危险,副本被当成真根用才危险

如果这 10 套副本只是安静地占磁盘,问题其实不大。一年攒 10 个旧目录,对一台 4T 的工作机来说不算什么。

真正的风险在另一边:副本被当成真根用了。

我最近就撞上了两件事。一件是纪嫣然的 MCP 路径,配置里还指向旧的 .openclaw——也就是第 3 套那个旧主配置残片。这个路径已经失效了一段时间——它从一个老的 config 里继承下来,新版生产根上线时没人重新校对过,MCP 调用走的还是老路径。表面上一切正常,旧目录里那份配置文件还在、还能加载;实际上它读出来的是过期的 agent 设置。这就是"副本被新调用方误用"的典型形态。

另一件是虚拟公司的 scheduler(任务调度器)。它在跨根调度纪嫣然和共享工具,即公司的根目录里那个调度器,会跑到另一个根目录里去触发任务。这本来是中性的——跨根调度不一定是错——但问题是,调度器走的几条路径里混着旧副本的路径。也就是说,公司这边的调度器,可能在某些 case 里调到的是副本里的脚本,而不是真根里的最新脚本。这是"副本之间互相引用"的典型形态。

这两件事都没造成事故,但都让我看见了一件事:副本被当真根用,是一个无声的失败模式。它不会在某个早上突然炸掉,它会以"明明设置改了为什么没生效"、"明明 bug 修了为什么还在复现"、"明明 token 轮换了为什么旧 token 还能用"这种小困惑慢慢出现。每一次困惑单独看都不致命,连起来看就是一条治理失控的曲线。

更麻烦的是 AI 也分不清。我让 AI agent 去做事,它会顺着配置文件里的路径去找——它不知道哪个路径是真根、哪个是副本。它只看路径合法、目录存在、文件可读,就当真材料用了。这个时候 AI 越听话,副本污染的范围就越大。AI 不是问题的来源,但它会忠实地放大问题。

还有一种更隐蔽的形态:副本互相调用。当 home 目录里同时存在多套 openclaw,它们曾经都是某个时间点的真根,每一份都带着自己那个时代的依赖图。今天我打开第 5 套副本的某个脚本,它可能 import 了第 3 套副本里的一个工具;我打开第 7 套副本的某个 config,它可能指向第 11 套副本里的某个模板文件。这种交叉引用不是我设计出来的,是历史层堆叠出来的副作用。每一次新版本切换时,我都尽量把它和旧版本切干净,但"尽量"不是"全部"——总有几条边漏在外面。漏的这几条,过两年回头看就是一张让人头皮发麻的关系图。

副本带来的 5 种典型风险

把这一年我和这 12 套副本打交道的所有麻烦摊开来看,可以归成五类风险。

第一类是真相源不清。哪一份是 authoritative root?人不一定记得清,AI 更不知道,调用方只看路径不看语义。当 home 目录里同时存在 12 套带相同关键字的根,"真"这件事就不再是默认状态,需要每次主动确认。一个项目活到这个阶段,最沉重的认知负担不是新功能,是"我现在动的是不是真根"。

第二类是凭证扩散。每一套副本可能都带过 token、key、.env。它们当初是合法的、是配过权限的、是真能用的。当副本留下来,凭证也跟着留下来。你以为你只有 2 套生产凭证要管,实际上你有 12 套。哪一天某个旧 token 被泄漏,溯源会指向某个你早已忘了的 archive 目录——这种事故的修复难度,远高于"线上服务出 bug"。

第三类是旧依赖回潮。副本里的代码还在引用一些已经失效的服务——某个早就下线的内部 bridge(桥接服务)端口、某个不存在的本地 SearXNG 实例。这些引用平时不出现在生产路径上,但只要有任何一次调用走偏到副本里,就会立刻命中失效依赖。错误日志会出现一些你早已不记得是什么的端口号,调试链条要往回追半年。

第四类是审计无效。这是我最近才想清楚的一件事。如果 audit 跑的是副本,结论不能代表生产;可如果 audit 没说清楚自己跑的是哪一套,得出的结论看起来又长得像生产结论。审计本来是为了让我对系统更有信心。结果 audit 一旦跑在副本上,它反而让我对错误的状态更有信心。这是治理学上最糟糕的反馈方向。

第五类是维护成本指数级。副本越多,每做一次治理动作(比如轮换密钥、升级依赖、改路径约定)都要乘以副本数量。2 套真根做一次升级,能在一个下午搞定;12 套全部走一遍,要花一整周,还得一份份判断"这套要不要跟、不跟会不会留隐患"。维护成本不是线性增加的,是指数级增加的——每一套副本都和其他几套有一些隐含的引用关系。

这五类风险有一个共同点:它们都不是技术风险,是治理风险。技术风险可以靠写更好的代码消解,治理风险只能靠制度——给每一份材料定责任、定边界、定生命周期。我以前花在前者上的时间是后者的十倍,做到这一年才意识到,治理这件事的隐性成本,远远超过任何一次单点的技术债。

我的处理原则:不直接删,先标

最容易的反应是"那就一次性全删呗"。我一开始也想这么干,但很快就劝住了自己——一旦真删下去,我就再也没有机会做溯源了。直接全删等于把治理问题转成数据丢失问题,账没算清就把账本烧了。

所以我现在走的是五步:识别、止血、迁移、归档、删。每一步都有自己的边界,不要跳。

第一步是识别。把所有副本扫出来,每一份打一个标签:authoritative(真根)、legacy(遗留 / 历史)、experiment(实验副本)、audit-copy(审计副本)、archive(归档)。标签不是装饰,是责任——一旦贴了 legacy,意思就是"这一份未来要被收掉,新代码不能再引用它"。一旦贴了 archive,意思就是"这一份只读,谁都不要再往里写"。

第二步是止血。确认没有新代码再引用副本路径。这一步要 grep 一遍代码库、grep 一遍配置文件、grep 一遍 LaunchAgent(macOS 后台服务)和 plist、grep 一遍 cron。任何一个还在引用副本的地方都要列出来。不止血就迁移,等于一边搬路一边还有车在跑老路上,迁完路才发现还有半个车队漏在外面。

第三步是迁移。把还指向副本的引用全部改成指向 authoritative root。这一步要小心改,每改一处就跑一次回归——尤其是涉及 MCP 路径、scheduler 配置、bridge 调用这几类核心入口。改完之后做一次烟囱测试:从最上层入口触发一次完整流程,看是不是所有调用都正确走到了真根。

第四步是归档。确认零引用之后,副本搬到 archive 区。archive 区是只读的、是带时间戳的、是和生产路径完全隔离的。搬过去之后要在原位置留一个 README,说明这一份去了哪、什么时候去的、为什么去。不能直接删原位置——直接删会让 AI 和我自己都失去线索。

第五步是删。archive 区呆够 3 个月,期间没有任何人、任何调用方来找它,才真删。这个 3 个月不是拍脑袋——是我观察自己工作模式得出的:一个失效的引用,最长会在 3 个月内被某次回归测试、某次审计、某次"咦那个东西去哪了"的疑问触发出来。3 个月平安无事,基本可以判定它真的不再需要。

五步走下来,慢,但不可逆的动作放在最后。前四步都是可逆的——标签可以改、引用可以回滚、归档可以搬回来。只有第五步删是不可逆的,必须在前面四步全部完成、并且时间证明过的前提下才能做。

这五步还有一个隐含的设计:它强迫我把"治理意图"和"治理动作"分开。识别和打标签是意图——我先表达"我打算怎么处置这一份",再用后面四步把意图转成动作。意图和动作分开的好处是,AI 也能进来帮忙。我可以让 AI 去 grep 引用、可以让 AI 去跑回归、可以让 AI 去搬归档——但前提是标签已经定好。标签是人的责任,不是 AI 的。AI 不能替我决定"这一份算 legacy 还是 archive",但只要这件事我定了,后面的执行 AI 可以做大半。

为什么不直接全删:副本是证据,不是垃圾

我后来意识到,对待这 12 套副本的态度,决定了我对整个项目的认知方式。

如果我把它们看成垃圾,那答案很简单——一句 rm -rf 就完事,省下几十 GB 的磁盘空间,桌面清爽。但如果我把它们看成证据,事情就完全不一样。每一套副本都是一段历史的物证:第 3 套告诉我"曾经的生产根长这样";第 5 套告诉我"我在某个第三方平台上跑过这一类实验";第 7 到 10 套告诉我"我曾经做过 3 次大版本切换,每一次都留下了完整的回滚快照";第 12 套告诉我"去年某天我对系统状态足够不安,所以打了个完整 tarball"。

这些证据有什么用?在三种场景下都有用。

第一种是溯源。某天发现一个奇怪的 token 在外面被人用了,要回到当时这个 token 是为哪个项目配的、是在哪个版本里生成的——副本是唯一能回答这个问题的来源。生产根早就把这个 token 轮换掉了,它不记得自己的过去。

第二种是回滚。某次新版本上线之后某个 agent 行为变得很奇怪——是不是新版引入的?副本里保留的旧版完整状态可以拿来做 A/B 对照,几分钟就能定位。如果副本都删干净了,A/B 对照就要从 git 历史里重建运行环境,那是几天的工作。

第三种是审计可信度。外部 audit 经常会问"你是从什么时候开始这么做的"、"上一个版本的设计是什么样的"。这种问题不能靠记忆回答,要靠物证。一套带时间戳的归档副本,是审计学意义上最干净的回答。

所以直接全删,本质上是用空间换时间——用磁盘上的清爽,换未来溯源、回滚、审计时的失语。这笔交易在 home 目录小的时候看起来划算,在项目活到一定年龄的时候就完全不划算了。

分类保留才是治理。它要求我承认一件事:副本和生产根是两种不同性质的存在,它们需要不同的对待方式。生产根要保持流量、要持续维护、要严格控制谁能改;副本要打标签、要止血、要冻结、要在适当时机消亡。混在一起看,副本就是垃圾;分开看,副本是项目自己的考古层。

我现在的进度:标了一半,真删一套都没做

把所有这些原则写下来很容易,做下去很慢。

截止今天,这 12 套副本里的处理状态是这样的:2 套生产根之外的 10 套,我标了 5 套是 legacy(旧主配置残片、拼写错误残片、3 份历史版本),3 套是 archive-only(年度归档副本、压缩备份、其中 1 份历史版本变更幅度最大,值得保留),还有 2 套在 review(实验副本和审计副本,需要先确认带的 token 是不是已经全部轮换)。

止血也只做了一半。代码库 grep 过一遍,配置文件 grep 过一遍,但 LaunchAgent 和 plist 那一层还没扫干净——这是 macOS 上最容易漏的一层——它们藏在用户和系统两个目录下,命名规则也不统一。

迁移工程还在排队。纪嫣然 MCP 路径指向旧 .openclaw 这件事我已经知道了,但 owner 还没放行改动——这一改要重启 MCP 服务,要重新做一次回归,改之前还得先把 MCP 当前的运行状态完整快照下来。公司 scheduler 跨根调度的事更复杂,要先把所有跨根调用的清单列出来,再决定哪些保留、哪些收回。这两件事都不是一个下午能搞定的,它们要排进未来几周的工程节奏里。

归档区还没搭。我现在只是把判定为 archive-only 的几套副本贴了标签,但还没真正搬到一个独立的、只读的、带时间戳的归档目录里。这一步要等止血和迁移都完成才能做——搬完之后原位置就只剩一个 README,如果还有调用方在指原位置,那个调用方会直接失败。

真删,一套都没做。最早能进入"真删"流程的副本,按 3 个月观察期算,也要到秋天才轮得到。

我对这个进度不焦虑。一年攒出来的 12 套副本,不可能用一周就清完——也不应该。如果我用一周就清完了,那说明我跳过了某些步骤;跳过的步骤迟早会以另一种形式回来找我。

做这件事的过程里我反复想到一句话:项目活到一定年龄,路径就变成了风险。

新功能没跑通是看得见的风险,旧副本还在跑是看不见的风险。看得见的风险逼着你解决,看不见的风险纵容着你拖延。我现在不再追求一个干净的 home 目录,我追求的是一个能说清楚每一份副本性质、每一条引用归属、每一个凭证生命周期的 home 目录。前者只是好看,后者才是真治理。这 12 套副本,我还在慢慢收口。

把这篇记录接到下一步

读完以后,可以继续追问这篇文章,也可以回到策展目录,或通过标签追同一条线索。

追问这篇 回到目录 浏览标签