实践

Browser Agent:共享 Chrome 运行时架构与接入手册

把真实 Chrome、固定 Profile、CDP 和恢复流程收敛为一个本地运行时;业务 Agent 只连接、创建页面并完成自己的任务。

作者:黄撑 更新于 2026-07-26
本文目录 7 节 · 点击展开

把多个 Agent 都接到同一个 Chrome,并不等于让它们共享同一个任务。真正应该被共享的是运行时:一个可见的真实 Chrome、一个固定 Profile、一条固定 CDP 通道和一套可恢复的生命周期。每个业务任务仍只拥有自己的页面、选择器与结果。

SHARED CHROME RUNTIME · FIELD GUIDE

先保证浏览器活着、可连接、可复用;再让 Agent 处理网页业务。

Browser Agent 把 Chrome 的启动与恢复从 Skill 中拿走。接入方只做两件事:调用 ensure,再连接 CDP。

  1. 01固定 Profile登录状态有唯一归属
  2. 02固定 CDP连接地址不再由项目各自分配
  3. 03独立 Page任务只关闭自己创建的页面
  4. 04显式边界默认串行,不把共享当作并发能力

先分清:谁管理运行时,谁完成网页任务

Browser Agent 不是采集器,也不是远程浏览器集群。它只在一台可执行本地命令的机器上管理 Chrome 进程、共享 Profile 和 CDP。业务 Skill 或 Python 项目负责进入网站、读取页面、提交表单或提取数据。

01 · RESPONSIBILITY MAP

共享的是底座,不是页面控制权

把生命周期和业务逻辑拆开后,故障归属与关闭权限也随之清晰。

业务层Skill / Python / 本地 AI Agent

调用 ensure、连接 CDP、创建自己的 page,执行该网站的选择器和业务逻辑。

↓ 连接,不启动自己的浏览器
控制层~/browser-agent/browser-agent

提供 ensurestatusconnectdisconnectstop 等统一入口;页面命令会先确保浏览器可用。

↓ 健康检查与恢复
运行时层真实 Google Chrome + 固定 Profile + 固定 CDP

保存登录状态与浏览器配置,并通过 http://127.0.0.1:19312 暴露调试连接。

职责边界:控制层负责“浏览器是否可用”;业务层负责“这个网页任务做什么、是否成功”。两边都不替对方猜测。

这样可以消除常见的重复劳动:每个项目各自寻找 Chrome、各自创建临时 Profile、各自抢端口、连接失败后各自重启进程。只要业务脚本遵守“只关闭自己 Page”的规则,任务结束时就不会主动带走其他任务仍在复用的共享 Chrome。

固定约定是一份连接合同

这套工具的当前默认配置将运行时放在用户目录下,使用本机回环地址的 19312 端口。上游配置的 Chrome 可执行文件默认值是 macOS 的 Google Chrome 路径;若部署在其他系统,必须通过配置确认可执行文件路径,而不是照抄默认值。

02 · CONNECTION CONTRACT

让每个接入方说同一种语言

路径与端口是运行时边界;Profile 本身承载登录态,不能当作普通项目文件。

控制入口
~/browser-agent/browser-agent所有 Agent 与本地项目的统一命令入口。
共享 Profile
~/browser-agent/chrome-profileCookie、本地存储和浏览器配置的持久化位置。
CDP 地址
http://127.0.0.1:19312Playwright 与命令行工具连接既有 Chrome 的回环地址。
默认会话
browser-agentPlaywright CLI 的会话名;可临时覆盖,但仍连接同一 Profile。
安全含义Profile 中可能包含登录凭据与站点状态:不要提交到 Git,不要把目录打包给不受信任的环境,也不要把 CDP 暴露到非本机网络。

如果要自定义路径、端口或会话名,先检查 config.env 中的 CHROME_BINCDP_PORTBROWSER_AGENT_SESSION。修改后是否仍能启动和连接属于运行环境事实,需运行验证。

ensure 把“能否连接”收敛为一个状态机

业务代码不应该先猜 Chrome 是否已启动。它应当把判断交给 ensure:脚本先访问 /json/version,再根据共享 Profile 进程与端口状态决定复用、报错或恢复。

~/browser-agent/browser-agent ensure
03 · ENSURE STATE MACHINE

复用优先;异常时先保护 Profile,再恢复 Chrome

这里的“恢复”只覆盖 Chrome 进程与 CDP 可用性,不验证某个网站是否仍处于登录状态。

输入执行 ensure

业务方不负责预检查。

CDP 就绪?请求 /json/version

端口可达并不自动代表 Profile 一定正确。

是 · 复用路径
复用确认匹配的 Profile 进程仍在

成立时不重复启动 Chrome。

直接返回 · ensure 结束
否 · 恢复路径
恢复结束异常的共享 Profile 进程

先尝试正常结束;仍存在时脚本才会强制结束。

清理检查端口,移除运行锁

清理 SingletonLockSingletonCookieSingletonSocket

启动以固定 Profile 和 CDP 启动 Chrome

等待 CDP 可访问后,接入方再连接。

CDP 就绪 · ensure 结束
明确失败:按当前脚本实现,CDP 已响应但找不到匹配的 Profile 进程,或端口检查发现 19312 被其他程序占用时,ensure 会报错,而非接管一个身份未知的浏览器。

