Agent 时代的工作区,需要「唯一上游」
复制是债务,挂载是资产。真源要版本化,派生物要可重建,临时状态要有 TTL。
2026-09-21 我盘点 ~/Documents/AI app/,du 报 28G;到 10-02 第二轮清理结束,同一个目录 22G。省下的 6G 是最不重要的数字。盘点让我看清的是另一件事:三个 AI 端(codex、ZCode、deepseek-harness)各自为政地在同一个工作区里干活,文件的产生机制跟人写代码的时代完全不一样,而我的工作区结构从没为此做过设计。这篇记录六类症状、两轮治理,以及最后落到的原则:每个资产只允许一个上游。28G 到 22G 只是副产品。
三个端,三套垃圾生产线
三个端各有自己的落脚点:codex 用 ~/.codex 加 AI app/codex,ZCode 用 ~/.agents 加 AI app/glm,dsh 有自己的 harness 目录。会话、venv、node_modules、skill 副本,各产各的,谁也不知道对方产了什么。9-21 那次盘点我用 du 和 find 逐类过了一遍,症状集中在六类:
| 症状 | 实测数字 | 怎么测的 |
|---|---|---|
| 一份 BP 连续 24 轮迭代,历史版本全量平铺 | 约 40M | 9-21 盘点,逐文件核对迭代记录 |
| 某客户 deck 项目 20 个版本 × html/pdf/pptx 三格式 | 60+ 文件平铺在项目根目录 | 9-21 盘点 ls 统计 |
| node_modules 散落 | 15 处,共 1.34G | find -name node_modules 后逐个 du |
| 同一个 Reddit 采集脚本被复制进 7 个项目 | 7 份,md5 各不相同 | md5 逐份比对 |
| 临时工作区(candidate.pptx / .inspect.ndjson / render 目录) | 累计 330M,从不回收 | 9-21 盘点 du |
| 常驻浏览器 profile 的缓存 | 持续回长到 2G+ | 清掉后观察数日复测 |
清理时还差点出事:转录资料库两个路径指向同一批 inode(硬链接),删任何一边另一边跟着没了。此后疑似重复文件先比 inode 号再比内容哈希,两道确认才删——md5 只说明内容相同,不说明磁盘上是同一份实体。
这张表里最值得停一下的是 md5 那一行。7 份采集脚本同源,但在各自项目里被分别改过,md5 已经各不相同。任何一份里的 bug 修复都没有回流到其他 6 份——同一个 bug 要在不同副本里各修一遍。
垃圾的形态变了
人写代码的时代也有垃圾,但形态旧:临时脚本忘删、下载目录堆满、旧项目舍不得清。agent 时代多出来的东西,量和性质都变了,我把它们归成三类。
第一类是中间版本。24 轮 BP 迭代、20 个版本的 deck,每一轮 agent 都会产出一个完整落盘的产物,”落一个新文件”是 agent 保存成果最自然的方式。人不这么干活——人在编辑器里改的是同一份文件。于是”复制文件夹”退化成了版本管理,目录树里躺满了本该由 git 管的历史。deck 项目每个版本同时存 html、pdf、pptx 三种格式,一轮迭代三个文件,60+ 个文件就是这么滚出来的;真正有信息量的是”改了什么”,格式膨胀纯属导出习惯的惯性。
第二类是依赖副本。15 处 node_modules 共 1.34G(平均每处约 90M),每一处对应某个小脚本的某次”先装个环境跑跑看”。agent 执行 npm install 毫无心理负担,一条命令就落一个几十兆起步的目录,而人至少会在装之前想一下”这东西以后还用不用”。
第三类是工具分叉。那 7 份采集脚本是最典型的:方法论层面我早就把经验写成 skill 了,改 prompt、加协议、换模型都有章可循;但工程资产——脚本、管线、模板——还在以整目录为单位流浪。每开一个新项目,agent 就忠实执行”复制一份再改”,改着改着就成了 7 个平行宇宙。方法论已经 skill 化了,工程资产还没有。
两轮治理做了什么
9-21 第一轮,10-02 第二轮,28G 清到 22G。具体动作按目标分四块。
版本链:定稿区与历史归档分离。BP 和 deck 项目改成两层结构:定稿区只放当前代表版,历史版本整体挪进 归档/,归档里也只留有代表性的里程碑版本,纯格式微调的轮次直接删。40M 的 BP 平铺收敛成定稿一份加少量代表版,deck 按同样规则归档。
工具中心:一份实体,三端挂载。建了统一的工具中心目录,收编 10 个 skill 和 7 条管线作为唯一实体,三个端用符号链接挂载进各自的工作路径。重复的 skill 先做内容 diff,确认无实质差异后去重合并;那 7 份分叉的采集脚本合并回一份,各项目的差异化配置改成参数传入。此后任何一处的修复,三个端同时受益。新开项目的动作也变了:往工具中心挂一个链接,差异走参数,不再复制任何整目录。
会回长的垃圾:交给定时任务。浏览器缓存清掉还会长回来,手动打扫没有意义,改成每月自动清理的 launchd 任务。临时工作区的 candidate.pptx、render 目录,约定为”任务结束即删”,写进了各端的 AGENTS.md。
项目目录本身:29 个合并到 20 个。相当一部分”项目”其实是一次性任务的残骸——跑完的实验、废弃的方案比稿,归档或删除。合并时踩过一个坑:Python venv 绑死绝对路径,仓库搬家后所有脚本集体报错——这类东西搬目录前要先想清楚。
资产化:skill 化只是第一步
清理动作是小事,真正该写下来的是清理逼出来的那层认识:prompt 和 skill 的资产化只是第一步;代码、脚本、模板、会话和产物同样必须资产化,否则会以债务的形态回来。
什么算 Agent 时代的资产?我最后落在三分法上:
| 类别 | 装什么 | 我工作区里的对应物 |
|---|---|---|
| Canonical(真源) | prompt、skill、源码、模板、原始数据、规则 | 10 个 skill、采集脚本与 7 条管线、BP/deck 模板、转录资料库 |
| Derived(派生物) | 从真源导出的 PDF、PPTX、HTML、报告 | BP 定稿、deck 的 html/pdf/pptx |
| Ephemeral(临时状态) | render、candidate、cache、node_modules、浏览器 profile | candidate.pptx、render 目录、15 处 node_modules、浏览器缓存 |
治理规则三句:真源要版本化;派生物要可重建;临时状态要有 TTL。会话记忆在这张表里没有现成座位,按性质拆开:值得复用的结论沉淀回真源(写进 skill 或文档),其余按临时状态对待,用归档约束总量。
这张表也把「唯一上游」的含义说准了:每个资产只有一个 canonical source of truth。这里的唯一上游,不是磁盘上只能存在一个物理副本,而是只有一个副本拥有修改权和真源地位,其他副本必须是可重建、可追溯的派生物。复制本身不犯规:package 发布、git clone、CDN 缓存、backup、构建产物、容器镜像,全是复制,全都合理,因为真源仍只有一处,改动后随时能重新生成。犯规的是让副本悄悄拿到修改权——7 份分叉脚本的问题不在存了 7 份,而在 7 份都能改,谁也不是权威。
复制文化和资产文化的分界线也在这里:复制一份再改,是债务;挂载引用,才是资产。债务不体现在磁盘上——7 份分叉脚本没占多少空间,占的是我之后在每一份副本上重复花掉的修复时间,而且没有提醒机制,我并不总能记起其他副本的存在。回头看 6G 的构成是同一个结构:大头是 node_modules 和临时区这类”装了就忘”的体积,版本平铺占的绝对量有限,但占空间小的那几类反而更贵。
对照这张表,两轮清理做到的:真源侧,skill 收进工具中心、脚本合并回一份;派生物侧,构建产物交给两层结构加 git。会话记忆是现成的反例:三个端格式互不兼容,只能定期归档约束总量,归档本身又长出新的历史副本。模板和数据集还没有上游,先记在账上。
唯一上游:一条可以搬走的原则
两轮清完,治理经验压成一条原则:每个资产只允许一个上游。工具一份实体加挂载,是它在空间上的体现;版本进 git,是它在时间上的体现——历史由 git 管,目录树只放当前态,BP 再迭代,两轮之间改了什么,git diff 一屏看完,这在 40M 平铺文件夹里做不到,那里只有 24 个沉默的完整版本;临时产物用完即删,是承认这类文件根本不配上游。原则管不到的,比如浏览器缓存,就承认它会回长,用定时任务兜底;2G 的峰值说明缓存增长率本身没降。
这条原则不绑定我的目录结构。工具中心加符号链接加 git,只是我这套三端并行场景的解法;换成 monorepo、内部包源、共享盘,实现形式随便换,三条规则不变:真源是不是只有一处,派生物可不可以重建,临时状态有没有 TTL。落到日常是一个自检:任何新文件落盘之前问一句,它是真源、派生物,还是临时状态;想删任何一份”重复”文件之前,确认它是可重建的派生物,不是别处真源唯一的实体。
22G 不是可以宣布胜利的数字。下一轮两件事:给工具中心的挂载加完整性校验,防止某个端哪天又悄悄复制出本地副本;把”临时产物清单”做成脚本化的白名单,到期自动删,不再依赖各端 agent 的自觉。这两件事还是在守同一条原则:垃圾不是打扫掉的,是设计掉的。