
这两天圈子里有个话题讨论度很高腾讯开源了一个项目让 AI 能直接接管你已经登录好的浏览器实例。意思是你本地正开着的 Chrome 或 EdgeAI 可以直接连上去带着你的登录态去打开网页、填表单、点按钮、抓数据。它不再像传统自动化工具那样启动一个干净的全新浏览器而是直接复用你当前浏览器的上下文。这个思路我觉得非常有意思因为它精准击中了 AI 操作网页类应用最头疼的问题登录认证。以前我们做浏览器自动化最大的坎就是各种登录验证扫码、短信、验证码、多因素认证每一道都能把自动化流程卡死。如果 AI 能直接用你已经登录好的浏览器这些麻烦几乎全部消失。这篇文章我就把这个项目的技术原理、落地方式和我在实际使用中踩过的坑一次性讲清楚。1. 项目整体设计与核心思路拆解1.1 一句话说清它到底在做什么传统 AI 操作网页的方案通常是启动一个全新的、干净无状态的浏览器实例。这个实例里没有任何 Cookie没有登录记录也没有历史浏览数据。AI 想访问一个需要账号的页面就得现场走一遍完整的登录流程——这在自动化场景里极其痛苦。腾讯开源的这个方案换了个路径不创建新浏览器而是直接连接一个用户已经在用的浏览器实例。AI 通过远程调试协议接进去拿到当前浏览器里已登录的会话状态然后以这个身份去执行各种操作。说白了就是一句话AI 不只是有一双手它还借了你的身份卡。这个思路的价值很直接登录态这种最难自动化的部分完全交给用户自己处理。你要 AI 帮你操作哪个网站自己先把那个网站登录好剩下的 AI 去执行就行。登录之后的事才是 AI 该做的事。1.2 为什么复用已登录浏览器是刚需做过网页自动化的人应该都有共鸣自动化流程十次失败里有八次是死在登录环节。验证码是第一个拦路虎。图形验证码、滑块验证、点选验证层出不穷识别成本极高。第二道是扫码登录很多网站强制要求 App 扫码确认这对完全无人值守的自动化任务来说基本是死路。第三道是短信验证和多因素认证就算你能收验证码还得在自动化逻辑里额外处理短信解析复杂度直接翻倍。更麻烦的是有些系统的登录态本身就有时效性比如企业内部的办公系统、云控制台、托管后台动不动就要求重新认证。如果你搞的是定时任务半夜登录态过期了整个流程就白跑了。复用已登录浏览器这些痛点全都在源头消失。用户自己在浏览器里完成了登录、完成了各项安全验证AI 只是借道去用这个会话。这个设计符合一个基本原则让用户做人擅长的事让 AI 做 AI 擅长的事。认证交给人操作交给机器分工清晰。1.3 什么是真正适合用这套方案的场景我实际用下来这套方案最适用的人群有三类第一类是写 AI Agent 的开发者。你是想让 AI 替用户处理具体网页任务而不是写一个通用登录机器人。有了登录态复用Agent 的代码可以很干净完全不用纠缠认证逻辑。第二类是测试工程师。很多 Web 项目的回归测试卡在登录态。以前的做法是写一套自动化登录脚本每次跑测试前先登录一次时间消耗大还经常因为测试环境账号被锁定导致失败。复用登录态之后测试脚本直接连接已就绪的浏览器实例打开就能干活。第三类是个人效率工具爱好者。让 AI 帮你在某个后台批量处理事项、整理报表、执行重复性操作。这类场景通常不需要大规模并发也不需要部署到服务器本地跑就够刚好适合这套方案。如果你只是偶尔用一下浏览器没有自动化需求这个项目对你帮助不大。但如果你是上面三类人之一它很值得研究。2. 核心技术原理AI 是怎么接管浏览器的2.1 CDP 协议是这一切的地基AI 能接管浏览器靠的是 Chrome DevTools Protocol简称 CDP。这是 Chrome 提供的一套远程控制协议本质是一个 WebSocket 接口外部程序可以通过这个接口向浏览器实例发送指令控制页面导航、点击、输入、截屏、执行 JavaScript几乎覆盖了 DevTools 里所有可操作的能力。这套协议就是 DevTools 背后的技术支持。你平时按 F12 打开的开发者工具所有的面板功能都是通过 CDP 实现的。当你用工具连上浏览器调试端口做的事情和普通程序员按 F12 调试网页没有本质区别只是把人按按钮换成了程序发指令。CDP 有两个端点比较重要。一个是 HTTP 端点访问http://localhost:9222/json/version能拿到浏览器版本和 WebSocket 地址另一个是 WebSocket 端点专门用来收发控制指令。AI Agent 连接的就是 WebSocket 端点所以浏览器得先把 CDP 服务暴露出来。2.2 登录态到底存在哪儿AI 是怎么继承的知道 CDP 能控制页面还不够关键问题是已登录状态从哪里来。登录态本质上存储在浏览器客户端的本地数据里主要包括Cookie包含会话标识、用户 token、偏好设置等localStorage常用于存放前端持久化数据和用户信息sessionStorage会话级别数据关标签页就清IndexedDB用于较大规模的客户端数据存储这些数据都归属到具体的浏览器 profile 目录下。当你用浏览器登录一个网站服务端返回的会话凭证就被写进了这些存储中。你关掉浏览器再打开登录态还在就是因为这些数据被持久化到了本机。现在重点来了AI 连接的是同一个浏览器实例自然就继承了这份登录态。它不需要知道你的密码不需要认识验证码它只是替你使用你已经完成认证的会话。打个比方这就好比你把已经刷好门禁卡的工牌挂在机器人身上让机器人替你进入办公室取东西而不是给机器人一套开锁工具让它自己破解。2.3 完整交互流程拆解一次完整的操作流程大致是这样的用户在本地启动浏览器并带上调试端口参数浏览器会开一个 CDP 监听端口等待外部连接用户登录需要的网站或浏览器恢复之前已登录的会话AI Agent 通过 CDP 端口连接上这个浏览器实例Agent 拿到当前浏览器里的页面列表和上下文Agent 按任务需求打开新页面或复用已有页面通过 CDP 执行点击、输入、滚动、读取数据等操作操作完成后Agent 把结果汇总返回给用户这里面有一个值得注意的设计细节AI 拿到的不是一个无头浏览器而是一个完整的有界面浏览器实例。你在电脑上能眼睁睁看到 AI 在操作你的浏览器页面上光标自己移动、文字自己输入、按钮自己点。这种人类看着 AI 干活的体验让用户对 Agent 的信任感强很多也便于随时干预。我用一个表格对比传统方案和这套方案的差异对比项传统全新实例复用已登录实例登录态无需要重新认证有直接继承验证码处理需要单独集成识别服务不需要首次启动耗时秒级零等待浏览器已在用操作可见性多数是无头模式不可见可见可实时干预网络身份全新会话可能触发风控与日常行为一致实现复杂度登录逻辑复杂几乎没有登录逻辑这个对比能很清楚地看出为什么这类方案在 Agent 场景里越来越受欢迎。3. 从零到一实际操作与完整接入流程3.1 第一步把一个已登录浏览器以调试模式启动这一步是整个流程的起点也是最容易踩坑的地方。如果你直接在命令行执行 Chrome 并指定远程调试端口会发现现有的 Chrome 进程总是抢占参数新指令不生效。这是因为 Chrome 默认会检测到已有实例并转而打开一个新窗口而不是新起一个带调试端口的进程。解决办法是先完全退出浏览器再启动。以 Chrome 为例启动命令如下WindowsC:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port9222 --user-data-dirC:\chrome-debug-profilemacOS/Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port9222 --user-data-dir/tmp/chrome-debug-profileLinuxgoogle-chrome --remote-debugging-port9222 --user-data-dir/tmp/chrome-debug-profile两个参数都很关键--remote-debugging-port9222指定调试端口--user-data-dir指定用户数据目录。第一次用这个新的 user-data-dir 启动时浏览器就像一个刚装好的全新浏览器你需要手动登录一次目标网站。之后关闭再启动登录态都会保留在这个目录里。用 Edge 的朋友把路径换成 Edge 的可执行文件路径参数完全一样。启动成功后浏览器窗口顶部会出现一个提示条写着Chrome 正受到自动测试软件的控制。这个提示是正常的说明调试端口已经生效不用慌。验证端口是否生效可以在浏览器地址栏打开http://localhost:9222/json/version如果能看到一坨 JSON里面有Browser、webSocketDebuggerUrl字段说明环境就绪。提示一定不要直接加--remote-debugging-port9222去连接你平时用的默认 profile。一来很容易和现有浏览器实例冲突二来把主力浏览器长期暴露在调试端口下风险太高。单独建一个专用目录更安全、更好管理。3.2 第二步用 Playwright 连接现有实例环境就绪后接入方式有很多种。我平时用的最顺手的是 Playwright它对 CDP 的支持很完善代码也简洁。安装依赖pip install playwright然后写一个最简单的连接脚本from playwright.sync_api import sync_playwright with sync_playwright() as p: # connect_over_cdp 表示连接现有浏览器而不是新启一个 browser p.chromium.connect_over_cdp(http://localhost:9222) # 获取浏览器里已有的上下文 contexts browser.contexts print(已有上下文数量:, len(contexts)) # 默认场景下第一个 context 就是桌面浏览器的主上下文 context contexts[0] pages context.pages print(当前打开页面:, [page.url for page in pages]) # 新开一个标签页并访问目标网站 page context.new_page() page.goto(https://example.com) print(页面标题:, page.title()) # 看看当前上下文中已有的 Cookie cookies context.cookies() print(Cookie 数量:, len(cookies)) # 断开连接注意不会关闭你的原浏览器窗口 browser.close()运行这个脚本你会看到它输出的 Cookie 数量里包含了你实际登录过的网站凭证。这个细节非常关键说明登录态确实被继承了。需要注意browser.close()在connect_over_cdp模式下只是断开连接并不会关掉你原来的浏览器窗口。这是 Playwright 的一个取舍设计实际体验下来很合理避免误杀用户的浏览器。3.3 第三步把浏览器操作能力接入 AI Agent连接校验通过之后下一步就是把浏览器操作封装成 AI 能调用的工具。如果你在用语言模型做 Agent通常会给模型准备一批工具函数模型根据用户的自然语言指令决定调用哪个工具、传什么参数。浏览器操作就可以封装成这么几个核心函数open_url(url)打开指定网址click_element(selector)点击页面元素fill_input(selector, text)填充输入框read_page_text()读取当前页面主要文本screenshot()对当前页面截图execute_js(code)执行一段自定义 JavaScript下面我用一个简化示例演示怎么把 Playwright 操作封装成一个可以被 LLM 调用的工具import json from playwright.sync_api import sync_playwright class BrowserAgent: def __init__(self, cdp_urlhttp://localhost:9222): self._playwright sync_playwright().start() self.browser self._playwright.chromium.connect_over_cdp(cdp_url) self.context self.browser.contexts[0] self.page self.context.new_page() def open_url(self, url): self.page.goto(url, wait_untilnetworkidle) return {status: ok, title: self.page.title()} def fill_input(self, selector, text): self.page.fill(selector, text) return {status: ok} def click_element(self, selector): self.page.click(selector) return {status: ok} def read_page_text(self): text self.page.inner_text(body) return {status: ok, content: text[:2000]} def screenshot(self): path /tmp/agent_screenshot.png self.page.screenshot(pathpath) return {status: ok, path: path} def close(self): self.browser.close() self._playwright.stop()如果你用 LangChain、AutoGPT 这类框架把上面的函数注册为工具的代码量也不大。以 LangChain 为例from langchain.agents import tool tool def open_url(url: str) - str: 在已登录的浏览器中打开指定网页 agent.page.goto(url, wait_untilnetworkidle) return f已打开 {url}标题为 {agent.page.title()} tool def fill_input(selector: str, text: str) - str: 向指定选择器的输入框填写内容 agent.page.fill(selector, text) return 填写完成 tool def click_element(selector: str) - str: 点击指定选择器的元素 agent.page.click(selector) return 点击完成这个阶段核心的工作量不在代码而在把常见的浏览器操作动作拆成粒度合适的工具函数。拆太粗AI 无法精细操作拆太细工具列表太长模型会很难选。我个人的经验是控制十个以内的工具函数最为理想。3.4 一个完整的端到端示例假设任务需求是打开某个后台管理页面把表格第一行的数据读出来。代码流程可以这样agent BrowserAgent() # 注入已登录浏览器直接打开后台 agent.open_url(https://your-admin.example.com/dashboard) # 等待页面加载完成 agent.page.wait_for_selector(table tbody tr, timeout10000) # 读取第一行内容 row agent.page.inner_text(table tbody tr:first-child) print(第一行数据:, row) # 截图确认 agent.screenshot() agent.close()整个过程完全没有登录逻辑。前提就是你已经在浏览器里访问过这个后台且保持登录态。4. 典型应用场景与实战扩展4.1 免登录的数据采集与报表生成我最早用这套方案是做一个内部的运营数据采集脚本。以前要频繁登录一个第三方数据后台去拉报表每次登录都要经过一次短信验证。后来改用复用登录态方案之后脚本定时连接调试端口打开后台直接抓取数据跑了一个月都没出过问题。这个场景最有价值的点在于绕过了所有登录验证机制采集的稳定性大幅提升。过去可能因为验证码服务超时导致任务失败现在这种问题完全不存在了。4.2 AI 驱动批量后台操作很多运营、行政、客服类工作都牵扯到网页后台的重复性操作。比如批量给用户打标签、批量审批流程、批量修改配置。这些操作单个点起来不难但量大就特别枯燥。把这类操作交给 AI 助理去做体验就很顺。你只需要用自然语言告诉它把订单列表里所有状态为待处理的订单标记为已审核它会自动打开页面、筛选、批量勾选、点击处理按钮。因为浏览器里有你的登录态它全程不会掉线也不会要求你重新认证。4.3 Web 自动化测试的降本增效做 Web 端自动化测试的同行对登录态应该深有感触。传统自动化框架比如 Selenium测试套件跑起来之前往往要先执行一个登录用例。这个用例在 CI 环境还好本地开发时是真的烦每改一次脚本就要重新登录一次登录久一点还要提心吊胆验证码识别。用复用登录态方案之后本地调试测试用例时只需连上自己手动登录好的浏览器测试脚本开头直接跳过 Login 步骤用例跑起来快很多也不需要维护一堆测试账号和测试密码。4.4 个人 AI 上网助理最近很火的 AI Agent 浏览器助理底子其实就是这一套。给 AI 配上一个能实时看到屏幕、能点击输入的浏览器让它帮你查快递、查账单、预约服务、处理工单。因为浏览器是你日常在用的AI 的访问行为和你自己访问没什么区别页面里的个性化内容、已登录数据它都能看到。这比冷启动的自动化浏览器靠谱得多。5. 安全边界与隐私注意事项5.1 调试端口等同于浏览器后门这是整套方案里最需要强调的一个点。当你给浏览器开了 CDP 调试端口相当于把浏览器的完整控制权交给了能访问这个端口的人。对方不仅能打开页面、点击按钮还能读取你的 Cookie、 localStorage、历史记录甚至可以执行任意 JavaScript直接把你的登录凭证偷走。端口默认只绑定在localhost也就是只有本机进程能访问。如果你图方便改成绑定所有网卡比如--remote-debugging-address0.0.0.0那局域网内任何一台机器都能试图连接你的浏览器这是极其危险的操作。我个人强烈不建议这么做。如果你确实需要远程连接到另一台机器上的浏览器应该走安全的加密通道并且只在受控环境里临时使用。这个通道怎么搭属于网络运维的范畴这里不展开核心原则是未经加密和认证的通道不能碰。5.2 单独 Profile 隔离是底线这也是我开头强调过的不要拿主力浏览器长期挂调试端口。主力浏览器里几乎有你全部重要的登录态一旦端口发生任何意外泄露损失不可估量。更稳妥的做法是单独建立一个专用的 user-data-dir里面只登录运行任务所需的网站。这个目录可以随时删除重建隔离了任务数据和你的个人数据。即使浏览器被恶意指令控制泄露出去的也只是这一个专用目录里的少量登录态。5.3 操作留痕与最小授权给 AI 赋权之后最好把它的操作记录下来。最简单的做法是在每个工具函数里加一行日志记录什么时间、执行了什么操作、访问了哪个 URL。出了问题可以回溯也方便观察 AI 的决策是否合理。我自己的习惯是所有 AI 操作都在一个独立上下文里跑用完马上关闭相关页面不让 AI 一直保持着对重要系统的访问权。权限用得越少风险面就越小。6. 常见问题与排查技巧6.1 连接失败端口连不上最常见的原因是浏览器没有真正带调试端口启动。很多人开着平时用的 Chrome然后在命令行执行带参数的启动命令结果只是弹了个新窗口9222 端口根本没有监听。排查方法就两步先访问http://localhost:9222/json/version看有没有 JSON 返回没有就确认一下是否已经把所有 Chrome 进程都退干净了再重新启动。另外注意端口是否被占用被其他进程占了也可以换个端口比如--remote-debugging-port9223。6.2 登录态失效上次登录白登了登录态丢失大概率是 user-data-dir 不一致。你手动打开浏览器登录时用的可能是默认的 profile 目录但启动调试模式时指定的却是另一个目录两者数据不互通登录态当然不存在。解决方法是统一目录。要么手动登录时就用同一个带参数的命令启动浏览器要么把登录好的 profile 内容原样复制到调试模式的目录中。记住一个原则调试端口指向哪个 user-data-dirAI 继承的就是哪个目录的登录态。6.3 AI 操作卡住或犹豫不决浏览器页面加载是异步的AI 往往在页面还没加载完就尝试操作导致读不到元素或点击失败。这个问题通过等待机制解决。在封装工具函数时统一加等待逻辑self.page.wait_for_selector(selector, timeout10000)如果页面是大量异步渲染的 SPA 应用建议加上网络空闲判断self.page.wait_for_load_state(networkidle, timeout15000)部分复杂页面会出现弹窗、浮层遮挡元素这种情况我会在点击前先尝试关闭弹窗或者用更稳定的 XPath 定位。这些都是老生常谈但实际用起来作用很大。6.4 被网站识别为自动化工具有一些网站会做反自动化检测识别到navigator.webdriver标记或异常的操作行为特征。既然你复用的是一个真实登录过的浏览器本身就比全新实例少很多特征。但 CDP 附加的会话有时还是会被打上自动化标记。减少痕迹的几个常规做法启动时加--disable-blink-featuresAutomationControlled参数以及在连接后手动删除自动化相关的 JS 属性。不过要强调一下这不是为了做任何违规的事只是让大家知道组件检测的机制。合规使用的时候这些技巧能让流程稳定很多。6.5 常见问题速查表现象可能原因解决办法9222 端口访问无响应浏览器未带调试参数启动完全退出浏览器后重新用参数启动登录态丢失user-data-dir 与登录时不一致统一使用同一用户数据目录Playwright 找不到已有页面上下文/页面对象获取方式不对使用browser.contexts[0]而不是新建 context页面元素点击无效页面还在加载或元素被遮挡加 wait_for_selector先处理弹窗浏览控制台出现自动化提示条调试模式正常现象忽略即可AI 执行 JS 报权限错误部分页面 CSP 限制通过 CDP 的 Runtime domain 注入而非页面内执行连接后浏览器被误关闭接错了关闭方法使用断开连接而非强制关闭这套方案我前前后后用了大概半年从最初的验证概念到后来接进真实的 Agent 流程体会最深的一点是它最大的价值不是技术上的突破而是把人的身份和AI 的能力很自然地衔接在一起省掉了自动化里最折腾的认证环节。如果你手头正好有AI 替你操作网页的需求建议先拿一个小任务跑通全流程感受一下什么叫浏览器交给 AI登录交给用户。最后再分享一个小技巧如果你经常在不同的任务里复用这套方案可以给调试端口写一个简单的开关脚本一键启动专用配置的浏览器、一键退出省得每次手动敲命令。工具虽小但能让日常开发效率提升一大截。