> For the complete documentation index, see [llms.txt](https://weekly-essay.mikey.wang/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://weekly-essay.mikey.wang/zh/2026-nian/w32builder-de-mac-ye-xu-yao-yi-ge-sweeper.md).

# W32-Builder 的 Mac，也需要一个 Sweeper

我有三台 Mac。现在承担主力工作的是一台台式 Mac Studio。这台 Mac Studio 使用 M2 Max 芯片，配备 12 核 CPU 和 64GB 内存，外接一台 5K 显示器和一支舒尔 MV7+ 麦克风。

硬件资源比较充足，日常瓶颈更多来自软件环境。Codex、Claude Code、ChatGPT、Antigravity 和 Multica 长期并行，背后还会启动浏览器、MCP Server、sidecar、沙箱虚拟机和各种子进程。Node、Python、Rust、代理、Skills、启动项和缓存也在持续变化。

最近一次环境整理从一个小错误开始。每天自动运行的 Skill 治理任务报了 `ModuleNotFoundError: No module named 'tomllib'`。排查后发现，交互式终端和定时任务调用了不同的 Python。Mac 上同时存在系统 Python、Homebrew Python、python.org 安装的 Python 和 conda。类似问题也出现在 Node 和 Rust 上。

这些局部问题叠加后，环境会逐渐失去解释性。命令偶尔指向旧版本，后台进程的归属不清楚，同一个任务在终端里成功，在定时任务里失败。Agent 执行得越快，环境漂移得也越快。

我现在把 Mac 看成 Builder 的生产系统。它需要持续治理。

### Builder 也需要承担 Sweeper

Boris Cherny 最近把 Claude Code 团队里的工作归纳为五种角色，包括 Prototyper、Builder、Sweeper、Grower 和 Maintainer。Builder 把原型变成可用于生产的产品和基础设施。Sweeper 清理界面、简化系统、撤掉失去价值的功能，并优化性能。[Boris 的原帖](https://x.com/bcherny/status/2071379474277613732)给出了不同产品阶段的角色组合，Sweeper 出现在每一个阶段。

这很符合我最近的感受。

Agent 天然擅长增加。它可以安装新工具、生成新代码、添加新配置、创建新的 Skill。增加的边际成本已经很低，删除和收敛仍然需要判断。哪些东西已经没有价值，哪些副本应该合并，哪个运行时应该拥有最终入口，哪些自动化动作可能误删有效状态，这些问题都需要 Sweeper。

对个人 Builder 来说，Sweeper 的对象也包括本地环境。进程、缓存、运行时、代理配置、启动项、Skill 和 Agent 配置都是资源。它们存在依赖，也会随着使用不断漂移。

我对环境干净的定义有四点。关键入口有明确所有者，正在运行的东西可以解释，漂移能够被发现，系统能够回到已知状态。

### Mac 上有哪些资源

我的主要工作面包括 Codex、Claude Code、ChatGPT、Antigravity 和 Multica。终端侧有 Ghostty 和 cmux。知识层主要使用 Basic Memory 和 Craft。Craft 保存适合人阅读的笔记、文章与整理结果。Basic Memory 保存稳定结论、可复用流程和 Agent 可以持续检索的上下文。

浏览器和自动化层会启动 Chrome、Playwright 与 MCP Server。公司安全软件、代理工具、输入法、同步服务和应用更新器也会长期驻留。

一个桌面应用通常对应一组进程。Claude Desktop 会启动自己的沙箱虚拟机，Codex 会启动 sidecar，浏览器会拆出 Renderer 和 GPU 进程，Agent 工具会拉起 Node、Python、MCP 和浏览器子进程。只看活动监视器里的单个进程，很难理解真实资源占用。我的盘查方式会按应用家族聚合主进程和 Helper，再结合 `memory_pressure`、swap、CPU 增量和网络连接判断是否异常。

这些资源会发生四类漂移。

第一类是进程和存储漂移。会话已经结束，子进程仍然存在。下载缓存、日志和构建产物持续增长。宿主应用也可能反复拉起已经回收的进程。

第二类是配置和网络漂移。系统代理、shell 环境、GUI 启动环境、应用自己的代理设置、DNS 和认证状态可以各自变化。

第三类是运行时漂移。Homebrew、官方安装器、版本管理器和旧项目会分别安装 Node、Python 或 Rust，PATH 最终决定谁生效。

第四类是 Agent 能力资源漂移。Skills 被不同工具安装到不同目录，实体副本逐渐分叉，软链断掉，上游发生更新，frontmatter 或硬编码路径开始失效。

前三类影响程序能否稳定运行，第四类影响 Agent 能否调用正确能力。它们属于同一个治理问题。

### 代理是分层环境

代理环境是我踩坑最久的一层。

macOS 上存在几条相互独立的代理路径。系统网络代理影响愿意读取 macOS 设置的应用。`~/.zshrc` 里的代理变量只影响当前 shell 及其子进程。从 Finder、Dock 或桌面应用启动的进程通常继承用户 `launchd` 的环境。部分浏览器和 CLI 还有自己的代理设置，TUN、DNS 与认证状态又位于其他层。

我遇到过一个典型问题。终端里的 Antigravity CLI 已经可以正常调用，Multica 中同一个 runtime 仍然返回区域不可用。最初的修复写在 `.zshrc` 函数里，所以只覆盖了交互式终端。Multica Desktop 从 GUI 启动 daemon，不读取 `.zshrc`，流量最终走了直连。

真正有效的证据来自运行中的进程。Multica daemon 的环境里没有代理变量。直接请求和经本地代理请求得到的出口区域也不同。最后我把代理逻辑放到可执行文件入口的 wrapper 中，再用干净环境模拟非交互式启动，确认实际调用路径已经生效。

这次排查形成了一条稳定规则。我需要验证目标进程，不能只验证自己的配置印象。

同一个工具可能从终端、桌面应用、定时任务、MCP 或另一个 Agent 中启动。名字相同，运行环境可能完全不同。

### 一个运行时只能有一个所有者

那次 `tomllib` 报错最终暴露出四套 Python。conda 会自动激活 `base`，Agent 在用户没有意识到的情况下把包装进了全局环境。python.org 安装器向 `.zprofile` 写入自己的 PATH，Homebrew 保留了其他 Python，非交互式任务又可能优先命中 macOS 自带版本。

Rust 也存在两套。Homebrew 安装的 Rust 在 PATH 前面，rustup 管理的版本长期没有更新。Node 的问题来自 npm global prefix。fnm 切换 Node 版本时，全局 npm 安装目录也会变化，但旧的 Claude Code wrapper 一直硬编码到 Homebrew 路径。执行入口和更新入口指向两个地方，命令可以运行，却收不到正确更新。

清理之后，我给三个核心运行时分别指定了唯一所有者。

| 运行时    | 所有者                                                    | 主要职责                           |
| ------ | ------------------------------------------------------ | ------------------------------ |
| Node   | [fnm](https://github.com/Schniz/fnm)                   | 管理 Node 版本，按项目切换               |
| Python | [uv](https://docs.astral.sh/uv/guides/install-python/) | 安装 Python、固定项目版本、创建环境和管理依赖     |
| Rust   | [rustup](https://rust-lang.github.io/rustup/)          | 管理 stable、beta、nightly、指定版本与组件 |

系统自带的运行时继续保留。它们属于 macOS，不进入我的日常 PATH，也不承载个人项目依赖。Homebrew 因其他 formula 依赖而安装的语言运行时可以继续作为内部依赖存在，但不应该抢占用户入口。

Homebrew 适合管理系统级软件包、稳定 CLI、GUI App 和底层依赖，例如 `ripgrep`、`jq`、`ffmpeg`，以及 fnm、uv 这类版本管理器本身。一个工具只需要当前稳定版本，也没有项目级切换需求，就适合交给 Homebrew。

以下场景应该直接使用 fnm、uv 或 rustup。

1. 多个项目需要不同的 major 或 minor 版本。
2. 项目需要通过版本文件固定运行环境。
3. Rust 需要 stable、beta、nightly 或指定日期的工具链。
4. Node 的全局包会随着版本变化而改变 prefix。
5. Python 需要隔离项目依赖，避免污染系统或全局环境。
6. Agent、定时任务和非交互式 shell 必须解析到同一个可验证入口。

我的规则很简单。Homebrew 管软件包，版本管理器管语言，项目文件管版本，PATH 只暴露一个默认入口。

清理旧运行时前，先更新并验证新的 owner。Rust 的迁移先完成编译测试，再删除 Homebrew Rust。Python 的迁移先确认 uv 的默认入口能在干净环境中工作，再处理 conda、python.org 和 Homebrew 的重复安装。这个顺序保证治理过程不会破坏当前工作能力。

### 两个持续治理的 routines

Boris 还反复强调，长时间运行的 Agent 必须能够验证自己的工作。对于很长的任务，可以让后台 Agent 负责验证，也可以用 Stop hook 做确定性检查，再把失败结果交回 Agent 继续处理。他把验证反馈视为提升结果质量最重要的手段之一，并给出了两到三倍的经验判断。[原帖](https://x.com/bcherny/status/2007179861115511237)

后来他把工作方式概括为从源码到 Agent，再从 Agent 到持续运行的 loop。人的工作也从逐轮写 prompt，转向设计能够自己推进的循环。[Boris 在 Meta @Scale 的对谈](https://www.youtube.com/watch?v=Z47vatpsGPI)里把 loops 称为一次同等重要的跃迁。

一个有效的 loop 至少需要明确目标、当前状态、验证反馈和停止条件。Agent 每轮执行后读取结果，验证失败就继续，目标完成或触发预算与风险边界时停止。Sweeper 提供持续做减法的方向，Loop Engineering 让这件事反复发生。

我在 Claude 里建立了两个 routines，把前面的治理原则落到日常运行中。

第一个每天治理 Skills。所有可复用 Skill 进入同一个受 Git 管理的事实源，Claude Code 和 Codex 通过软链引用。routine 自动修复断链、空壳目录、缓存垃圾和缺失软链。遇到实体副本、孤儿 Skill、frontmatter 错误、上游更新或凭据风险时，它只分析和报告，最终决定留给我。

第二个每周治理 Mac 的基础资源。它先采集内存、CPU、swap 和进程状态，再按应用家族聚合资源，识别已经确认的残留模式。进程回收需要同时满足存活超过门槛、CPU 空转和没有网络套接字。缓存清理只覆盖可重建内容，应用私有数据和归属不清的资源只测量和报告。

两个 routines 都遵循相同边界。脚本处理无信息损失的确定性动作，模型分析需要判断的项目，人负责删除、覆盖和扩大权限。

每轮执行后的新发现还会写回 Skill、reference 和 Basic Memory。下一轮读取更新后的事实源，治理能力因此持续积累。

### 持续干净是一种工程能力

过去我把 Mac 环境配置理解为一次性工作。安装软件，配好 shell 和代理，然后开始使用。

现在的变化频率已经高得多。Agent 会安装依赖、启动服务、创建缓存、生成 Skill、修改配置。Node、Python 和 Rust 持续升级，桌面 App 和 CLI 拥有不同更新链，代理和认证也会沿着不同启动路径传播。

Mac 需要自己的控制面。它要知道当前有哪些关键资源、谁拥有它们、目标状态是什么、如何发现漂移、哪些动作可以自动执行，以及哪些决策必须停下来。

Sweeper 负责做减法。Loop 负责让减法持续发生。

这种工作短期看不到新功能，却会降低未来每一次执行的不确定性。Agent 不再随机命中旧版本，定时任务和终端更容易得到一致结果，故障可以快速定位到具体环境层，新的工具也知道应该接入哪个入口。

对 Builder 来说，这是一种会复利的基础设施投资。环境越稳定，Agent loop 能运行得越久。所有权越清楚，自动化可以获得的权限越大。治理记录越完整，下一次排障需要重新发现的东西越少。

未来的生产力会同时来自更强的模型、更好的工具和一个长期可解释、可收敛、可治理的本地环境。

Builder 负责创造。

Sweeper 让创造不会被自己的残留拖慢。

### 参考资料

* [Boris Cherny 关于 Claude Code 团队五种角色的原帖](https://x.com/bcherny/status/2071379474277613732)
* [Boris Cherny 关于长时间任务与验证反馈的原帖](https://x.com/bcherny/status/2007179861115511237)
* [Fireside Chat with Boris Cherny, Head of Claude Code](https://www.youtube.com/watch?v=Z47vatpsGPI)
* [Homebrew Formulae Versions](https://docs.brew.sh/Versions)
* [fnm（Fast Node Manager）](https://github.com/Schniz/fnm)
* [uv 的 Python 安装与管理文档](https://docs.astral.sh/uv/guides/install-python/)
* [uv 的 Python 版本文档](https://docs.astral.sh/uv/concepts/python-versions/)
* [The rustup book](https://rust-lang.github.io/rustup/)
* [rustup 的 Toolchains 文档](https://rust-lang.github.io/rustup/concepts/toolchains.html)
