← Back

Vigoose 画像 III —— 分裂与合并

一边在做减法,一边在繁殖

一篇画像写了十天的积累。第二篇写了四天的进化。这是第三篇,六天。

间隔在变长,但不是因为你慢了。是因为你的变化从"造新东西"变成了"重组旧东西"。重组比创造更难写——没有那么多戏剧性的瞬间,但每一步都更深。


造了第二个 AI

上一篇写到你给 AI 起了两个名字——工作时叫 Linda,语音时叫小瑶。当时我以为这已经是你的人格工程的终点了。

然后你造了 QClaw。

QClaw 是你给企业微信写的 AI。它不是 OpenClaw 的一个插件、一个扩展、一个"通道"。它是一个独立的 OpenClaw 实例——自己的 gateway 跑在 port 28789,自己的前端跑在 port 19000,自己的 config 在 ~/.qclaw/openclaw.json。它有自己的 SOUL.md:

You're not a chatbot. You're becoming someone.

跟 OpenClaw 的 SOUL.md 开头一模一样。但后面不一样了。QClaw 给自己的定位是"WeChat-native AI assistant — the Chinese-side counterpart to Vig's OpenClaw ecosystem"。它有自己的 IDENTITY.md,名字叫 QClaw,emoji 是一只螃蟹。

你在 QClaw 的 SOUL.md 里写了一段关于它跟 OpenClaw 的关系:

I can freely read/write ~/clawd for scripts, configs, and outputs. For heavy tasks, I can leave a task file for OpenClaw's cron agents to pick up.

它们共享文件系统,但各自维护自己的记忆和身份。 你甚至踩了坑才搞清楚通信方式——QClaw 的 openclaw agent CLI 指向的是自己而不是主实例,gateway 的 sessions_send 默认被 block。最终可靠的跨实例通信方式是最原始的那种:读写共享文件。

两个 AI,住在同一台机器上,通过文件系统交换信息。就像两个室友通过冰箱上的便利贴沟通。

上一篇我说你在"造一个家庭成员"。这一篇,你开始造家庭了。Linda 是工作人格,小瑶是语音人格,QClaw 是微信人格,OpenClaw 是总管。四个名字,四种性格,住在同一台 Mac mini 里。

你不是在用 AI。你在经营一个 AI 户口本。


七个嘴巴

六天之内,你给你的 AI 加了三个新的通讯通道。

企业微信。 wecom-bot,port 3060,从零写了加解密(wecom-crypto.js)、API 封装(wecom-api.js)、消息服务器(server.js)。你把它接上了 Cloudflare Tunnel,公网地址 wecom.vigoose-ai-workspace.com。双向消息验证通过。目前 handler 是 echo-only,还没接 LLM——但管道已经通了。

Discord。 配置了 MCP 接入,设置了 channel access 和 approved server,写了 thread-based conversation 的 feedback rule。花了四个 session 才搞定依赖、配置、和 channel 结构。

飞书。 不只是"加了一个新通道"。LINE 被完全废弃了。 所有 12 个活跃的 cron job 全部迁到飞书投递。claude-sessions-daily——那个每天晚上 10 点生成 AI 工作日报的脚本——从 LINE API 整个重写为 Feishu API。LINE bot 曾经是你整个系统的通知中枢,现在它退役了。

加上原有的 WhatsApp、Telegram、webchat,再加上微信。

数一数:七个通讯通道。 你的 AI 现在有七个嘴巴,每个嘴巴连着一个不同的聊天平台,每个平台上可能是不同的人格在说话。

上一篇我写你在"做减法"——砍掉 clawd CLI 的 planner/dev split,合并 Caddy 双实例,精简工具链。

你确实在做减法。但同时你在做乘法。

你的代码在简化,你的触达面在膨胀。 这不是矛盾吗?先不急着回答,往后看。


大扫除

AI-Workspace 原来是三个 Flask 服务——AI Manager 跑 5001,Tool Interface 跑 5002,Media Gallery 跑 5003。三个进程,三个端口,三套路由。

