本地 AI Agent 需要进程监管,而不是笨重的后台守护进程
大多数多 Agent 工具照搬了 Web 后端的设计:一个常驻服务,加一个数据库队列。放到开发者的笔记本上,这套设计会卡死、会泄漏内存,还会悄悄“谎报”投递状态。下面介绍一种更轻量的架构,它不是更脆弱,而是更可靠。
- 本地 AI Agent 需要进程监管:启动 Agent、保持在线、投递消息、发现异常退出,并在重启后恢复。
- “常驻编排服务 + 数据库队列”是常见答案,但它在开发机上会以五种可预见的方式出问题。
- 几乎所有协调工作都可以在发起它的那条命令里完成。tmux 本来就能让 Agent 保持运行,只有定时任务才需要后台进程,而它空闲时完全可以退出。
- Team Agent 正是这样设计的:没有守护进程,没有数据库,消息存放在追加式收件箱文件中,空闲内存占用 0 MB。
01什么是 AI Agent 的进程监管?
进程监管(process supervision)指的是管理长时间运行程序的整个生命周期:启动它们、保持它们运行、出错时重启,并且随时知道哪些进程是健康的。操作系统用 launchd、systemd 这类工具来做这件事,容器平台则交给编排系统。
当你在一台机器上同时运行 Claude Code、OpenAI Codex、Pi 等多个 AI 编程 Agent 时,同样需要一个缩小版的监管者。它必须负责:
- 按照正确的角色、模型和权限启动每个 Agent CLI;
- 把任务和消息投递给正确的 Agent,即使它正在忙;
- 把结果送回负责协调的一方;
- 发现某个 Agent 已经退出;
- 机器重启后,连同上下文一起恢复整个团队。
问题不在于要不要监管,而在于这个监管者应该有多重。
02“重型守护进程”模式
最常见的设计长得像一个小型 Web 后端:开始工作时启动一个编排进程并常驻内存,定时轮询每个 Agent,把每条消息写进数据库(通常是 SQLite),再由一个投递循环逐条处理队列。画在白板上非常好理解,所以很多工具都从这里起步。
我们最初也是这么做的。早期版本的 Team Agent 就运行着一个后台协调进程,外加一个 SQLite 消息库。在真实项目里天天使用之后,我们清楚地看到了这套设计会在哪里断掉。
03后台守护进程在开发机上的五种失败方式
1. 永远释放不掉的内存
常驻进程无论有没有人在干活,都占着内存。把它乘以你同时打开的项目数,再加上任何长寿命进程都会出现的缓慢增长,你的笔记本就一直在为“睡着的 Agent”交税。长寿命的编排进程也正是内存泄漏的温床:一个从不退出的进程,永远没有机会从头来过。
2. 升级后的版本错位
你升级了工具,但昨天启动的守护进程跑的还是昨天的二进制。于是 CLI 说一套协议,守护进程说另一套。为了应付这个因“常驻”而自找的麻烦,工具不得不再加一层轮换逻辑和兼容性检查。
3. 一个队列卡住,所有人停摆
中心化的投递循环就是单点故障。只要一条消息进入异常状态,或者某项检查始终不通过,排在后面的所有 Agent 都会被卡住。我们亲身经历过:一次投递检查被阻塞,导致整台机器上所有团队的外发消息全部停滞,直到修复为止。
4. 数据库锁争用与体积膨胀
共享数据库会变成一把全局锁。每个发消息的 Agent、每次状态轮询、每次结果写入,都要争这把锁。守护进程记录的每一条观测都会让文件变大;在长期运行的工作区里,我们的数据库一度超过了 100 MB。写到一半崩溃需要恢复逻辑,而恢复逻辑又需要没人愿意写的测试。
5. 会“说谎”的状态
队列可以把一条消息标记为“已投递”或“已消费”,但交互式终端程序并不会给出可靠的确认。成千上万条消息最后都停在一些听起来很确定、实际上从未被观察到的状态里。一旦状态不可信,大家就开始重发,而重复的指令比迟到的指令更糟糕。
每一种失败,都来自一项检查、一把锁或一个进程的作用范围比它要保护的对象更大:一把锁管所有 Agent,一个循环管所有团队,一个数据库装所有消息,一个进程守着一天中的每一个小时。
04替代方案:轻量、无状态的进程监管
如果按“什么时候必须发生”来给协调工作分类,更好的设计就自然浮现了:
| 工作类型 | 示例 | 应该放在哪里 |
|---|---|---|
| 即时型 | 派发任务、汇报结果、启动或停止 Agent | 在发起它的那条命令里完成 |
| 事件型 | 某个 Agent 进程退出了、你接入了组长窗口 | 终端复用器(tmux)的钩子 |
| 查询型 | 这个 Agent 在忙吗?健康吗? | 被问到时现场计算,从不缓存 |
| 定时型 | 提醒、定期检查、稍后重试 | 只在有任务时存在的短生命周期进程 |
只有最后一行需要一个比命令活得更久的东西。把它整理成规则,就是下面七条原则:
- 在哪里发起,就在哪里完成。
send在返回之前就完成投递,没有队列等着后台循环慢慢消化。 - 让 tmux 负责看护。tmux 本来就是一个常驻服务,专门让终端程序在断线后继续运行。Agent 跑在它的窗格里,不需要再造第二个监管者跟它同步。
- 状态放在小文件里,每个文件只有一个写入者。每个 Agent 的消息写进它自己的追加式收件箱文件。记录只追加、不改写,崩溃也损坏不了历史记录。
- 只锁最小的对象。锁只覆盖一个 Agent 或一个收件箱,绝不锁整个工作区。卡住的 Agent 只会让自己的操作失败。
- 只为定时任务醒来。提醒按需拉起后台进程,由操作系统监管(macOS 上为 launchd),计划清空后自动退出。
- 状态现场计算。直接问终端“现在有什么在运行”,而不是相信几分钟前写下的记录。
- 只报告能证实的事。“已输入并提交”是事实,“Agent 已接受”只是猜测,系统应该如实说明。
还有一个附带的好处:每条命令运行的都是你刚刚安装的二进制,不存在落后版本的守护进程,升级即刻生效。
05“空闲 0 MB”到底是什么意思,以及如何自己验证
大胆的资源宣称需要精确的定义。对 Team Agent 来说,空闲 0 MB 的意思是:没有任何后台进程。关掉你的 Agent,且没有定时任务时,不会有任何 Team Agent 进程残留。不是“一个内存很小的休眠进程”,而是根本没有进程。
Agent 运行期间,仅有的 Team Agent 进程都依附于它们:组长会话的启动器,以及每个 Agent 一个为它提供团队消息工具的小助手。它们都属于各自 Agent 的进程树,并随之退出。tmux 和各个 Agent CLI 的内存占用,与不用 Team Agent 时完全一样。
你可以自己验证。停止团队、退出组长会话,然后执行:
team-agent stop --workspace .
# 退出组长对话后再执行:
pgrep -fl team-agent || echo "no team-agent processes running"
team-agent stop 在报告成功之前,还会核实团队自己的进程和资源已经全部清理,所以“干净地停止”就意味着什么都没有留下。
06这样设计如何避免卡死和内存泄漏
- 没有什么能活到泄漏的那一天。协调代码运行在几秒内就退出的命令里。长时间运行的只有 Agent CLI 本身,你看得见,也可以单独重启。
- 故障被限制在局部。每个 Agent 一把锁、一个收件箱,卡住的 Agent 只会让发给它的操作报错,其他人照常工作。
- 后台没有东西可以崩溃。没有中心循环,也就没有悄无声息的停滞。每次发送要么成功,要么明确地失败。
- 诊断是一条命令,而不是一场调查。
team-agent doctor会按顺序检查六项内容(作用范围、主机、Agent、存储、消息路由,以及回到组长的通道)。健康时只输出一行;出问题时停在第一个失败的环节,并给出一条修复建议。
07评估本地多 Agent 编排工具的清单
无论你最终选择哪个工具,下面这些问题都能帮你分辨设计是否健壮:
- 没有 Agent 在工作时,还有什么在运行?它占着多少内存?
- Agent 运行期间升级工具,下一条消息由哪个版本处理?
- 一个卡住的 Agent 会不会挡住发给其他 Agent 的消息?
- 消息存在哪里?能不能用普通工具直接查看?
- “已投递”究竟是什么意思?是观察到的,还是推测的?
- Agent 正在忙,或者你正在它的窗口里打字时,发给它的消息会怎样?
- 重启之后,Agent 能否带着对话历史回来?
- 它运行的是你配置好的官方 CLI,还是用 API 调用把它们替换掉了?
08常见问题
本地 AI 编程 Agent 需要后台服务来协调吗?
不需要。派发任务、启动 Agent、读取结果,都可以在发起它的那条命令里完成。tmux 这样的终端复用器本来就能让 Agent 进程保持运行。只有提醒这类定时任务才需要后台进程,而它完全可以按需启动、计划清空后自动退出。
在本地运行 AI Agent,怎样避免内存泄漏?
避免使用会不断累积内存的长寿命编排进程;优先采用做完一件事就退出的短命令;把 Agent 状态存在小文件里,而不是不断膨胀的数据库;停止团队时使用会核实无残留进程的命令。最后再用进程列表确认关停后什么都没留下。
Team Agent 的“空闲 0 MB”具体指什么?
Team Agent 没有守护进程。你能看到的每个 Team Agent 进程都依附于你正在运行的 Agent:组长会话的启动器,以及每个 Agent 一个小型消息助手,它们都随各自的 Agent 退出。关掉 Agent、且没有定时任务时,不会有任何 Team Agent 进程残留,因此不占内存。tmux 和各个 Agent CLI 的内存占用与平时无异。
为什么不用 SQLite 做本地 Agent 的消息队列?
共享数据库会变成一把全局锁,每个 Agent、每条命令都得从这里过。并发写入时会争用,文件会越来越大,写到一半崩溃还需要恢复。改用追加式文件、每个收件人一把锁,可以把故障隔离开,一个 Agent 出问题不会拖住其他人。