OpenClaw UI自动化核心技术解析:CDP与ReAct驱动的Chrome DevTools Protocol实践

发布时间:2026/10/12 21:23:34
OpenClaw UI自动化核心技术解析:CDP与ReAct驱动的Chrome DevTools Protocol实践
1. 为什么浏览器自动化总在“点不准”上翻车做 UI 自动化的人大多经历过这个场景脚本在本地跑得好好的换台机器或者页面稍微改版click就点空了。传统方案要么靠图像识别找按钮要么靠固定 XPath 硬编码前者怕分辨率变化后者怕 DOM 结构微调。OpenClaw 这类工具换了个思路——它不“看”像素而是直接通过 Chrome DevTools ProtocolCDP跟浏览器内核对话拿到真实的 DOM 节点再操作。CDP 是什么你可以把它理解成 Chrome 留给外部程序的“遥控器接口”。浏览器里每一个标签页、每一次网络请求、每一个 DOM 变更CDP 都能以结构化事件的形式吐出来。OpenClaw 在 CDP 之上又叠了一层 ReAct 循环模型先推理“现在页面处于什么状态、目标元素在哪”再决定“执行哪个 CDP 命令”拿到执行结果后继续推理下一步。这套“推理-行动-观察”的闭环让自动化脚本从“死记步骤”变成“看着页面走”。这篇文章面向的是需要做浏览器操作自动化的开发者尤其是那些被元素定位和动态加载折磨过的人。我会给出可复制的 CDP 连接配置、ReAct 提示模板以及一次完整的页面元素定位与点击验证流程。过程中用到的模型调用通道我会用 TaoToken 统一接入省去多平台 Key 来回切换的麻烦。你跟着走一遍应该能跑通一个最小可用的自动化闭环。2. TaoToken 前置把模型通道先理顺OpenClaw 的 ReAct 循环里每一步“推理”都要调用大模型。如果你直接用某家官方 APIKey 管理、额度、模型切换都是琐事。TaoToken 在这里的角色是统一入口一个 Key 走通多家模型Base URL 和鉴权格式保持一致OpenClaw 的配置里只需要填一次。先拿 Key。访问 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite登录后创建一个新 Key复制出来。注意这个 Key 只在创建时完整显示一次先存到本地环境变量里别直接写进代码提交到仓库。export TAOTOKEN_API_KEYsk-你的实际Key接着确认你要用的模型 ID。OpenClaw 的 ReAct 推理对模型指令遵循能力有要求建议选 Claude 系列或同级别模型。你可以在模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite先手动试一句“帮我分析这个按钮的 CSS 选择器”确认通道通、模型响应正常再写进配置。Base URL 统一用https://taotoken.net/api不要加 UTM 后缀那是给网页跳转用的API 请求带上反而可能出问题。鉴权头是标准的Authorization: Bearer Key跟 OpenAI 格式兼容所以 OpenClaw 里凡是填base_url和api_key的地方直接对应填就行。如果你打算长期跑编码类或 Agent 类任务可以顺带看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它针对高频调用场景做了额度优化比按次计费更适合自动化这种“一步一推理”的消耗模式。这一步不强制先把基础通道跑通再说。3. 可复制配置CDP 连接与 ReAct 模板这一节是核心配置分两块一块让 OpenClaw 连上 Chrome 的 CDP 端口一块让 ReAct 循环知道怎么调模型。3.1 启动带 CDP 的 Chrome 实例Chrome 默认不开远程调试端口需要手动启动。macOS 下命令如下Windows 把路径换成chrome.exe的实际位置即可。/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \ --remote-debugging-port9222 \ --user-data-dir/tmp/openclaw-chrome-profile \ --no-first-run \ --no-default-browser-check--remote-debugging-port9222是 CDP 的入口--user-data-dir指定一个独立配置目录避免污染你日常用的浏览器数据。启动后访问http://127.0.0.1:9222/json/version能看到类似下面的 JSON 就说明 CDP 通了{ Browser: Chrome/120.0.6099.109, Protocol-Version: 1.3, webSocketDebuggerUrl: ws://127.0.0.1:9222/devtools/browser/xxxx }这个webSocketDebuggerUrl就是 OpenClaw 要连的地址。注意它是ws://开头的 WebSocket不是 HTTP。3.2 OpenClaw 的 CDP 连接配置OpenClaw 的配置文件通常放在项目根目录的claw.config.json。下面这份配置同时包含了 CDP 连接和模型通道两部分路径和字段名按你实际安装版本对齐核心是cdpEndpoint和llm两个块。{ browser: { cdpEndpoint: ws://127.0.0.1:9222/devtools/browser/xxxx, defaultTimeout: 30000, headless: false }, llm: { provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: ${TAOTOKEN_API_KEY}, model: claude-sonnet-4-20250514, maxTokens: 2048, temperature: 0.2 }, react: { maxSteps: 15, observationTruncate: 4000 } }几个关键点apiKey用${TAOTOKEN_API_KEY}引用环境变量别写明文temperature设低一点ReAct 推理要的是稳定而不是创意maxSteps限制循环次数防止模型卡在某个状态里无限转圈observationTruncate控制每次把页面状态喂回模型时的截断长度太长会烧 token太短模型看不清。3.3 ReAct 提示模板OpenClaw 内部会拼装提示词但你可以通过prompts/react.md覆盖默认模板。下面这份模板经过实测对元素定位类任务比较友好。你是一个浏览器自动化助手通过 CDP 操作 Chrome。每一轮你只能输出一个 JSON 对象格式如下 { thought: 当前页面状态分析目标元素可能在哪, action: cdp_command, params: { method: DOM.querySelector, nodeId: 1, selector: #login-btn } } 可用 action 列表 - navigate: 参数 url - querySelector: 参数 selector返回 nodeId - click: 参数 nodeId - type: 参数 nodeId, text - waitForSelector: 参数 selector, timeout - extractText: 参数 nodeId 规则 1. 每次只做一个动作等 observation 返回后再决定下一步。 2. 如果 observation 里出现 error先分析原因再重试不要重复同样的失败动作。 3. 目标达成后输出 {thought: 完成, action: done, params: {}}。这份模板的作用是把模型的输出约束成结构化 JSONOpenClaw 解析后转成 CDP 命令。thought字段是 ReAct 的“推理”部分action是“行动”observation 由 OpenClaw 执行后自动回填。三者循环直到done。4. 验证请求跑通一次元素定位与点击配置就绪后写一个最小任务验证整条链路。目标打开一个测试页面定位登录按钮点击确认跳转。4.1 任务脚本import { Claw } from openclaw/cli; const claw new Claw({ configPath: ./claw.config.json }); async function run() { const result await claw.run({ goal: 打开 https://example.com/login找到 id 为 login-btn 的按钮并点击然后报告页面标题, maxSteps: 10 }); console.log(最终结果:, result.finalObservation); console.log(执行步骤数:, result.steps.length); console.log(每步动作:, result.steps.map(s s.action)); } run().catch(console.error);goal用自然语言描述OpenClaw 会把它和 ReAct 模板拼在一起发给模型。maxSteps在这里再设一次作为单次任务的硬上限。4.2 预期输出与解读正常跑通的话控制台会打印类似下面的内容最终结果: 页面标题为 Dashboard - Example 执行步骤数: 5 每步动作: [ navigate, waitForSelector, querySelector, click, extractText ]五步对应导航到登录页、等待按钮出现、查询按钮节点、点击、提取跳转后标题。如果某一步 observation 返回error: node not found说明选择器没匹配上模型会在下一轮 thought 里调整策略比如换成[data-testidlogin-btn]再试。4.3 用模型对话先验证通道在跑完整脚本前建议先用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite发一条测试消息确认 Key 和模型 ID 都对。比如输入“返回一个 JSON包含字段 action 和 selector”看返回格式是否稳定。这一步能提前排除鉴权问题省得在 OpenClaw 里排查半天发现是 Key 错了。5. 本篇常见错排查自动化跑不起来八成是下面几个错。我按报错原文对照着说。401 Unauthorized模型通道鉴权失败。检查claw.config.json里apiKey引用的环境变量是否真的导出了echo $TAOTOKEN_API_KEY看有没有值。另外确认baseUrl是https://taotoken.net/api末尾不要多斜杠也不要把网页地址填进去。local proxy failed / ECONNREFUSED 127.0.0.1:9222CDP 端口没通。先确认 Chrome 是用--remote-debugging-port9222启动的再访问http://127.0.0.1:9222/json/version验证。如果返回空可能是 Chrome 已经在运行占用了默认 profile需要先完全退出再带参数启动。reading choices of undefined模型返回体结构不符合预期。通常是model字段填错了或者通道返回了错误对象但代码没处理。在模型对话页面用同一个模型 ID 发一条消息看返回的 JSON 里有没有choices数组。没有的话换模型 ID。OAuth token expired / invalid_grant如果你用的是带 OAuth 的客户端比如某些 Claude Code 配置token 过期了。重新走一遍授权流程或者改用 API Key 方式接入。OpenClaw 的llm块用openai-compatible模式时不需要 OAuth直接 Key 就行。元素定位到了但 click 无效CDP 的click是模拟鼠标事件如果目标元素被遮挡或者有pointer-events: none点击不会生效。让模型在 thought 里先检查元素的可点击性或者改用Runtime.evaluate直接触发element.click()。排查顺序建议先确认模型通道401 类再确认 CDP 连接ECONNREFUSED 类最后才是元素定位逻辑。前两层通了第三层就是调选择器和等待策略的事。6. 把通道固定下来让自动化跑得久一点跑通一次不难难的是让它稳定跑一百次。我的经验是把模型通道和浏览器连接都做成“可复用资源”而不是每次任务重新初始化。TaoToken 的 Key 放在环境变量里OpenClaw 配置引用它这样换机器只需要重新导出变量配置文件不用动。Chrome 的 CDP 实例也可以常驻OpenClaw 每次任务连同一个webSocketDebuggerUrl省去反复启动浏览器的开销。如果你要跑的是长时间、多步骤的 Agent 任务比如批量表单填写或者跨页面数据采集建议看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它的额度模型更适合这种持续推理的场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言 SDK 的调用示例需要换语言实现时可以直接参考。最后留一个实用技巧在 ReAct 模板里加一条规则让模型每次 click 之前先waitForSelector并且把 timeout 设成 5000 而不是默认的 30000。这样元素没出现时快速失败、快速重试比干等半分钟再报错要高效得多。自动化这东西失败得快比成功得慢更有价值。