建议:为 CDP Proxy 增加同时打开的 tab 数量上限,避免内存过载
问题
/new 目前没有任何数量约束,短时间内可以无限创建后台 tab。
大型调研类任务天然会开很多页——多来源交叉验证、批量抓取列表页详情、并行分治,这些都是 skill 鼓励的正常用法,不是滥用。而现代站点单个 tab 常驻内存动辄 100~400MB(尤其是这个 skill 主打的电商后台、社媒、数据面板这类重 JS 页面),累积几十个就是 GB 级占用,内存吃紧的机器会直接卡死或崩溃。
也就是说:任务规模越大、skill 用得越充分,越容易撞上这个问题。
现状
(基于 origin/main @ 7af34af,v2.5.3)
/new 拿到请求后直接创建,没有任何计数或守卫:
// scripts/cdp-proxy.mjs:361
const resp = await sendCDP('Target.createTarget', { url: targetUrl, background: true });
managedTabs.set(targetId, { lastAccessed: Date.now() });现有的唯一回收机制是时间维度的闲置清理:
// scripts/cdp-proxy.mjs:31
const TAB_IDLE_TIMEOUT = parseInt(process.env.CDP_TAB_IDLE_TIMEOUT || '900000'); // 15 min defaultcleanupIdleTabs() 每 60s 扫一次,关掉超过 15 分钟没被访问的 managed tab。
为什么闲置清理不足以覆盖这个问题
时间兜底救不了瞬时并发。 内存压力取决于「同一时刻并存多少 tab」,而 15 分钟窗口内可以开出任意多个。密集抓取时几分钟就能堆到几十个,此时一个 tab 都还没到回收年龄。
并行分治场景会叠加,且没有全局视图。 SKILL.md 明确鼓励子 Agent 并行(「每个子 Agent 在当前用户浏览器实例中,自行创建所需的后台 tab」)。N 个子 Agent 各开若干 tab,单个子 Agent 并不知道其它子 Agent 已经开了多少,谁都没有超额的自觉。
/close依赖 LLM 自觉,是概率事件。 任务中途被打断、Agent 判断失误、报错提前退出、用户中止对话——任何一种都会留下没关的 tab,然后要等满 15 分钟才回收。
换句话说:现在的设计假设「Agent 会自觉关 tab,忘关的有超时兜底」,但缺一道并发数的硬闸。
建议方案
新增 CDP_MAX_TABS 环境变量(与现有 CDP_TAB_IDLE_TIMEOUT 风格一致),只统计 managedTabs,绝不把用户自己开的 tab 计入或关闭——保持现有的边界原则不变。
超限时建议「先软回收,再硬拒绝」:
- 先尝试回收一批闲置较久的 managed tab(比如按
lastAccessed取最旧的,或用一个短于TAB_IDLE_TIMEOUT的次级阈值),能腾出位置就正常创建; - 仍然超限则返回 429 + 结构化错误,让 Agent 自己决定关掉哪个:
{
"error": "已达同时打开 tab 上限(12/12)",
"hint": "请先 /close 不再需要的 tab;或提高 CDP_MAX_TABS 上限",
"managedTabs": ["TARGET_ID_1", "TARGET_ID_2", "..."]
}这个错误 Agent 读得懂、能自我纠正,符合 skill 一贯的「目标导向、让 Agent 自主判断」的设计,比静默 LRU 掉一个正在用的 tab 更安全(静默淘汰可能关掉一个刚 /new 出来、还没来得及操作的页面)。
顺带建议:/health 已经返回 managedTabs: managedTabs.size,可以再加上上限值,方便 Agent 在批量开页前自查余量。
默认值我倾向于 10~15 之间——足够覆盖绝大多数并行调研场景,又能挡住失控累积。保守起见也可以默认不限制、由用户在 config.env 里显式开启,不过考虑到崩溃是"发生了才知道"的事故,我个人更建议给一个安全的默认值。
补充:SKILL.md 侧
SKILL.md 里已经提示了「短时间内密集打开大量页面(如批量 /new)可能触发网站的反爬风控」,这里可以顺带补一句内存维度的提示,让 Agent 在分治时主动控制并发量。
Source: eze-is/web-access