Loop X:用我已经付过费的 CLI,搭一个完全私有的 AI 同事
Claude co-worker 和 Claude Tag 都很好用,但算完账单发现不划算,于是我用已经付费的 Claude CLI 搭了 Loop X,一个完全跑在自己 Mac 上的 AI 同事。
一边开着 Claude 的 co-worker 介绍页面,一边开着团队的 GitLab issue 积压列表。那个介绍页写的正是我想要的东西:每天早上自动把队列过一遍,能修的修掉,修不了的标出来,这样没人会让分配给自己的 issue 一直晾在那儿。我之前在 Slack 里已经见识过 Claude Tag 做类似的事,确实好用。然后我按团队实际产生的 GitLab 流量算了一下账单,发现跟团队每台机器上已经在跑的 Claude Code 订阅比起来,这笔钱花得不值。
这件事让我一直惦记着。我想要的能力其实不复杂:列出分配给我的 issue,一个一个处理,能修的修,不用改代码的就回复,弄不清楚的就标出来,最后给我发一份摘要。这几步 claude -p 在终端里本来就能做。真正要额外付费的,是围绕这个命令的调度、安全边界,还有一个仪表盘。于是接下来几周,我就在已经付费的 CLI 之上,把这一层自己搭了出来,而且整套东西就跑在我自己的电脑上。
Loop X 到底是什么

这是一个只用标准库的 Python 项目,在我的 Mac 上跑成两个 launchd 守护进程。一个是调度器,每十五分钟轮询一次,到点就触发该跑的 loop。另一个是常驻的本地仪表盘。不管是 issue 分诊、邮箱打标签、CI 排查还是会前准备,每一项都是 loops.json 里的一条 loop 定义,每次跑起来,就用 loop 自己的指令文件拼出一段 prompt,调用 claude -p(如果配置的是 Codex,就调用 codex exec)。没有任何东西托管在外面。没有哪个第三方账号能看到我的 issue、我的邮件或者我的日程,loop 的状态、历史记录、草稿全都存在我自己这台机器上的 ~/.loop-engineering 目录里。
主力 loop 是 GitLab issue 处理。每个工作日上午十点,它会列出我在每个已配置项目里被分配的所有 issue,然后一个一个处理,绝不并行,这点我花了点时间才习惯,因为用 agent 干活的本能反应总是”为什么不开几个并行跑”。对每个 issue,它只会做三件事里的一件:在独立的 git worktree 里修完,等项目自己的 lint 和测试命令都通过后开一个合并请求;不需要改代码的,直接评论回答;需求含糊或者验证失败的,评论请求澄清。处理完一批后会给我发一条 Slack 摘要,并更新 PROGRESS.md,让下一次运行知道上次发生了什么。我自己也能看懂。
然后我就停不下来了
GitLab 那个 loop 干干净净跑了几周以后,我发现生活里到处都是同样形状的问题,于是一个接一个地把它们也变成了 loop。下面这几个现在全都在我自己的机器上跑着,不是什么概念演示。
Gmail 和 Outlook,分诊但绝不动手。 收件箱分诊 loop 会读取上次运行以来的未读邮件,把它们归到几个类别之一(Loop/Urgent、Loop/Action、Loop/FYI 等等),遇到紧急的就起草一封回复,放进邮箱自己的草稿箱里,等我自己看过、改过、发出去。它不能发邮件。不是”被叮嘱不要发”,是真的做不到:Gmail 和 Outlook 两边的集成代码里都没有发送邮件这个功能,而 Outlook 申请的 OAuth 令牌本身就没有 Mail.Send 这个权限,就算代码想发也发不出去。它也不能归档、删除或者改已读状态。它唯一能写的,就是建一个标签、丢一份草稿。
日历,提前十五分钟就知道。 会前准备 loop 盯着我的 Google 日历,只要有别人参加的会议,提前四十五分钟就会整理出一份简报:议程、邀请描述里链接的 GitLab issue 或 MR、跟组织者和参会人往来的近期邮件(从不涉及我自己,也从不看邮件正文,只看主题、发件人、日期),如果是周期性会议,还会附上上一次的小结和待跟进事项。最后这一点比我预想的有用得多。周期性会议很容易越开越散,还没打开电脑,Slack 里就已经躺着”上次我们说要跟进这件事”,这已经救了我好几次,让我没有带着一片空白走进一场本该接着上次讲的会。
话题监控,一句英文就能配一个。 话题监控跟 GitLab、邮箱都没关系,它做的是实打实的联网搜索。每个话题就是一个名字加一句描述,写清楚什么算”值得关注”,所以一个话题可以盯着模型发布和融资新闻,另一个盯着某个具体项目的更新日志,同一个 loop、同一份代码,换一句 brief 就行。它会跟过去七天的记录去重,不会把同一条新闻再发一遍;就算哪天什么都没有,也会老老实实发一句”今天没什么新东西”,而不是悄悄不发,这点比听起来重要,因为一个该出声却沉默的 loop,跟一个坏掉的 loop 从外面根本分不出来。
RSS,排序过的而不是堆在那儿的。 RSS 监控 loop 干的,其实是我这些年一直希望 RSS 阅读器能帮我干的事:抓取新内容,按我在设置框里写的兴趣列表打分排序,挑出最好的十条,各配一句理由发给我,而不是让未读数字一路爬到四位数然后彻底放弃。这个 loop 多了一层额外的警惕,因为订阅内容是整套系统里唯一来自公开互联网、而不是我自己账号的输入。prompt 里明确提醒了这一点,它本身不带任何工具权限、不能碰 shell,写出来的内容在送到 Slack 之前,还要经过跟其他所有通知一样的清洗流程。
两件真正难的事
第一件是决定到底哪些 loop 值得拿到真正的工具权限。GitLab issue 处理需要 shell、git worktree、跑项目自己测试套件的能力,覆盖面相当大,毕竟修代码就得碰代码。但收件箱分诊、会前准备、话题监控和 RSS 监控都不需要这些。它们只需要拿着我已经抓好的数据,调一次模型,要一个判断。我最后把 loop 分成了两类:需要写代码的用带工具和 MCP 服务器的完整运行时;剩下那些本质上只是”看一眼,告诉我重点是什么”的,就用一个封闭插件模式,没有工具、没有 shell,每一条单独隔离,一条出错也不会拖垮整批。这个拆分对成本的影响,比任何 prompt 调优都大,因为我每天大部分的 loop 其实都属于第二类。
第二件是信任问题,这个我一开始明显低估了,而且每个 loop 上这个问题都长得不太一样。对 GitLab 来说,它绝不合并自己开的 MR,没有例外,合并永远是人点一下鼠标的事,每次修复都在独立的分支和 worktree 里进行,验证失败就升级,不会循环重试。对收件箱分诊来说,“这个人的邮件永远标紧急”这条 VIP 规则,是在模型给出回答之后,用一段实打实的 Python 代码强制执行的,排除名单上的发件人,也是在邮件送到 AI 面前之前就已经被过滤掉了。只要是碰真实账号的 loop,我反复回到的都是同一条原则:凡是出一次错都不能接受的事,就别指望光靠 prompt 来兜底。写出每个 loop 真正干活那部分代码,一个下午顶多两个下午就够了。写出”这件事无论之前表现多好,也绝对不许做”的那部分,花掉了剩下的三周。
真正让这一切不再像玩具的那个时刻,其实一点都不戏剧化,这才是它让我记住的原因。有些早上打开 Slack,会看到四份摘要一起躺在那儿:GitLab issue 在夜里被处理完了,一两封紧急邮件的草稿等着我扫一眼再发出去,一份会前简报已经附好了该跟进的事项,一份话题简报那天确实提到了点什么。这些消息没有一条要求我马上去做什么。这正是重点。以前要攒到有空才会去碰的那四堆积压,等我倒完咖啡的时候已经被过了一遍。
让任何人都能用,而不只是我自己在终端里敲命令
我不想让这东西只活在我一个人对 shell 的熟练程度里。bin/scripts/build_macos_app.sh 会把整个仪表盘包进一个带独立 Python 环境的原生窗口,最后得到的是一个 Loop X.app,拖进应用程序文件夹,像打开任何普通软件一样打开它,装好之后就再也不用碰终端了。它会自动连上后台已经在跑的调度器和仪表盘,如果还没启动,就自己把它们拉起来。