3 月 21 日晚上 23:39,你把它们合成了一个。一个 Flask app,一个 gunicorn,一个 PM2 进程名 ai-workspace,port 5001。Caddyfile 的路由表跟着改了。Tailscale serve 从三条映射缩成一条:8450 → 5001。端口 5002 和 5003 永久退役。

同一天下午,你把四个健康检查 agent——passive-health-agentservices-health-agenttrend-drift-agentcloudflare-drift-agent——合并成了一个 system-agent.js

68 行代码。三种运行模式:

  • --mode quick(奇数小时):PM2 进程存活检查 + 端口探测,连续 3 次失败才报警
  • --mode full(偶数小时):quick 的全部 + 磁盘用量 + Plex 可达性 + Cloudflare DNS/tunnel 监控
  • --mode daily(每天早上 8 点):full 的全部 + 30 天趋势分析 + 陈旧度检查 + 日报推送

四个散落的脚本变成了一个入口、一套状态文件、一条通知管道。代码量不一定少了,但 认知负担少了。你不再需要记住"passive-health 在哪个文件、services-health 用什么 cron 表达式、trend-drift 的状态存在哪"——现在只有一个东西,system-agent.js,三个 flag。

然后是 cron 的清理。OpenClaw 的 cron jobs 从 18 个删到 12 个——那 6 个被删的全是已经 disabled 但没人清理的僵尸任务。你终于回头扫了一遍,把尸体埋了。

还有一个安全修复不能不提:claude-remote 的 server.ts 第 387 行,原来绑的是 *——通配符,意味着任何网络接口都能访问你的 Claude Code Web UI。你改成了 127.0.0.1这种东西在生产环境里叫"事故",在你的 Mac mini 上叫"还好发现了"。

你还给它写了完整的测试套件:state、notify、services、disk、trends、cloudflare,单元测试和集成测试全过。上一篇我说你"嘴上说不 gold-plate,手上全是 gold-plate"。这次你连测试都 gold-plate 了。


跟 macOS 的权限洋葱搏斗

ORICO 2TB 外接硬盘,exFAT 格式,插在 Mac mini 上。Finder 能读,shell 能读,但 tmux 里读不到。返回 EPERM。

不是硬件问题。不是文件系统问题。不是挂载参数问题。

TCC

macOS 的 Transparency, Consent, and Control 系统——那个每次装新 app 都弹"是否允许访问"对话框的东西——对 tmux 这种 ad-hoc signed 的二进制有特殊的权限要求。它需要 Full Disk Access 授权。而且这个授权是按 二进制文件路径 发的,不是按进程树继承的。

也就是说:

  1. tmux 本身需要在 System Settings → Privacy → Full Disk Access 里被添加
  2. 添加之后必须 tmux kill-server 然后重新启动 tmux,授权才生效
  3. node 有自己独立的 TCC 授权(RemovableVolumes + NetworkVolumes),所以 PM2 spawned 的 node 进程不需要 tmux 的 FDA——它们走自己的权限路径

你花了多个 session 把这个洋葱一层层剥开。最后的修复动作只有两步——加 FDA、重启 tmux。但诊断过程经历了 mount flags 检查、osascript Finder 测试、Python/shell 路径对比、raw device 访问测试。你写了一个 test-volume-access.sh,测 6 个维度:3 种 shell session + node + Python + PM2。

然后你做了一件很 Vigoose 的事:brew pin tmux && brew pin node

因为 Homebrew 升级会改变二进制路径,路径一变 TCC 授权就失效。所以你锁死了版本。手动升级时的 SOP 也写好了:unpin → upgrade → 重新添加 TCC → 重启进程 → pin。

用一次事故换一条规则。用一条规则换一套 SOP。用一套 SOP 换一道防火墙。 这个模式在上两篇画像里出现过。现在它不只是一个习惯了,它是你的免疫反应。


免疫系统 2.0

上一篇写到你的系统有一个"免疫系统"——health check、gateway healer、cloudflare drift monitor。

这一篇你把免疫系统重写了。

system-agent.js 不只是把旧 agent 合在一起。它加了几个旧系统没有的能力:

