Mac 发热?AI 可能忘了关浏览器

Mac 发热?AI 可能忘了关浏览器
Claude Code、Codex 调用 agent-browser 后留下后台 daemon 和 Chrome 进程?本文讲清根因、排查命令、安全清理方式与长期预防配置。
Claude Code、Codex 明明已经停下来了,Mac 风扇却还在转,内存也迟迟不降。问题可能不在当前会话,而在一批没有正常收尾的
agent-browserdaemon 与自动化 Chrome。
这次我在本机清理了 8 组遗留的 agent-browser / Chrome 后台实例及其子进程。普通 Chrome 标签页没有动,机器热量很快就降了下来。
这不是一个玄学问题,也不能简单归咎于“Chrome 太吃内存”。真正要理解的是:AI 编程工具、浏览器自动化工具、CDP、daemon 和 session 之间,到底是谁启动了谁,又该由谁负责关掉。
30 秒结论
先给赶时间的人答案:
agent-browser采用 CLI + 后台 daemon + Chrome 的架构,daemon 会故意跨命令常驻,以便下一条指令不用重新启动浏览器。- 每个独立 session 都可能拥有自己的浏览器实例。Claude Code、Codex 并发执行多个 agent 或工具任务时,session 数量很容易叠加。
- 如果任务异常中断、会话被强制停止,或者调用方忘了执行
agent-browser close,后台进程就可能继续存在。 - CDP 只是控制通道,不是泄漏的根因。根因是 浏览器生命周期没有闭环。
- 最优先执行官方收尾命令:
agent-browser close --all。只有正常关闭失败,才按特征匹配并发送TERM,最后才考虑KILL。 - 长期方案是设置空闲超时、复用 session,并要求每个自动化任务在
finally或 shelltrap中关闭自己的 session。
目录
- 一、现象:AI 停了,浏览器却没停
- 二、根因:常驻 daemon 遇上并发 session
- 三、先确认,不要上来就 kill
- 四、三层清理:官方关闭、温和终止、强制清理
- 五、怎样保证不误伤普通 Chrome
- 六、从源头预防:让生命周期闭环
- 七、可直接收藏的排查清单
一、现象:AI 停了,浏览器却没停
典型场景是这样的:
- 你让 Claude Code 或 Codex 通过 skill、MCP、shell 工具打开网页;
- 自动化任务创建了多个命名 session,甚至并行跑了多个子 agent;
- 任务完成或会话终止后,终端已经安静;
- 但“活动监视器”里仍有多组 Chrome、Chrome Helper、GPU、Renderer 或 agent-browser 进程;
- 合上几次窗口也没用,Mac 继续发热,内存和 CPU 占用居高不下。
最迷惑的一点是:这些进程通常没有可见窗口。你看到的普通 Chrome 标签页可能只有几个,但后台自动化 Chrome 使用的是独立临时用户目录,它们和日常浏览器不是同一组实例。
因此,“我已经关掉 Chrome 窗口了”并不能证明自动化浏览器已经退出。
二、根因:常驻 daemon 遇上并发 session
以本机安装的 agent-browser 0.27.0 为例,官方说明明确写了两件事:
- 它采用 client-daemon 架构,Rust CLI 负责接收命令,Rust daemon 通过 CDP 控制 Chrome;
- daemon 会在第一次命令时自动启动,并在后续命令之间保持运行,以换取更快的响应。
与此同时,官方 session 文档也说明:每个 session 都有独立的浏览器实例、Cookie、存储、历史记录和认证状态。
这两点放到多 agent 场景里,就形成了下面的链路:
1 | Claude Code / Codex |
单看一组并不可怕。真正吃资源的是“多组浏览器进程树一起常驻”。一个 Chrome 主进程还会派生渲染、GPU、网络等子进程,所以 8 个遗留实例在活动监视器里看起来可能远不止 8 行。
CDP 是不是罪魁祸首?
不是。
CDP(Chrome DevTools Protocol)只是自动化工具与 Chrome 之间的控制协议。它负责导航、点击、读取 DOM、截图等操作,但不会替调用方决定“任务什么时候算结束”。
真正缺失的是这条生命周期:
1 | 创建 session → 使用浏览器 → 任务成功或失败 → 关闭 session → 回收 daemon 和 Chrome |
如果最后两步没有执行,前面的资源就可能继续留在后台。
关键判断: 不是 AI “还在思考”,而是 AI 启动的外部进程已经脱离了当前对话的可见范围。
三、先确认,不要上来就 kill
清理前先分清三件事:有多少活动 session、有哪些 agent-browser daemon、哪些 Chrome 使用了 agent-browser 的临时用户目录。
1. 查看 agent-browser 自己记录的活动 session
1 | agent-browser session list |
如果版本支持 JSON:
1 | agent-browser session list --json |
这一步最可信,因为它从工具自己的会话视角回答“现在认为谁还活着”。
2. 查看 daemon 和自动化 Chrome
macOS 上可以使用:
1 | pgrep -fl '/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)' |
第二条命令的关键不是进程名“Google Chrome”,而是启动参数中的:
1 | --user-data-dir=.../agent-browser-chrome-... |
它说明这个 Chrome 使用的是 agent-browser 创建的隔离用户目录,而不是你日常使用的默认 Chrome Profile。
3. 按 CPU、内存和运行时间查看完整进程树
1 | ps -axo pid,ppid,%cpu,%mem,rss,etime,command \ |
字段含义:
PID:当前进程编号,后续终止时使用。PPID:父进程编号,用来判断 daemon 与 Chrome 的父子关系。%CPU:CPU 占用,观察它是否持续让机器发热。%MEM/RSS:内存占用,确认多个实例是否不断叠加。ETIME:已运行时间,判断它是否早已超过当前 AI 任务时长。COMMAND:完整启动命令,确认是否带有 agent-browser 特征参数。
不要只看 ~/.agent-browser 下有没有 .pid、.sock 文件。它们可能只是过期的 sidecar 文件,并不等于进程仍然存活。进程是否活着,最终以 session list、ps 和 pgrep 为准。
四、三层清理:官方关闭、温和终止、强制清理
第一层:让 agent-browser 自己收尾
优先执行:
1 | agent-browser close --all |
然后清理过期的 pid、socket、version 等 sidecar 文件:
1 | agent-browser doctor --offline --quick |
当前版本的 doctor 会自动清理陈旧的 daemon sidecar 文件;--fix 还可能执行重装 Chrome、清理旧状态等破坏性修复,因此日常排查先不要加 --fix。
再次验证:
1 | agent-browser session list |
如果没有输出,说明已经清干净。
第二层:发送 TERM,让进程有机会优雅退出
只有官方关闭失败时再执行:
1 | pgrep -f '/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)' \ |
TERM 会给进程处理退出、关闭文件和回收子进程的机会。执行后等 3 秒,再跑一次检查命令。
第三层:只对仍残留的目标使用 KILL
1 | pgrep -f '/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)' \ |
KILL 不允许进程执行清理逻辑,只适合处理已经卡死、无法响应 TERM 的残留。正在进行的自动化、下载、表单填写和未保存状态都会立即丢失。
仓库里放一个可重复使用的脚本
本文配套脚本已放在:
1 | scripts_py/agent_browser_cleanup.sh |
使用方式:
1 | ./scripts_py/agent_browser_cleanup.sh status |
三条命令依次对应:只查看、官方关闭并清理 sidecar、官方关闭失败后的分级强制清理。
五、怎样保证不误伤普通 Chrome
不要使用下面这种“大锤”:
1 | killall 'Google Chrome' |
它们会把普通 Chrome 窗口和日常标签页一起关掉。
本文脚本采用两层限制:
- daemon 必须匹配
agent-browser-darwin-arm64或agent-browser-darwin-x64的安装路径; - Chrome 必须带有
--user-data-dir=.../agent-browser-chrome-...参数。
也就是说,它针对的是 agent-browser 创建的隔离 Chrome,不是所有名为 Google Chrome 的进程。
不过还有一个例外需要注意:如果你主动使用 --cdp 或 --auto-connect 连接了日常 Chrome,那么自动化 session 与普通浏览器之间的边界会变得更近。此时先用 session list 和完整命令行确认,不要直接执行强制清理。
六、从源头预防:让生命周期闭环
清理脚本只是灭火器。更好的办法是让每个任务都负责关掉自己打开的浏览器。
1. 设置 daemon 空闲自动退出
官方提供了 AGENT_BROWSER_IDLE_TIMEOUT_MS。例如 10 分钟无命令后自动关闭浏览器并退出 daemon:
1 | export AGENT_BROWSER_IDLE_TIMEOUT_MS=600000 |
长期使用可以写入 ~/.zshrc:
1 | echo 'export AGENT_BROWSER_IDLE_TIMEOUT_MS=600000' >> ~/.zshrc |
10 分钟只是一个稳妥起点。频繁执行长任务可以设为 30 分钟;偶尔使用、机器资源紧张时可以设为 3 到 5 分钟。
2. 不要为每一步创建新 session
同一个任务尽量复用一个语义明确的 session:
1 | agent-browser --session blog-research open https://example.com |
并发任务确实需要隔离时,可以创建多个 session,但每个任务结束后都要单独关闭。
3. 在 shell 脚本里用 trap 兜底
1 | set -euo pipefail |
无论脚本正常结束、报错,还是收到中断信号,cleanup 都会尝试关闭对应 session。
4. 给 Claude Code / Codex 的浏览器 skill 加退出规则
长期指令里应明确写上:
1 | 浏览器自动化任务结束后,必须关闭本任务创建的 agent-browser session。 |
这比“任务结束后记得关浏览器”更有效,因为它定义了成功、失败和取消三条路径。
5. 定期做轻量检查
如果你经常让多个 agent 并发操作网页,可以在结束工作前运行:
1 | agent-browser session list |
发现不认识、运行时间异常长的 session,再决定是否关闭。不要把 force 放进高频定时任务,因为它可能打断仍在工作的自动化。
七、可直接收藏的排查清单
遇到 Mac 突然发热、内存异常时,按这个顺序走:
- 打开“活动监视器”,确认是否有多组无窗口的 Chrome / agent-browser。
- 运行
agent-browser session list,确认活动 session。 - 用
pgrep -fl或ps检查 daemon 与agent-browser-chrome-用户目录。 - 先执行
agent-browser close --all。 - 再执行
agent-browser doctor --offline --quick清理陈旧 sidecar。 - 等待几秒并复查 CPU、内存和进程。
- 仍有残留时先
TERM,最后才KILL。 - 设置
AGENT_BROWSER_IDLE_TIMEOUT_MS。 - 在每个自动化脚本或 skill 中加入
finally/trap清理。 - 避免
killall Google Chrome这类会误伤普通标签页的命令。
最后
浏览器自动化最容易被忽略的,不是“怎么打开网页”,而是“谁负责关闭网页背后的整棵进程树”。
Claude Code 和 Codex 能把网页操作拆成工具调用,却不会天然替所有第三方工具完成生命周期管理。只要调用链中存在 daemon、session 和独立 Chrome,就应该把清理当成任务的一部分,而不是任务结束后的家务。
真正可靠的自动化,不只要能启动,还要能在成功、失败和取消时都干净退出。
如果你的 Mac 也曾在 AI 会话结束后莫名发热,先别急着重启。跑一下本文的检查命令,很可能几分钟就能找到那批“已经下班,但进程还没打卡”的浏览器。












