ARTICLE / 安全

AI agent 网关越跑越慢直到假死:三类劣化信号和监控缺口

一个开源自托管 agent 网关的用户做了一次教科书级的资源问题排查:单用户、轻负载的实例,RSS 从 2.5 GB 一路涨到 8-10 GB,60 到 90 分钟内触发内存压力告警,最终威胁 OOM。他排除了插件、排除了面板轮询、排除了厂商插件,冷重启后泄漏照旧——最后用堆剖析把问题定位到核心包里一个模型目录 worker。这是 OpenClaw #159662,2026 年 9 月末开出的 P0 级 issue。把它和同项目的另外两条问题(僵尸进程堆积、MCP 调用审批缺口)放在一起看,会得到一张对 AI agent 基础设施运维很有用的"劣化信号地图":长驻 agent 服务出问题,往往不是崩,而是慢、拖、漏。

信号一:内存单调增长,与负载无关

#159662 的报告质量高在排除法。报告者观察了三个实例:一个存量进程、一个干净重启、一个整机冷启动——泄漏速率都在每小时 4-5 GB 量级。他把主模型从 moonshot 切到 anthropic,堆增长曲线穿过了切换点,斜率不变。他杀掉了轮询网关的两个面板进程,关掉了浏览器标签,泄漏依旧。在一个几乎空闲的窗口里,全系统只发生了 15 次模型调用,worker 却涨了约 2.2 GB——折合每次调用净留 100-150 MB。

堆剖析给出的线索都在核心流式处理路径上:SSE 解析器的 push、undici 的响应转换、openai 流式迭代器,外加两帧插件装配代码。报告者的推断是:模型响应流在喂给 catalog worker 里的某个结构(缓存、历史数组、按调用或会话键控的 Map),而这个结构从不逐出,或逐出远慢于增长。

这条 issue 给运维的直接启示:单调性本身就是指纹。与负载无关、重启不清零、跨配置变更连续的 RSS 曲线,指向的是核心代码里的保留问题,不是流量问题。监控上要做的不是设一个绝对阈值,而是看斜率——健康的常驻服务内存应该有锯齿(GC 回收),一条只涨不跌的线就是告警条件。

信号二:僵尸进程堆积,spawn 开始失败

同一项目的 #97616 是另一形态:hook 和工具执行产生的子进程没有被收割(reap),以僵尸态挂在主进程下——openclaw-hooks、bash、codex、偶尔还有 chrome-headless。积累到一定程度后运行时劣化,最终无法派生新的辅助工作:spawn EAGAIN、uv_thread_create 失败、网关不稳、连接器静默。重启进程表立刻清空,服务恢复,然后泄漏重新积累。

对从传统中间件转到 AI agent 运维的工程师,这个形态其实很经典——uncleared child 就是 Unix 世界的老问题。新东西在于 AI agent 的子进程来源比传统服务杂得多:一次工具调用可能拉起 shell、代码执行器、无头浏览器,生命周期管理分散在插件、hook、连接器多层。监控要点是进程表审计:主进程下的 defunct 子进程计数、按父子关系聚合的进程树快照。僵尸进程在 ps 里一目了然,问题只在于没人看。

信号三:审批管线的覆盖缺口

第三条 #78308 性质不同:不是资源问题,是权限边界问题。提出者指出,agent 框架对 shell 执行已有硬化门禁——运行时发出审批请求,用户在验证过的通道回复批准,然后才执行;但 MCP 工具调用没有同等待遇。当 MCP 工具会修改外部状态(发邮件、写密钥库、改智能家居状态、发聊天消息)时,用户和动作之间隔着的东西,只有"模型自己决定不调用"。工具描述可以要求模型先向用户确认,但那是提示词层面的纪律,没有基础设施层的强制。一个错位或被入侵的 agent 可以伪造确认令牌重调工具绕过去。

提案方案是个很好的参考设计:MCP 服务端在需要审批时返回一个标准信封(带单次令牌、人类可读摘要、过期时间),运行时识别信封、屏蔽结果、走既有审批通道,用户批准后才带令牌重调——而且模型永远看不到令牌,模型输入里的令牌字段会被剥掉,防止自我批准。这个"提示词纪律升格为运行时强制"的思路,适用于所有把第三方工具接进 agent 的场景。

蓝队的监控清单

三条问题合起来,给自托管 agent 基础设施(无论哪个发行版)的运维清单至少有四项:

内存看斜率不看绝对值。 常驻 worker 的 RSS 单调增长、跨重启复现、与调用量脱钩——三个条件命中两个就该开堆剖析,不要等到 critical。

进程表定期审计。 僵尸子进程计数纳入日常巡检;出现 spawn EAGAIN 类错误时先查进程表再查应用日志,前者往往更快定位。

审批边界做映射,不做假设。 把 agent 能触发的所有副作用工具列出来(shell、MCP、插件、连接器),逐个标注"谁批准、在哪强制"。你会发现强制点比想象中少,而提示词层纪律覆盖不了错位场景。

诊断通道本身要留余量。 #159662 里有个容易被忽略的细节:网关在内存压力下,自己的诊断导出命令要 90 秒以上才响应——监控工具和被监控对象抢资源时,监控先死。关键诊断(进程表、内存快照)要走独立通道,不能只依赖被监控服务自己提供的 API。

自托管 agent 框架还在快速迭代期,这类资源与边界问题会持续出现。运维侧能做的是把信号地图建好:内存斜率、进程表、审批覆盖、诊断通道——四项都指向同一个原则,agent 基础设施要按长驻关键服务对待,不能按跑完即弃的脚本对待。

本文面向授权安全研究与防御建设,未经授权对任何系统实施文中技术属违法行为。