让 AI 钻进你的浏览器:Kimi WebBridge 原理与源码深度拆解
当 AI 不再"假装"操作网页,而是钻进你正在用的浏览器里,直接指挥浏览器引擎干活,会发生什么?
最近在研究 Kimi WebBridge——一个让 AI 控制你真实浏览器的桥接工具。它既不是传统的 RPA 模拟点击,也不是爬虫式的接口调用,而是走了第三条路:钻进浏览器内部,调用浏览器自己的开发者协议(CDP),替你操作你真实打开的浏览器。
这篇文章把它的原理、源码设计、实际用法和改造边界一次讲透。
一、它到底是什么:一个"遥控器 + 传话快递员"的组合
先看全貌。WebBridge 的整体链路:
AI 助手(Kimi Code)│ curl POST http://127.0.0.1:10086/command ← HTTP 命令层▼本地 Daemon(127.0.0.1:10086)│ WebSocket ws://127.0.0.1:10086/ws ← 消息转发层▼浏览器扩展(Chrome/Edge 插件,MV3)│ chrome.debugger → Chrome DevTools Protocol (CDP)▼你真实打开的浏览器(带完整登录态)
把它想象成一个"人肉遥控器 + 传话快递员"的组合:
AI 是发号施令的人:"打开淘宝,搜一下保温杯" 本地 Daemon 是传话的快递员:在 10086 端口收指令,再喊话给浏览器,顺带把 AI 打开的页面收进一个标签分组,让你肉眼可见它在干什么 浏览器扩展 是长在浏览器身上的遥控器 CDP 是浏览器内部那条"万能遥控接口"——就是按 F12 打开开发者工具时用到的那套底层协议。你在 DevTools 里能干的事(查元素、执行 JS、模拟点击、截图),它都能用代码干
所以最直白的说法:WebBridge 就是"有人用代码替你操作 F12 开发者工具"。
二、它是模拟人操作,还是调接口?——都不是,是第三种
这是最值得掰扯清楚的问题。三条技术路线对比:
| WebBridge | 浏览器引擎内部 |
具体来说:
它不移动你屏幕上的鼠标光标。 click是浏览器引擎内部执行el.click()——相当于你在控制台敲了一行 JS。mouse_click更接近"真人":它派发带isTrusted=true的真实鼠标事件(压下去、抬起来),但依然发生在浏览器进程内部。它也不碰网站的后端接口。 network工具只是"旁听",抓的是浏览器自己发出的请求,绝不伪造。它调用的唯一接口,是浏览器自己为开发者提供的接口(CDP)。
为什么这条路值钱? 因为它操作的是你真实打开的浏览器,白捡了三样硬通货:
真实登录态——淘宝、知乎、付费后台,打开就是已登录,不用塞 cookie 真实 JS 环境——SPA 页面、动态渲染内容全部读得到,因为就是浏览器自己渲染完的状态 网站很难分辨——经过 mouse_click、send_keys发出的事件带isTrusted=true,网站代码检查"这事件是不是人干的"时,查的就是这个标志
一句话总结:模拟人是替你的手干活;调接口是绕过浏览器直接问网站要数据;WebBridge 是钻进浏览器内部,替"浏览器"干活——所以你看到的结果,和你自己用电脑时一模一样。
三、源码设计拆解:一个"薄状态、厚工具、协议统一"的插件
接下来进入源码层。整个扩展的核心是一个压缩过的 background.js(仅 28KB),但它包含了全部 17 个工具和整套通信系统。拆开看,结构非常清晰。
3.1 分层架构
L4 UI 层 popup(React)→ 状态监测 / 版本协调 / 信任管理L3 传输层 WebSocket 客户端 → 连接状态机 + 消息协议L2 调度层 工具注册表 + 分发器 + 会话状态注入L1 协议层 17 个工具类,全部通过 chrome.debugger 发 CDP 命令L0 基础层 attach/当前标签管理 + Tab 分组 + 事件清理
设计原则一句话:"薄状态、厚工具、协议统一"。扩展几乎不保存业务状态,只保留"当前操作哪张标签"这个指针;所有工具呈现统一接口;一切浏览器操作最终归一为 CDP 命令。
3.2 17 个工具一览
navigate | Page.navigate,同 URL 则强制刷新 | |
find_tab | active:true 借用用户正看的页 | *://hostname/* |
snapshot | Accessibility.getFullAXTree | @eN 引用,不怕前端改版 |
click | el.click() | isTrusted=false,严格站点拒收 |
mouse_click | isTrusted=true | |
fill | execCommand | |
evaluate | Runtime.evaluate | |
cdp | ||
network | getResponseBody 取响应体 | |
screenshotsave_as_pdf | captureScreenshotprintToPDF | |
upload | setFileInputFiles | |
key_typesend_keys | Input.insertTextdispatchKeyEvent | Mod 按平台解析 |
list_tabsclose_tab / close_session | chrome.tabs |
3.3 四个值得记住的设计决策
① 状态外置:会话清单由 Daemon 下发,扩展保持无状态
每次工具调用,daemon 都会把会话的标签 ID 列表(_tabId/_tabIds)塞进请求参数里一并下发,扩展执行前先按它重新 attach 并设当前标签,用完即丢。会话的"记忆"在 daemon 侧,扩展只留一个指针——所以多会话并发互相不污染,这是全系统能稳定并发的基础。
② 无障碍树优先于 CSS 选择器
snapshot 走无障碍树(Accessibility Tree),只给 button、link、textbox 这类交互角色分配 @e12 这种引用,背后映射到 DOM 节点 ID。前端发版改 class 哈希,引用依然有效——这是对抗"样式天天变"的定位方案。
③ 实力分级,而非单一方案
点击有两档: click(快)和mouse_click(真);填表有双通道:input 走原生 setter,富文本走 execCommand;每种能力背后还垫着 evaluate和cdp两个万能兜底。
不把所有压力押在单一技术上,这是自动化稳定性的核心思想。
④ 传输层是一台自愈状态机
WebSocket 客户端维护 disconnected → connecting → connected 状态,每 30 秒用定时器自检重连,10 秒连接超时,连上先发 hello 报版本号——daemon 据此判断扩展是否太旧,太旧就让工具报"请更新扩展"。所有异常都被包装成 {error} 返回,绝不裸抛导致协议错乱。
3.4 Popup:状态监测、版本协调与安全信任模型
弹窗 UI 是个 340px 宽的 React 小页面,干三件事:
每 3 秒轮询:扩展连没连上 daemon、daemon 版本和多 agent 的 skill 版本; 版本协调:内置迷你 semver 比较器,发现 skill 比扩展新就提示"发现新版本",一键复制升级命令; 安全信任模型:连点图标 5 次进入开发者模式,可以改 daemon 的 WebSocket 地址;对非本机 + 明文 ws:// 的地址强制弹出"该服务将获得控制你浏览器的权限"双重警告,确认后才加入白名单。
这个信任模型非常值得学习:它把"把浏览器交给谁"这个高风险决策,用可见的警告摊到了用户面前。
四、实战问答:它和自己的使用会冲突吗?能抓接口吗?
4.1 我自己在用浏览器,它操作另一个页面,能同时用吗?
能,而且这正是默认工作方式。 源码里 navigate 创建标签用的是 active:false——默认在后台打开,不抢焦点。你在前台正常用,它在后台静默操作,因为 CDP 命令不要求页面在前台。
只有两种"碰你"的情况,且都是你主动要求的:
你说"用我开着的这个页面" → 它借用你当前正在看的标签(返回 borrowed:true),你会看到页面自己动;它想导航回自己的后台标签 → 只是换个网址,不影响你。
小技巧:想让它的标签彻底隔离在另一个窗口,就按 Cmd+N 新建一个窗口并保持聚焦——chrome.tabs.create 不带 windowId 时,新标签默认开在"最后聚焦的窗口",它的标签自然落进新窗口。注意会话中别把焦点切回主窗口,否则新标签会跟回来。
4.2 能抓到页面调用的后端接口吗?
能,这是 network 工具的主业。 流程:network start 开始旁听 → 在页面操作触发请求 → network list 看请求清单(URL、方法、状态码、响应体)→ network detail 拿响应 JSON。
但有一个源码层面的边界要坦白:requestWillBeSent 事件里只记录了 url/method/timestamp,POST 的请求体和请求头没进记录。补全有两种办法:
正统补法:用 cdp工具裸调Network.getRequestPostData,传入list拿到的 requestId,取回 POST body;最全补法:操作页面前用 evaluate注入一段钩子,重写window.fetch和XMLHttpRequest,把 method/url/headers/body 全部记下来,之后一次性读出。
因为流量是浏览器真实发出的,带着你的登录态和签名——接口画像几乎是透明的,比 Postman 手搓容易太多。
4.3 拿到接口后,能自己写代码调用吗?
可以,但看接口"认不认暗号"。按省力程度排序:
不离开浏览器:用 evaluate在页面上下文里fetch——cookie、CSRF token、签名 header 全自动带上,不撞 CORS,最推荐;复制到自己代码:适合只有普通 cookie 鉴权的公开接口; 逆向签名算法:如果接口带 sign/ts/nonce这类动态签名,得先在页面里找到签名函数,或彻底复刻算法——工作量取决于混淆程度。
常见拦路虎:Cookie 缺失(401)、动态签名(重放过期)、频控风控(429/验证码)、设备指纹校验(403)。最后提醒一句合规:绕过风控批量抓取可能违反网站条款,自己账号内低频取数风险最低。
五、关于源码的一个诚实结论:产物能还原到什么程度
这个扩展的目录是构建产物而非源码——background.js 是压缩过的单行代码,变量全是 t/n/r,popup 是带 hash 的编译 chunk,目录里没有 package.json、src/、构建配置。
但压缩 ≠ 混淆。没有字符串加密,也没有控制流打乱,所以:
✅ 能 100% 还原:功能逻辑、算法、CDP 命令、消息协议、字符串文案 ❌ 永久丢失:作者的变量命名、注释、模块拆分结构、TS 类型、JSX 组件结构
目录里没有 sourcemap(我已查证),所以不存在"一键还原原始代码"的捷径。想要开发级源码,两条路:
人工反向重构:把还原出的逻辑按语义重命名、拆模块、重建构建配置,产出一套功能等价、可维护、能重新构建的源码工程; 找官方渠道:WebBridge 有官方安装器和官网文档,值得先查是否开源。
轻量增强(比如给 network 补上 POST body 记录,就几行)可以直接 patch 产物,改完以"开发者模式加载已解压的扩展"生效;重型增强(跨域 iframe、授权弹窗)则需要源码级改造。
六、已知局限(源码层面看得出的)
isTrusted严格校验的站点(银行、验证码)会忽略click/fill——mouse_click是协议级真实事件,但默认不启用;跨域 iframe 内的元素操作不了—— fill/click/evaluate都只在顶层 frame,需直接导航到 iframe 的 URL;network不记录 POST body 和 headers——需要用 CDP 或 evaluate 补;安全边界:只要 daemon 可达,任何能连上它的人都能指挥你的浏览器,它的安全底线是"daemon 只在 127.0.0.1 + 本地进程持有"。
结语
WebBridge 的设计给我最大的启发,是它选对了"操作的位置":不在操作系统层模拟人,也不在网络层伪造请求,而是站在浏览器引擎内部,用自己的开发者协议驱动真实会话。 这让自动化获得了登录态、JS 环境和事件可信度三重真实,同时保持了"扩展无状态、协议统一、能力分层兜底"的工程整洁度。
对普通用户,它是"AI 替你上网"的入口;对开发者,它是一套值得拆解的浏览器自动化架构范本。希望这篇文章能帮你理解它,甚至动手改造它。
本文基于 Kimi WebBridge 扩展源码(v1.11.6)与官方 skill 文档的实际代码分析撰写,所有结论均可对应到具体实现。
夜雨聆风