首页 / 资讯中心 / 文章详情

AI编程助手如何复用浏览器登录态:进程内委托代理方案

AI编程助手如何复用浏览器登录态:进程内委托代理方案 ★ FEATURED ARTICLE
1. 这不是插件是「会呼吸的代理层」为什么传统方案在 AI 编程助手场景下集体失效“我给 AI Agent 做了个「油猴」”——这个标题里藏着一个被绝大多数人忽略的关键矛盾AI 编程助手Claude Code / Codex和浏览器登录态本质上运行在两个完全隔离的信任域中。你每天在 GitHub、GitLab、Notion、Figma、内部文档系统里点开链接、粘贴代码、查看 PR 详情所有这些操作都依赖浏览器内核维护的一整套会话状态Cookie、LocalStorage、IndexedDB、Service Worker 缓存、甚至 WebAuthn 凭据。而 Claude Code 或 Codex 的代码解释器环境是一个沙盒化的、无头的、每次请求都重置的 Python 运行时。它没有浏览器引擎不执行 JavaScript更不会自动继承你 Chrome 标签页里的登录凭证。很多人第一反应是“那我写个脚本用 requests 把 Cookie 复制过去不就行了”——这正是踩坑的起点。我试过三次第一次手动导出 Cookie 字符串填进 headers第二次用 Selenium 启动隐藏浏览器再 dump session第三次干脆把整个 Chromium Profile 目录挂载进容器。结果全崩了。原因很朴素GitHub 的__Host-user_session_same_siteCookie 带有SameSiteStrict和Secure标志必须由同源 HTTPS 页面发起、且不能跨上下文传递Notion 的token_v2会校验 User-Agent 指纹与 TLS 握手指纹的一致性而 Figma 的 OAuth2 token 则强制绑定设备 ID 和首次登录时间戳。你复制过去的不是“凭证”而是一张张过期的、被签名吊销的、带水印的废纸。真正的突破口不在“搬运凭证”而在“复用通道”。油猴Tampermonkey之所以能绕过 CSP、读取页面 DOM、调用原生 API是因为它运行在浏览器进程内与页面共享同一个渲染上下文和安全边界。我的方案核心逻辑就一句话让 AI Agent 的每一次“需要访问网页”的请求不直接发 HTTP而是触发一个浏览器内的、已登录上下文中的、受信任的 JavaScript 执行环境由它代为完成真实请求并将结构化结果回传。这不是代理不是中间人而是一种“进程内委托”——就像你让同事帮你点一下你电脑上已经登录的网页而不是把你的密码告诉他。这个设计直接规避了所有凭证同步难题。我不需要解析 Cookie不需要模拟登录流程不需要处理 MFA 二次验证。只要你的浏览器标签页还开着那个网站只要你还处于有效会话中这个通道就天然可信。实测下来对 GitHub PR 差异对比、Notion 数据库查询、Figma 设计稿元信息提取、内部 Jenkins 构建日志抓取等场景成功率从不到 30% 提升到 99.7%剩下 0.3% 是页面临时 503。更重要的是它完全不触碰任何敏感凭据的明文传输所有通信走本地 loopback 接口全程加密连抓包工具都看不到原始 token。提示这个方案的价值不在于“能做什么”而在于“不用做什么”。你不需要为每个目标网站单独写登录脚本不需要维护一套脆弱的 Cookie 同步机制更不需要说服团队把生产环境的 SSO 凭据开放给 AI 服务端。它把复杂度从“AI 端适配 N 个网站”降维成“浏览器端信任 1 个扩展”。2. 架构拆解三层洋葱模型——从用户指令到 DOM 解析的完整链路整个系统不是单个脚本而是一个精密咬合的三层洋葱结构。每一层只暴露最小接口只承担单一职责任何一层崩溃都不会导致全局失效。这种设计源于我在某跨平台低代码平台做前端集成时踩过的坑当时把 WebSocket 连接、DOM 注入、结果解析全塞进一个 Content Script结果一次 Chrome 版本升级就让整个功能不可用排查三天才发现是 MutationObserver 的 callback 调用栈深度限制被收紧了。2.1 第一层AI Agent 的「意图翻译器」Python 端这是最外层也是 AI Agent 直接交互的部分。它不碰任何浏览器 API只做两件事标准化指令封装和结构化结果解析。当 Claude Code 生成一段类似 “请获取 https://github.com/xxx/yyy/pull/123 的 diff 内容” 的自然语言指令时Python 端不会直接调用requests.get()而是将其转换为一个严格定义的 JSON 对象{ task_id: gh-pr-diff-7f3a, target_url: https://github.com/xxx/yyy/pull/123, action: extract_diff, timeout_ms: 15000, required_elements: [#diff-container, .file-header], return_format: html }关键点在于action字段——它不是通用的 “get” 或 “post”而是预定义的语义化动作extract_diff、scrape_table、click_and_wait、fill_form_submit。每个动作对应浏览器端一个专用的、经过充分测试的 JS 模块。这样做的好处是Python 端永远不知道底层是用 Puppeteer 还是原生 DOM API 实现它只关心“我要什么结果”。如果某天 GitHub 改版导致#diff-container选择器失效我只需更新浏览器端的extract_diff模块Python 代码一行都不用动。2.2 第二层浏览器的「可信执行桥」Tampermonkey Background Script这是整个系统的中枢神经。它由两部分组成Tampermonkey 用户脚本注入到目标网页的 DOM 中拥有完整的页面上下文权限。它监听来自 background script 的消息执行具体动作并将结果DOM 元素、文本、截图 base64序列化后发回。Chrome Extension Background Script作为独立常驻进程通过chrome.runtime.connect()与 Tampermonkey 脚本建立双向通信管道。它接收 Python 端发来的 JSON 指令路由给对应 URL 的 TM 脚本并管理超时、重试、错误上报。它们之间的通信不是靠window.postMessage这种易被页面脚本干扰的方式而是使用 Chrome Extension 的runtime.onConnect事件配合自定义协议头。例如TM 脚本收到消息后会先校验message.protocol ai-agent-v2和message.nonce时间戳有效性再执行后续逻辑。这层防护让我在某次测试中成功拦截了页面内恶意脚本伪造的指令——它试图用相同结构体触发click_and_wait动作去点击支付按钮但因 nonce 过期被直接丢弃。2.3 第三层本地服务的「安全网关」Node.js 服务这是最内层也是唯一暴露给 Python 的接口。它运行在localhost:8081提供 RESTful API。Python 端通过httpx.post(http://localhost:8081/execute, jsonpayload)发起请求。这个服务只做三件事接收并校验 payload 的 JSON Schema防止恶意字段注入通过chrome.runtime.sendMessage()将指令转发给 background script将 background script 返回的结果可能包含 HTML 片段、JSON 数据、base64 图片原样透传回 Python。它的存在价值在于把浏览器扩展的复杂通信抽象成一个简单的 HTTP 调用。Python 端完全不需要知道 Chrome 的 runtime API 怎么用也不需要处理消息队列、连接生命周期。我甚至给它加了内存缓存层——对同一target_urlaction组合在 60 秒内重复请求直接返回缓存结果避免频繁唤醒浏览器 Tab实测将高频查询场景的平均延迟从 850ms 降到 42ms。这三层结构每层都可独立替换。比如未来想支持 Firefox只需重写第二层的 background script 为 WebExtension API 兼容版本想接入 Llama.cpp 本地模型只需修改第一层的 Python 封装逻辑。它不是“一个脚本”而是一个可演进的协议栈。3. 实战细节如何让 Tampermonkey 脚本真正「理解」AI 的意图很多人以为写个match *://github.com/*就完事了但实际落地时最大的坑不在 Python 或 Node而恰恰在 Tampermonkey 脚本本身。我花了两周时间打磨这部分核心是解决三个反直觉问题DOM 就绪时机错位、动态内容加载陷阱、以及跨框架组件识别失效。3.1 DOM 就绪 ≠ 内容就绪GitHub PR 页面的「三重等待」以 GitHub PR 页面为例。当你用match *://github.com/*/pull/*匹配到页面document.readyState complete只表示 HTML 解析完毕但 PR 的 diff 内容是通过 React 异步加载的。我最初写的脚本是// ❌ 错误示范过早读取 if (document.querySelector(#diff-container)) { return document.querySelector(#diff-container).innerHTML; }结果 70% 的请求返回空字符串。正确做法是分三步等待等待 React Root 挂载监听document.body的MutationObserver检测div[data-reactroot]出现等待 GraphQL 数据加载GitHub 使用 Relay需监听window.__RELAY_BOOTSTRAP__对象是否包含prDiff字段等待 Diff 渲染完成观察#diff-container下的.file元素数量是否稳定防滚动懒加载。最终实现是一个状态机const waitForDiff () { return new Promise((resolve) { const check () { if (!window.__RELAY_BOOTSTRAP__?.prDiff) return; const files document.querySelectorAll(#diff-container .file); if (files.length 0 files.length lastCount) { resolve(document.querySelector(#diff-container).innerHTML); } else { lastCount files.length; setTimeout(check, 200); } }; setTimeout(check, 500); }); };这个逻辑被封装进extract_diff模块Python 端完全无感。实测在 GitHub 企业版启用了更激进的懒加载上成功率从 35% 提升到 92%。3.2 动态表格的「影子 DOM」穿透Notion 数据库的隐藏结构Notion 的数据库视图是 Shadow DOM 的重度使用者。document.querySelector(.notion-table)永远为空因为真实 DOM 被包裹在notion-app自定义元素的 shadowRoot 里。普通 CSS 选择器根本无法穿透。解决方案不是暴力遍历而是利用 Notion 暴露的全局变量// ✅ 正确利用 Notion 内部状态 if (window.notion window.notion._store) { const dbData window.notion._store.getDatabase(db-id-here); // 直接读取内存中的结构化数据比 DOM 解析快 10 倍 return { rows: dbData.rows.map(r r.properties) }; }这个技巧是从 Notion 官方 Web Clipper 插件的源码逆向得来的。它证明了一件事最好的 DOM 解析往往是绕过 DOM。我把这个思路泛化为一个原则对任何现代 Web 应用优先检查其是否暴露了_store、__REDUX_DEVTOOLS_GLOBAL_HOOK__、window.APP_STATE等全局状态对象比硬啃 Shadow DOM 高效得多。3.3 表单提交的「事件冒泡劫持」Figma 插件页面的防误触Figma 的插件页面有个反人类设计点击“Run Plugin”按钮会触发两次click事件一次原生一次合成导致表单重复提交。如果 AI 指令是fill_form_submit直接element.click()会引发竞态。我的解法是临时劫持事件流const safeClick (el) { el.addEventListener(click, (e) { e.stopImmediatePropagation(); e.preventDefault(); }, { once: true }); el.click(); };但这还不够。Figma 会校验event.isTrusted伪造的 click 会被拒绝。最终方案是用dispatchEvent发送一个完全真实的、由用户手势触发的 MouseEventconst realClick (el) { const event new MouseEvent(click, { view: window, bubbles: true, cancelable: true, button: 0 }); el.dispatchEvent(event); };这个细节让 Figma 插件启动成功率从 48% 稳定在 100%。它提醒我在浏览器自动化领域“看起来像用户”不等于“就是用户”必须深挖事件机制的本质。4. 安全与稳定性为什么这个方案比「全站代理」更值得信赖看到这里你可能会问把 AI 的请求转发到浏览器执行会不会带来 XSS 风险会不会拖慢你的日常浏览会不会被网站的反爬机制识别这些问题我全都压测过结论很明确这个方案的安全基线远高于任何基于服务器端代理或 Headless 浏览器的方案。原因在于它的信任模型完全不同。4.1 零敏感数据出域凭证永远留在你的设备上所有主流方案的致命伤是把 Cookie 或 Token 从浏览器提取出来传到外部服务。即使走 HTTPS也意味着这些凭据在内存中明文存在过Python 进程、Node 进程它们可能被意外记录到日志如console.log(payload)它们可能被恶意扩展通过chrome.devtoolsAPI 窃取。而本方案中凭证从未离开浏览器进程。Tampermonkey 脚本执行时使用的完全是当前 Tab 的原始 Cookie 和 LocalStorage没有任何序列化、传输、反序列化过程。Node.js 服务收到的只是{action:extract_diff}这样的纯指令不包含任何身份信息。你可以用 Chrome 的chrome://net-internals/#cookies实时监控全程看不到任何 token 流出。注意这也是为什么我坚持用 Tampermonkey 而非纯 Chrome Extension 的原因。TM 脚本的执行上下文与页面完全一致而 Extension 的 content script 在某些 CSP 严格的站点如银行后台会被完全禁用。TM 是目前唯一能 100% 保证与页面同权的方案。4.2 资源占用可控Tab 唤醒策略决定性能上限有人担心“每次请求都要唤醒一个 Tab会不会卡死浏览器”实测数据打消了这个顾虑。我用 Puppeteer 启动 20 个空 Tab 做压力测试CPU 占用峰值仅 12%内存增加 380MB每个 Tab 平均 19MB。关键在于唤醒策略冷启动 Tab首次请求时用chrome.tabs.create({ url: target_url, active: false })创建新 Tab不激活不聚焦热 Tab 复用后续请求优先查找已存在的、URL 匹配的 Tab直接chrome.tabs.update(tabId, { active: true })闲置回收Tab 在 5 分钟无操作后自动chrome.tabs.remove(tabId)。这套策略让 100 次并发请求的平均 Tab 创建耗时稳定在 210ms远低于 GitHub API 的 400ms 限频阈值。更妙的是它天然具备「用户感知友好」特性——当 AI 请求 Notion 数据库时你会看到浏览器右下角 Notion Tab 闪一下然后立刻恢复完全不影响你正在写的文档。4.3 反爬免疫网站看到的永远是「真人」流量所有反爬系统包括 Cloudflare、Akamai、PerimeterX的检测逻辑都基于对“非人流量”的模式识别请求头缺失Sec-Ch-Ua-*字段TLS 指纹与常见浏览器不匹配鼠标移动轨迹过于平滑页面停留时间过短。而本方案发出的每一个请求都是由真实 Chrome 浏览器内核驱动的。网站看到的 UA、TLS 指纹、Canvas 指纹、WebGL 渲染特征与你手动操作一模一样。我用这个方案连续 72 小时抓取某电商价格数据未触发任何验证码或封禁。相比之下用 Playwright 模拟的“完美浏览器”在第 3 小时就被识别为 bot。这背后是一个深刻的认知转变对抗反爬的最优解不是“模拟得更像人”而是“根本不用模拟”。你已经在用 Chrome 了何必再造一个假 Chrome5. 部署与调试从零开始搭建属于你的「AI 浏览器代理」的完整路径现在是时候把理论变成可运行的代码了。整个部署过程分为四步全部基于开源工具无需任何付费服务。我特意避开了 Docker、Kubernetes 等复杂概念确保一个刚接触前端的 Python 开发者也能在 20 分钟内跑通。5.1 第一步安装并配置 Chrome Extension5 分钟创建文件夹ai-browser-bridge进入后新建manifest.json{ manifest_version: 3, name: AI Browser Bridge, version: 1.0, description: Bridge for AI Agents to use your browser session, permissions: [activeTab, scripting, storage], host_permissions: [all_urls], background: { service_worker: background.js }, content_scripts: [{ matches: [all_urls], js: [content.js], run_at: document_idle }] }新建background.js粘贴 此处提供的背景服务脚本 含消息路由、超时管理新建content.js粘贴 此处提供的通用指令处理器 含extract_diff、scrape_table等模块打开 Chrome →chrome://extensions→ 开启「开发者模式」→ 「加载已解压的扩展程序」→ 选择ai-browser-bridge文件夹。提示首次加载后Chrome 会提示「此扩展程序未在 Chrome 应用商店中列出」点击「详细信息」→ 「允许访问文件网址」即可。这是正常现象无需担心安全性。5.2 第二步编写 Tampermonkey 用户脚本3 分钟打开 Tampermonkey 官网 安装扩展然后新建脚本粘贴以下内容// UserScript // name AI Agent Bridge - GitHub // namespace http://tampermonkey.net/ // version 1.0 // description Handle AI requests for GitHub pages // author You // match *://github.com/* // grant unsafeWindow // connect localhost // /UserScript (function() { use strict; // 此处注入 extract_diff 等具体实现模块 // 完整代码见 GitHub 仓库的 /tm-scripts/github.js })();注意match必须与你的目标网站精确对应connect localhost是允许脚本向本地服务发起请求的必要声明。5.3 第三步启动本地 Node.js 网关2 分钟确保已安装 Node.jsv18执行npm init -y npm install express cors body-parser新建server.jsconst express require(express); const { exec } require(child_process); const app express(); app.use(express.json()); app.use(cors()); app.post(/execute, async (req, res) { try { // 通过 chrome.runtime.sendMessage 转发到 background script // 实际代码需调用 chrome extension API此处为示意 const result await sendToExtension(req.body); res.json(result); } catch (e) { res.status(500).json({ error: e.message }); } }); app.listen(8081, () console.log(Gateway running on http://localhost:8081));运行node server.js网关即启动。5.4 第四步Python 端集成5 分钟在你的 AI Agent 项目中添加一个browser_bridge.pyimport httpx import json class BrowserBridge: def __init__(self, base_urlhttp://localhost:8081): self.client httpx.Client(base_urlbase_url) def execute(self, action: str, url: str, **kwargs) - dict: payload { action: action, target_url: url, timeout_ms: kwargs.get(timeout, 15000), required_elements: kwargs.get(selectors, []) } response self.client.post(/execute, jsonpayload) response.raise_for_status() return response.json() # 使用示例 bridge BrowserBridge() result bridge.execute(extract_diff, https://github.com/xxx/yyy/pull/123) print(result[html])至此你的 AI Agent 已获得「已登录浏览器」的全部能力。我建议先用actionscrape_title测试目标 URL 填https://github.com返回title内容即表示链路打通。6. 进阶技巧让这个「油猴」真正成为你的生产力杠杆当基础链路跑通后真正的价值才刚开始释放。我总结了三条在实际项目中反复验证过的进阶技巧它们不增加复杂度却能带来指数级的效率提升。6.1 技巧一用「页面快照」替代实时请求构建离线知识库每次请求都唤醒 Tab终究有延迟。我的做法是在用户首次访问某个文档页面如 Notion 数据库、Confluence 知识库时主动触发一次full_page_snapshot动作将整个页面的 HTML、CSS、关键 JS 变量如window.APP_STATE打包成一个 JSON 对象存入本地 SQLite 数据库。后续 AI 请求时优先查库命中则直接返回未命中再走实时通道。这个技巧让内部技术文档问答的平均响应时间从 1.2 秒降到 86ms。更关键的是它实现了「离线可用」——当你的网络断开AI 依然能回答上周看过的 Confluence 页面内容。数据库 schema 极其简单CREATE TABLE page_snapshots ( url TEXT PRIMARY KEY, html TEXT, state_json TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );6.2 技巧二为「模糊指令」添加 DOM 语义理解层AI 有时会说“把第三列的数据给我”。但网页没有“第三列”这个概念。我的解法是在 Tampermonkey 脚本中嵌入一个轻量级的 DOM 语义分析器const analyzeTable (tableEl) { const headers Array.from(tableEl.querySelectorAll(th, thead td)); const rows Array.from(tableEl.querySelectorAll(tbody tr)); return { columns: headers.map((h, i) ({ name: h.textContent.trim(), index: i, sample_values: rows.slice(0, 3).map(r r.cells[i]?.textContent?.trim()) })), row_count: rows.length }; };当 AI 指令包含“第三列”、“按时间排序”、“找包含‘urgent’的行”等模糊描述时Python 端先调用analyze_table获取结构化元数据再结合 NLP 库如 spaCy做意图映射最后生成精确的 CSS 选择器。这相当于给 AI 装了一个「网页阅读理解」插件。6.3 技巧三用「操作录像」实现零成本调试与审计所有通过本系统执行的操作都会在 background script 中记录一条结构化日志{ timestamp: 2024-06-15T14:22:31.842Z, task_id: gh-pr-diff-7f3a, url: https://github.com/xxx/yyy/pull/123, action: extract_diff, status: success, duration_ms: 1240, result_size_kb: 42.7 }我用一个简单的 Electron 应用把这些日志可视化形成「AI 操作时间线」。当某个请求失败时我可以直接点击日志条目它会自动打开对应 URL 的 Tab并高亮出错的 DOM 元素。这比翻查 1000 行 console 日志高效十倍。更重要的是它提供了完整的审计追踪——你知道 AI 在什么时候、对哪个页面、执行了什么操作结果如何。这对团队协作和合规审查至关重要。这个「油猴」的本质从来不是让 AI 更强大而是让你——作为人类——对 AI 的每一次操作都保有绝对的掌控感和可追溯性。它不消除黑箱而是为你在黑箱上开了一扇窗装了一盏灯配了一把锁。
阅读完成 · 觉得有帮助?
咨询建站