两种形态里仪表盘内容完全一样,按钮一样,对话框一样,loop 列表也一样。唯一的区别是它到底躺在 loop.x 这个浏览器标签页里,还是躺在自己的窗口里、带着自己的 Dock 图标,而恰恰是这个区别,决定了能不能把它递给一个本来会反问 127.0.0.1:8420 是什么意思的人。
能让”接入一个新账号”变成填一张表单而不是改代码的,是 connector 这个模型。一个 connector 就是一个账号,带一个类型(GitLab、Gmail、Slack webhook、RSS 订阅列表、Jira、Notion、某个日历)和它能提供的能力,比如 issues、mail、notify、feed;一个 loop 只要求某种能力,任何对应类型的 connector 都能满足它。想加个 Telegram 或 Discord 当通知渠道,或者再接一个 Gmail 账号,在 Connectors 页面填个表单、点一下 Test 就行,不用动一行代码。
而且因为整套东西都是无人值守的,真正让它好用的,其实是不用自己去翻就能知道发生了什么。每个 loop 一跑完就会发自己的 Slack 摘要,仪表盘的 Activity 页面里还嵌了一个对话助手,我可以直接问”现在接了哪些账号”或者”GitLab 那个 loop 今天早上干了什么”,拿到的是从真实运行记录里查出来的答案,不是瞎猜。
真正变了的东西
我一开始以为自己在挑的产品是”一个帮你处理 issue 的 AI”。后来发现不是。调用模型这一步从来都不是稀缺资源,我本来就有,反正订阅费都付了。Claude co-worker 和 Claude Tag 真正在卖的,是围绕那次模型调用的编排:调度、安全边界、仪表盘,还有”无人值守的 agent 到底能碰什么、不能碰什么”这套 policy。这是个真实的产品,收费也合理。但它同时也是一个我能用自己已有的权限自己搭出来的产品,因为这些部分没有一个是秘密,都是决定,不是能力。而一旦为第一个 loop 做完这些决定,再给邮箱、日历和一堆 RSS 订阅做一遍,大部分时候就只是重复而已。
没想到的是,整个代码库里,写边界规则的部分比写实际行为的部分多得多,而且加了这么多 loop 之后,这个规律在每一个上面都成立,不只是第一个。真正修一个 GitLab issue、起草一封回复、写一份会前简报的代码,每个都不过是几百行包了一次模型调用。而决定每个 loop 什么时候能动手、什么时候必须停下来问人、不管之前跑得多顺也绝不能碰什么,这部分代码占了整个项目的大头。要是明天重写一遍,我会先写边界那部分,再把实际的任务逻辑当成容易的部分去做,因为连着做了四个 loop,事实证明重要性的顺序一直都是这样的。Loop X 开源在 GitHub 上,github.com/encoreshao/loop-engineering,MIT 协议。