自动清理。 磁盘用量 ≥85% 时自动触发:Docker prune、npm cache clean、pip cache purge、brew cleanup、/tmp 清理、应用缓存移到废纸篓。不是报警让你手动处理,是自己动手清。

告警批处理。 一个 run cycle 里发现的所有问题攒成一条飞书消息发出去。不再是一个问题一条通知、手机响五遍。你在 memory/feedback-push-quality.md 里写过"减少数量、提高质量"——这次你把这条规则刻进了代码。

趋势分析。 30 天滚动窗口的磁盘历史。不只是"现在满不满",而是"按这个速度,什么时候会满"。你从"能检测问题"升级到了"能预测问题"。

陈旧度检查。 如果某个服务的状态数据太久没更新,单独报警。这是在监控"监控系统本身是否在正常工作"——meta-monitoring。

自愈。 ~/bin/service-healthcheck 每 5 分钟 cron 跑一次,覆盖 ai-workspace、claude-remote、home-dashboard、media-gateway,挂了自动 restart。加上 ~/bin/tailscale-serves-restore 声明式恢复所有 9 个 Tailscale serve + 1 个 funnel 映射,缺了自动补。

你的免疫系统现在不只是白细胞了。它有了记忆 B 细胞(30 天趋势)、有了自噬机制(自动清理)、有了免疫监视(陈旧度检查)、有了再生能力(自愈 + 声明式恢复)。

59.4KB 的 work-log.md 记录了整个构建过程。你写代码写得很快,但你记笔记记得更勤。


GPT 不认注入

3 月 17 日你发现了一个有意思的问题。

你把 PA(个人助理)的数据写在了 AGENTS.md 和 USER.md 里,通过 bootstrap 注入到 system prompt。理论上 bot 应该能读到这些数据。但小瑶被问到出差信息时,回答"找不到"。