这里有一个很重要的边界:ensure 解决的是“Chrome/CDP 是否可用”,不是“页面是否加载成功”“Cookie 是否过期”或“验证码是否已处理”。后几项仍要由业务任务观察并在需要时交给人处理。

接入顺序:保证运行时,再拥有自己的页面

外部 Python 项目最小接入步骤是:先运行 ensure,再以 CDP 附着到 Chrome;从已有 context 中新建页面;完成后只关闭该页面。下面的代码保留了连接与关闭边界,适合作为业务脚本的起点。

04 · ATTACH SEQUENCE

每个任务都从同一个入口进入,却只带走自己的 Page

CDP 连接附着的是已有 Chrome;它不应被业务任务当成私有浏览器来关闭。

  1. 1确保调用 browser-agent ensure
  2. 2附着connect_over_cdp() 连接固定地址。
  3. 3隔离从既有 context 新建本任务的 page
  4. 4收尾关闭该 page,保留 context、browser 与 Chrome。
import subprocess
from pathlib import Path

from playwright.sync_api import sync_playwright


browser_agent = Path.home() / "browser-agent" / "browser-agent"

subprocess.run([str(browser_agent), "ensure"], check=True)

with sync_playwright() as playwright:
    browser = playwright.chromium.connect_over_cdp(
        "http://127.0.0.1:19312"
    )
    context = browser.contexts[0]
    page = context.new_page()

    try:
        page.goto("https://example.com")
        # 只写当前项目的网站业务逻辑。
    finally:
        page.close()

如果该运行时没有可用 context,或 CDP 协议与客户端版本不兼容,上面示例会失败;这是接入环境而非网页业务本身的问题,需运行验证后再决定如何处理。

复用登录态,不复用页面控制权

同一 Profile 让不同任务看见同一份登录状态。上游规则仍明确要求默认不要并发操作,并让不同任务各自创建页面;因此页面隔离只是最低限度的协作约定,不是多租户安全边界。

05 · SHARE / ISOLATE

一份身份状态,多个独立任务页面

先识别什么被共享,才能避免把“新建页面”误当成账号或业务隔离。

共享Chrome 进程、Profile、Cookie、Local Storage、CDP 地址

它们由运行时维护,通常在任务结束后持续存在。

任务独有本任务创建的 Page、选择器、导航与结果

任务负责关闭自己的页面,并把结果写回自己的业务上下文。

不要触碰context.close()browser.close()、未授权的 stop

这些动作会影响共享运行时与其他任务,应保留给明确授权的运维操作。

并发规则默认同一时刻只让一个 Agent 操作共享 Chrome。

即使以不同 BROWSER_AGENT_SESSION 连接,它们依然控制同一 Profile;会话名不等于浏览器隔离。

验证码、扫码和账号安全验证不应尝试绕过。让用户在可见的 Chrome 中完成验证,再继续运行任务。对有写入、副作用或资金风险的网站,业务层还应自行设计确认、幂等和结果复核;Browser Agent 不替代这些控制。

日常命令:先看状态,再做可定位的动作

除了给 Python 接入,控制入口还封装了 Playwright CLI 的常用页面操作。它会在需要时附着到 CDP;其中 start 当前是 ensure 的别名,close 被显式拒绝,以避免误关共享 Chrome。

06 · OPERATOR RUNBOOK

把“连上了”变成可检查的日常操作

页面已经跳转、刷新或弹窗变化后,应重新获取快照;旧引用不应继续假定有效。

browser-agent ensure保证共享 Chrome 与 CDP 可用。
browser-agent status查看当前运行状态。
browser-agent snapshot读取最新页面结构与引用。
browser-agent click e12按最新快照中的引用执行操作。
browser-agent fill e20 "文本"填写指定引用对应的输入区域。
browser-agent screenshot保留页面可视证据。
browser-agent disconnect只断开 CLI 会话,不关闭 Chrome。
browser-agent stop仅在用户明确要求关闭共享浏览器时使用。

snapshotclickfill 中的引用值取决于当时的页面结构,示例里的 e12e20 不是可跨页面复用的固定选择器。实际页面命令、登录状态和站点反馈均需运行验证。

出现故障时,按影响范围回到正确层级

07 · FAILURE TRIAGE

不要用重启 Chrome 处理所有问题

先判断异常发生在运行时、连接、页面还是账号验证,再把动作限制在对应层级。

CDP 不可达先执行 ensure

若仍失败,检查端口占用、Chrome 可执行文件、Profile 权限和脚本输出。

能连上但页面异常重新 snapshot,检查 URL 与页面状态

这是网站或任务逻辑问题;不要仅因元素找不到就结束共享 Chrome。

登录失效或出现验证码停在可见 Chrome,交给用户

登录状态保存在 Profile,但过期与安全验证仍是站点的业务边界。

多个 Agent 相互干扰停止并发,明确串行所有者

不同 CLI 会话不提供页面或身份隔离;先完成当前任务再交接。

最终原则:共享运行时降低重复维护成本,但不取消操作授权、网站规则、人工验证和业务结果复核。
TAKEAWAY把 Chrome 当作受管理的本地基础设施:入口统一、身份固定、连接可恢复、页面各自负责;只要不越过共享边界,业务 Agent 就能把精力放在网页任务本身。

项目仓库:apexcheng/browser-agent