本地 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 在忙吗?健康吗?被问到时现场计算,从不缓存
定时型提醒、定期检查、稍后重试只在有任务时存在的短生命周期进程

只有最后一行需要一个比命令活得更久的东西。把它整理成规则,就是下面七条原则:

  1. 在哪里发起,就在哪里完成。send 在返回之前就完成投递,没有队列等着后台循环慢慢消化。
  2. 让 tmux 负责看护。tmux 本来就是一个常驻服务,专门让终端程序在断线后继续运行。Agent 跑在它的窗格里,不需要再造第二个监管者跟它同步。
  3. 状态放在小文件里,每个文件只有一个写入者。每个 Agent 的消息写进它自己的追加式收件箱文件。记录只追加、不改写,崩溃也损坏不了历史记录。
  4. 只锁最小的对象。锁只覆盖一个 Agent 或一个收件箱,绝不锁整个工作区。卡住的 Agent 只会让自己的操作失败。
  5. 只为定时任务醒来。提醒按需拉起后台进程,由操作系统监管(macOS 上为 launchd),计划清空后自动退出。
  6. 状态现场计算。直接问终端“现在有什么在运行”,而不是相信几分钟前写下的记录。
  7. 只报告能证实的事。“已输入并提交”是事实,“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 编排工具的清单

无论你最终选择哪个工具,下面这些问题都能帮你分辨设计是否健壮:

  1. 没有 Agent 在工作时,还有什么在运行?它占着多少内存?
  2. Agent 运行期间升级工具,下一条消息由哪个版本处理?
  3. 一个卡住的 Agent 会不会挡住发给其他 Agent 的消息?
  4. 消息存在哪里?能不能用普通工具直接查看?
  5. “已投递”究竟是什么意思?是观察到的,还是推测的?
  6. Agent 正在忙,或者你正在它的窗口里打字时,发给它的消息会怎样?
  7. 重启之后,Agent 能否带着对话历史回来?
  8. 它运行的是你配置好的官方 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 出问题不会拖住其他人。

延伸阅读