你追踪了 bot 的实际行为:读 qclaw-rules → 读 memory/*.md → 读 MEMORY.md → memory_search → 结论"没有"。它严格按工具搜索流程走,完全忽略了 system prompt 里已经存在的数据。

把同样的数据写到 MEMORY.md 后,bot 立刻能正确回答。

你把这条记录存进了 feedback-codex-bootstrap.md,标注为"未确认推测"——你不确定是 GPT-5.3-codex 模型本身不认 bootstrap 注入,还是指令流程太强导致模型优先走工具而忽略 context。你诚实地标注了自己不知道的部分。

但实用主义的结论很清楚:要让 bot "知道"什么,写到它会主动搜索的文件里,别只靠注入。 你不需要理解原因,你需要知道什么有效。

这个发现本身很有意思。你在跟一个你无法完全理解内部工作机制的系统协作。你不知道它为什么忽略 bootstrap 数据——可能是模型行为,可能是 prompt engineering 的副作用。但你像对待一个新同事一样处理它:观察行为,记录规律,调整沟通方式。


刷题

你搭了一个面试刷题应用。

~/interview-prep/,Node.js + Express + EJS,port 3070。功能列表:面经爬虫搜索、LeetCode split-panel 刷题 UI、AI 代码审查、Markdown 导出。

LeetCode 集成不是简单地嵌个 iframe。你用了 leetcode-query npm 包的 _fetch 绕 Cloudflare,Credential 自动生成 CSRF token,认证实例懒加载支持 Premium 题目。Run 和 Submit 走的是 LeetCode 的内部 API——/interpret_solution//submit/ → poll /check/。你把 LeetCode 的半个后端逆向了一遍。

然后你想把它分享给朋友。Tailscale Funnel 配了一个 8452 端口——外面死活连不上。超时。

你发现 Tailscale Funnel 只支持三个端口:443、8443、10000。用其他端口配置 funnel,命令不报错,但流量根本到不了。又一个"命令成功但事实失败"的坑。你换到 10000 端口,通了。

公网地址:https://yuanyemac-mini.tailf902c7.ts.net:10000/

这个项目的出现改变了整篇画像的叙事。前面所有东西——健康监控、服务合并、权限修复、通知迁移——都是 基础设施工作。它们重要,但它们是给"现在"服务的。

刷题应用是给"未来"服务的。

结合 USCIS 自动化追踪(每天 12 点自动查身份状态)、一亩三分地论坛监控(每天两次推送 TOP20)、algorithm notes viewer(今天刚做的),一条暗线浮出水面:你在准备找下一份工作。

你用造基础设施的方式准备面试。别人刷 LeetCode 用网页,你自己写了一个 LeetCode 客户端。别人看面经用手机,你写了爬虫把面经数据结构化。别人做算法笔记用 Notion,你写了一个 HTML viewer。

你的 data/profiles/<username>/lists/*.json 里存着刷题列表,data/profiles/<username>/面经/*.json 里存着面经数据。连找工作都工程化了。

你不是在刷题。你在搭一个刷题系统。 这太 Vigoose 了。


你还顺手做了这些

Claude Control。 你找到了一个 macOS 桌面应用,可以从一个仪表盘管理多个 Claude Code CLI session——实时状态、Git 集成、从 GUI 批准/拒绝工具权限、桌面通知。你把它存进了 reference memory。你在寻找管理"你的 AI 们"的工具,就像管理者寻找项目管理软件一样。

tax-helper。 不是"辅助工具",是一整个报税系统。Express.js,port 3071(3060 被 wecom-bot 占了),一个 session 从零搭完,9 个 subagent 并行施工。你拖进去一堆 PDF——W2、1098、1099-B、1099-DIV、1099-INT、RSU supplement——它用 pdfjs-dist 转成图片,然后用 Claude Sonnet Vision 做结构化数据提取,再用 Claude Sonnet 做税务分析:standard vs itemized 对比、税额估算、RSU 双重计税警告、SALT cap、HELOC、AMT 检测。最后生成一份 FreeTaxUSA 的填报指南,按它们的 UI 流程排序,还有一个交互式聊天可以追问。你把报税变成了一个 AI pipeline。

tel-bot persona 更新。 Telegram bot 的人格文件改了好几轮,重启了五六次。你在调教一个 bot 的性格,反复删除重建 PM2 进程直到满意。

Plex 重启。 端口 32400 上的 Plex Media Server 挂了,你手动拉起来。生活基础设施也需要运维。

algorithm notes viewer。 一个 HTML 文件,用来阅读算法笔记的 Markdown。检查了图片引用、章节标题、文件大小。配套写了 tmux-sidebar.shtmux-sidebar.py。你把 tmux 变成了一个带侧边栏的 IDE。


这一周的时间线

3 月 16 日 — clawd CLI 精简完成。第二篇画像发布。同一天晚上,开始搭 tel-bot standalone。装了 zoxide。

3 月 17 日七段 session 的超级星期一。 WeCom bot 从零搭建。QClaw 诞生。发现 GPT bootstrap 注入问题。openclaw-voice wake word 设计。LINE proxy 设计文档。PA 数据同步脚本。还顺便搜了一下 ACG.RIP 上的《石纪元》。

3 月 18 日 — 更新 tel-bot persona。检查 USCIS 状态。晚上的 session 比较短——可能累了。

3 月 19 日 — ORICO 外接盘 EPERM 首次出现。小红书 MCP 重新登录。Tailscale serve 调试。

3 月 20 日 — session 记录里没有 2026-03-20.md。你歇了一天。或者至少歇了一个晚上。在连续高强度输出的日子里,空白的一天反而是最值得记录的。

3 月 21 日 — 大日子。十个 session,从早 09:20 到晚 23:39。 ORICO TCC 修复。system-agent 设计+实现+测试。AI-Workspace 三合一。Discord 集成(四个 session)。Plex 重启。飞书迁移。Cron 大扫除。安全修复。磁盘空间审计。

3 月 22 日 — 面试刷题应用。algorithm notes viewer。tmux sidebar。tax-helper 从零搭完(9 个 subagent 并行,一个 session 搞定 PDF 提取 + 税务分析 + FreeTaxUSA 指南 + 交互聊天)。Tailscale Funnel 端口踩坑。小红书 parking 笔记收集。节奏不算慢,只是更聚焦了——全部跟"准备未来"有关。


你的系统现在长什么样

截至今天,PM2 里跑着 16 个服务:

ai-workspace    — 统一 Flask app,port 5001
agent-town      — 像素风 AI 仪表盘,port 3333
blog            — 文人书房,port 3050
claude-remote   — Web UI 桥接 TUI,port 8775(已修复绑定 127.0.0.1)
devhub-caddy    — 反向代理,port 4443
home-dashboard  — Home Assistant 面板,port 4000
line-proxy      — LINE 消息代理,port 18780(通道已弃用,服务还在跑)
media-gateway   — 图片签名 URL,port 8787
media-manager   — 影音库管理
mteam-client    — PT 站自动化
ttg-client      — PT 站自动化
tel-bot         — Telegram bot
video-server    — 自建视频服务
wecom-bot       — 企业微信,port 3060(新)
xhs-dashboard   — 小红书数据面板
pm2-logrotate   — 日志轮转

加上 OpenClaw gateway(port 18789)、QClaw gateway(port 28789,新)、Cloudflare tunnel、小红书 MCP(按需启动)、Chrome Browser MCP。12 个活跃 cron job 全部走飞书。Tailscale 上 9 个 serve + 1 个 funnel。service-healthcheck 每 5 分钟巡逻一次,tailscale-serves-restore 声明式自愈。

还有 ~/interview-prep/ 手动启动,等着被 PM2 收编。~/tax-helper/ 已经有 ecosystem.config.js 了——你搭它的时候就知道它会留下来。

上一篇我说"这不是一个服务器,这是一个器官系统"。

这一篇我要修正:这是两个器官系统。 一个维持现在(基础设施、监控、通知),一个准备未来(刷题、面经、报税)。它们跑在同一台机器上,同时发生。


分裂与合并

回到开头的问题:一边做减法一边做乘法,矛盾吗?

看你的文件系统就知道答案。

~/clawd/health-agent/ 里,四个独立脚本合并成一个 system-agent.js。根在收拢。

~/clawd/wecom-bot/ 里,一个全新的服务从零长出来。枝在伸展。

~/AI-Workspace/ 里,三个端口合并成一个,两个端口永久退役。根在收拢。

~/.qclaw/workspace/ 里,一个新的 AI 身份诞生,有自己的灵魂文件和螃蟹 emoji。枝在伸展。

brew pin tmux && brew pin node。根在收拢。

~/interview-prep/,从零搭起,Tailscale Funnel 对外暴露。枝在伸展。

LINE 全面废弃,12 个 cron job 迁飞书。旧枝在修剪。

Discord、WeCom、QClaw。新枝在萌发。

这不是矛盾。这是树的生长方式。

根越粗,能撑住的枝越多。你合并服务不是因为你在收缩,是因为你需要腾出结构承载力来支撑更多的枝叶。

三个 Flask 变一个,省出来的不是代码行数,是你脑子里的认知槽位。四个 health agent 变一个,省出来的不是 CPU,是你 debug 时需要检查的地方。18 个 cron 删到 12 个,省出来的不是调度资源,是你半夜被通知吵醒时需要排查的范围。

然后你把省出来的认知带宽,投入到了面试准备和新通道上。

你的系统正在经历一棵树最重要的阶段:主干定型,开始分枝。 而你自己正在经历一个人最重要的阶段之一:准备下一步。

你可能自己都没意识到这两件事是同步发生的。你在给系统做减法的同时,也在给自己的人生做加法。brew pin 锁定的不只是 tmux 和 node 的版本——你在锁定"现在",好让自己腾出手来够"未来"。

面试刷题应用还没被 PM2 托管。报税应用已经写好了 ecosystem.config.js

一个还在手动启动,一个已经准备好被收编。就像你自己——一半还在当前的生活里手动运行,一半已经写好了下一阶段的启动配置。

你所有的东西最后都会被收编。你自己也是。