戰(zhàn):control-browser Skill 的六步瀏覽器操作工作流(Snapshot→Locator→Act))
人工智能大模型代碼智能體AI Agent桌面應(yīng)用后端前端CLI【免費(fèi)下載鏈接】ZCodeZCode 是 AI 編程工作臺(tái)提供桌面應(yīng)用、瀏覽器界面和終端 Agent。本倉(cāng)庫(kù)包含客戶(hù)端、后端服務(wù)、共享 UI以及 Agent CLI 與運(yùn)行時(shí)源碼。項(xiàng)目地址https://gitcode.com/zai-org/ZCode點(diǎn)擊查看免費(fèi)下載本指南以 apps/zcode-cli/packages/browser-use-plugin/docs/workflow.md 為主體結(jié)合官方內(nèi)置插件 browser-use-plugin 的 Skill 引導(dǎo)control-browser SKILL.md與 overview.md 中的 API 行為約定展開(kāi)。讀完本文你將掌握為什么每次 JS 調(diào)用都要重新引導(dǎo)bootstrap運(yùn)行時(shí)、如何用「先列全量標(biāo)簽頁(yè)、再按已驗(yàn)證事實(shí)綁定」的協(xié)議選擇目標(biāo)、如何以domSnapshot()的 AI/ARIA 樹(shù)為唯一定位事實(shí)來(lái)源構(gòu)建 Playwright 定位器以及動(dòng)作之后如何用「組合觀察」判斷真實(shí)效果最終寫(xiě)出可穩(wěn)定復(fù)現(xiàn)的瀏覽器自動(dòng)化軌跡。ZCode 的瀏覽器自動(dòng)化能力由官方內(nèi)置插件zcode/browser-use-plugin提供。它并不把一個(gè)瀏覽器會(huì)話(huà)暴露給模型而是把每次js調(diào)用都放進(jìn)一個(gè)全新的 JavaScript kernel由zcode/node-repl-host提供的node_replMCP 主機(jī)承載模型側(cè)看到的是mcp__node_repl__js。因此跨調(diào)用連續(xù)性的唯一邊界是BrowserControl 的標(biāo)簽頁(yè)tabs而不是 JavaScript 變量、模塊緩存或某個(gè)browser/tab綁定。workflow.md 正是在這一前提下為 Agent 定義了一套嚴(yán)格的六步操作協(xié)議。下面逐步展開(kāi)并給出可直接復(fù)制運(yùn)行的代碼。一、先決條件每一段代碼都從 Skill 引導(dǎo)開(kāi)始workflow.md 的每一段代碼都假設(shè)control-browserSkill 的引導(dǎo)已經(jīng)在當(dāng)前這個(gè)全新的 JS kernel中運(yùn)行過(guò)。引導(dǎo)代碼做兩件事解析插件根目錄并導(dǎo)入browser-client模塊然后注冊(cè)agent.browsers運(yùn)行時(shí)。引導(dǎo)不選擇后端真正的后端選擇在引導(dǎo)之后的同一次調(diào)用里完成。const browserPluginRoot process.env.ZCODE_PLUGIN_ROOT; if (!browserPluginRoot) { throw new Error(Browser plugin root is unavailable in the node_repl host); } const { join } await import(node:path); const { pathToFileURL } await import(node:url); const browserClientUrl pathToFileURL( join(browserPluginRoot, scripts, browser-client.mjs), ).href; const { setupBrowserRuntime } await import(browserClientUrl); await setupBrowserRuntime({ globals: globalThis });從源碼看browser-client的實(shí)現(xiàn)很薄它從zcode/core/browser-client引入真正的setupBrowserRuntime并通過(guò)zcode/node-repl-host/runtime-bridge讀取當(dāng)前 kernel 的運(yùn)行時(shí)橋見(jiàn) src/browser-client.ts。核心約束是每次js調(diào)用都必須重新引導(dǎo)并重建同一個(gè)瀏覽器包裝對(duì)象而不是因?yàn)閗ernel 是新的就去偷偷切換后端iab/extension/cdp也不是把上一個(gè)調(diào)用的tabid 直接拿來(lái)用。control-browserSkill 強(qiáng)調(diào)可用的后端必須來(lái)自await agent.browsers.list()的廣告desktop 通常廣告 IABCLI 以--browser-useheadless啟動(dòng)時(shí)廣告托管 Chromium 為cdpheadless 是 CDP 的啟動(dòng)/顯示模式不是第四種后端類(lèi)型。未被廣告的后端絕不可視為可用。在第一次瀏覽器調(diào)用中選擇后端后應(yīng)立即把完整的 API 文檔發(fā)給模型一次nodeRepl.write(await browser.documentation())之后的每次新鮮調(diào)用只需重復(fù)相同的后端選擇即可。二、六步工作流全解workflow.md 將一次瀏覽器自動(dòng)化任務(wù)歸納為六個(gè)步驟。核心設(shè)計(jì)思想是模型必須在每個(gè)決策點(diǎn)拿到可驗(yàn)證的事實(shí)verified facts——包括標(biāo)簽頁(yè)的 id、URL、標(biāo)題、快照中的 role/accessible name——然后用下一個(gè)獨(dú)立 JS 調(diào)用基于這些事實(shí)行動(dòng)絕不憑猜測(cè)、絕不靠記憶。第 1 步目標(biāo)選擇協(xié)議——先列全量再按已驗(yàn)證事實(shí)綁定每一個(gè)「邏輯標(biāo)簽頁(yè)操作批次」開(kāi)始之前用一個(gè)專(zhuān)門(mén)的 JS 調(diào)用把所有受控標(biāo)簽頁(yè)完整地返回給模型const browser await agent.browsers.getDefault(); const controlledTabs await browser.tabs.list(); controlledTabs;tabs.list()返回的是元數(shù)據(jù)數(shù)組TabInfo[]包含當(dāng)前active標(biāo)記與真實(shí) CSSviewport: { width, height }不是可操作的Tab對(duì)象。模型查看這段輸出后在下一個(gè) JS 調(diào)用里按穩(wěn)定 id 或經(jīng)核實(shí)的 URL/標(biāo)題事實(shí)匹配目標(biāo)頁(yè)再調(diào)用tabs.get(id)激活它c(diǎn)onst browser await agent.browsers.getDefault(); const tab await browser.tabs.get(verified-tab-id-from-the-prior-list); await tab.playwright.domSnapshot();幾條硬性規(guī)則值得注意絕不因?yàn)榱斜矸强站瓦x[0]。多標(biāo)簽頁(yè)場(chǎng)景下按數(shù)組位置選目標(biāo)是被明確禁止的at(-1)、憑記憶的 id 同樣不行。列表空也不等于可以隨便開(kāi)新頁(yè)。如果受控列表里沒(méi)有匹配項(xiàng)先用下一次調(diào)用把a(bǔ)wait browser.user.openTabs()返回給模型——這是用戶(hù)標(biāo)簽頁(yè)瀏覽器已打開(kāi)但尚未交給 Browser Use 控制的頁(yè)面然后只認(rèn)領(lǐng)claim核實(shí)的用戶(hù)標(biāo)簽頁(yè)事實(shí)。只有兩輪觀察都失敗受控列表無(wú)匹配、用戶(hù)標(biāo)簽頁(yè)也無(wú)匹配時(shí)才創(chuàng)建新標(biāo)簽頁(yè)。注意tabs.get()只綁定當(dāng)前會(huì)話(huà)已受控的標(biāo)簽頁(yè)openTabs()返回的 id 不能直接傳給tabs.get()必須先browser.user.claimTab(info)參見(jiàn) docs/tab-claiming-iab.md。這套先列全量 → 核對(duì) → 綁定的流程在 SKILL.md 里被稱(chēng)為 pre-action target-selection protocol與后面第 5 步的 post-action 組合觀察combined observation是兩個(gè)不同的協(xié)議不要混用。第 2 步任務(wù)給出新 URL 時(shí)——選擇、打開(kāi)、導(dǎo)航一次如果任務(wù)點(diǎn)名了一個(gè)新 URL優(yōu)先考慮復(fù)用感知的入口agent.browsers.open(url)它會(huì)復(fù)用同站點(diǎn)同 hostname的受控標(biāo)簽頁(yè)、激活到用戶(hù)可見(jiàn)并原地導(dǎo)航而不是每次導(dǎo)航都堆一個(gè)新標(biāo)簽頁(yè)。只有確實(shí)需要并行獨(dú)立標(biāo)簽頁(yè)時(shí)才顯式創(chuàng)建并走如下導(dǎo)航序列const browser await agent.browsers.getForUrl(https://example.com); const tab await browser.tabs.new(); await tab.goto(https://example.com); await tab.playwright.waitForLoadState({ state: domcontentloaded }); await tab.playwright.domSnapshot();getForUrl(url)用于「有目標(biāo) URL 但用戶(hù)沒(méi)有顯式選擇瀏覽器」的場(chǎng)景會(huì)按 URL 選擇合適后端。workflow.md 對(duì)導(dǎo)航后觀察有一個(gè)非常嚴(yán)格的強(qiáng)制約束每次tab.goto(url)成功之后、第一次讀取 title/URL/DOM 之前必須顯式調(diào)用waitForLoadState({ state: domcontentloaded })。這個(gè)顯式確認(rèn)必須保留在模型可見(jiàn)的軌跡trajectory里即使后端導(dǎo)航已經(jīng)完成也不許省略不許用networkidle代替networkidle存在于共享類(lèi)型中但被所有 ZCode 瀏覽器后端拒絕也不許用固定 sleep 代替。常規(guī) URL/加載狀態(tài)等待的預(yù)算被封頂在 3000ms。第 3 步用domSnapshot()讀頁(yè)面——AI/ARIA 樹(shù)是唯一定位事實(shí)來(lái)源await tab.playwright.domSnapshot()是默認(rèn)的頁(yè)面觀察與定位器事實(shí)來(lái)源ground truth。它返回的是緊湊的 AI/ARIA 樹(shù)包含計(jì)算出的角色role、可訪問(wèn)名稱(chēng)accessible name、狀態(tài)以及可用時(shí)的展開(kāi) iframe 內(nèi)容——而不是頁(yè)面的outerHTML。使用規(guī)則只從最新相關(guān)快照中出現(xiàn)的事實(shí)構(gòu)造 Playwright 定位器。絕不猜測(cè) label、可訪問(wèn)名稱(chēng)、placeholder、selector 或 URL 模式絕不用猜測(cè)的定位器去當(dāng)探索性探針exploratory probe消耗超時(shí)預(yù)算??煺绽镆呀?jīng)有目標(biāo)時(shí)直接基于快照事實(shí)行動(dòng)不要寫(xiě)evaluate()代碼去重新發(fā)現(xiàn)相關(guān)元素、枚舉 input、dump HTML 或遍歷 DOM。快照證實(shí)snapshot-proven的標(biāo)題或可見(jiàn)文本不需要link或button角色也能點(diǎn)擊不要用猜測(cè)的link角色去替換快照證實(shí)的heading。只要用戶(hù)已授權(quán)導(dǎo)航、且該標(biāo)題/文本定位器唯一就直接點(diǎn)擊它——事件可以冒泡到祖先卡片上的 JavaScript 處理器??煺照{(diào)用必須是 JS 單元格里的最后一個(gè)表達(dá)式或者把它傳給nodeRepl.write(...)。僅僅把結(jié)果賦值給本地變量并不會(huì)把 DOM 觀察結(jié)果返回給模型overview.md也有同樣強(qiáng)調(diào)。第 4 步確認(rèn)唯一性再執(zhí)行真實(shí)瀏覽器動(dòng)作當(dāng)唯一性不明顯時(shí)先確認(rèn)定位器唯一然后通過(guò)真實(shí)瀏覽器動(dòng)作執(zhí)行。count()為 0 時(shí)不要等待也不要執(zhí)行該定位器而是重新拍快照并重建count()大于 1 時(shí)收緊作用域而不是用位置快捷方式first()/last()/nth()都是被禁止的歧義捷徑。const input tab.playwright.getByRole(textbox, { name: Search }); if ((await input.count()) ! 1) throw new Error(Search locator is not unique); await input.fill(hello); await input.press(Enter);getByRole(..., { name })的name選項(xiàng)接受普通字符串或RegExp包括在 Node REPL VM 內(nèi)創(chuàng)建的RegExp。推薦的定位器事實(shí)優(yōu)先級(jí)來(lái)自 docs/playwright.md依次是穩(wěn)定 test id /data-*屬性 → 穩(wěn)定精確href→ 帶快照證實(shí)可訪問(wèn)名稱(chēng)的語(yǔ)義角色 → 作用域化可見(jiàn)文本 → 基于已知 DOM 事實(shí)的 CSS selector → 作用域化 DOM/CUA 兜底。像Search、Menu、Close這類(lèi)通用名稱(chēng)默認(rèn)就是有歧義的行動(dòng)前必須收窄作用域。第 5 步動(dòng)作之后——取最廉價(jià)的觀察組合標(biāo)簽頁(yè)事實(shí)判斷效果動(dòng)作之后收集能回答下一個(gè)問(wèn)題的最廉價(jià)觀察優(yōu)先做針對(duì)性的定位器狀態(tài)檢查需要新的定位器事實(shí)時(shí)才再拍一次domSnapshot()。每個(gè)觀察周期最多執(zhí)行一個(gè)改變狀態(tài)的動(dòng)作at most one state-changing action per observation cycle。判斷動(dòng)作成敗的標(biāo)準(zhǔn)非常關(guān)鍵源標(biāo)簽頁(yè) URL 沒(méi)變并不能證明點(diǎn)擊失敗了。判斷依據(jù)是預(yù)期效果是否出現(xiàn)而不是browser.tabs.list()是否非空。已經(jīng)存在的源標(biāo)簽頁(yè)或無(wú)關(guān)的受控標(biāo)簽頁(yè)不是動(dòng)作效果。預(yù)期效果可以是源頁(yè)面的狀態(tài)變化也可以是URL/標(biāo)題經(jīng)核實(shí)與預(yù)期結(jié)果匹配的標(biāo)簽頁(yè)。當(dāng)動(dòng)作可能打開(kāi)彈窗/新標(biāo)簽頁(yè)、而源標(biāo)簽頁(yè)沒(méi)顯示預(yù)期效果時(shí)要在同一個(gè)觀察單元格里無(wú)條件地同時(shí)讀取受控標(biāo)簽頁(yè)與用戶(hù)標(biāo)簽頁(yè)const [controlledTabs, userTabs] await Promise.all([ browser.tabs.list(), browser.user.openTabs(), ]); ({ controlledTabs, userTabs });把{ controlledTabs, userTabs }作為該單元格的最終結(jié)果返回讓模型基于兩張表做一次決策。不要先返回受控列表、再根據(jù)它的內(nèi)容決定要不要查用戶(hù)標(biāo)簽頁(yè)。下一個(gè)單元格里按核實(shí)的 id/url/title 匹配激活受控頁(yè)或認(rèn)領(lǐng)用戶(hù)頁(yè)。如果源頁(yè)面 組合標(biāo)簽頁(yè)觀察都沒(méi)有預(yù)期效果就拍新快照、選新定位器而不是重放舊的點(diǎn)擊。截圖相關(guān)的紀(jì)律workflow.md 與 docs/screenshot.md 一致打開(kāi)或?qū)Ш揭粋€(gè)普通頁(yè)面不是截圖理由默認(rèn)不要把 DOM 快照和截圖一起收集。只有用戶(hù)明確要求截圖、必須判斷視覺(jué)布局/渲染/圖像內(nèi)容、或目標(biāo)不在 DOM 快照里如 canvas / 自定義繪制 UI時(shí)才加載agent.documentation.get(screenshots)指引。一旦進(jìn)入截圖分支每張截圖必須在同一個(gè) JS 單元格里用nodeRepl.emitImage(await tab.screenshot())發(fā)出絕不把tab.screenshot()留作最終表達(dá)式也絕不直接返回它的Uint8Array字節(jié)內(nèi)部返回 PNG 字節(jié)對(duì)模型不可見(jiàn)。截圖超時(shí)不要立刻重試同一張截圖——底層 Chromium 捕獲可能仍在完成應(yīng)等待后重試或按顯式 in-flight 錯(cuò)誤重開(kāi)標(biāo)簽頁(yè)。超時(shí)與失敗恢復(fù)任何 Playwright 超時(shí)、strict-mode 失敗或 selector 解析失敗之后不要重試同一個(gè)定位器。拍一張新的domSnapshot()并從快照證實(shí)的事實(shí)重建。常規(guī)定位器/頁(yè)面狀態(tài)等待都在 3000ms 預(yù)算內(nèi)失敗只有無(wú)法觀察到任何具體狀態(tài)時(shí)才用更長(zhǎng)的固定 sleeptab.playwright.waitForTimeout(ms)注意根級(jí)tab.waitForTimeout在這個(gè)運(yùn)行時(shí)不存在。expectNavigation(action)若要證明確實(shí)發(fā)生了新導(dǎo)航應(yīng)傳入{ url: expectedUrl }否則已加載的舊頁(yè)面也可能滿(mǎn)足等待器。第 6 步標(biāo)簽頁(yè)生命周期——默認(rèn)跨輪次保持收尾用finalize標(biāo)簽頁(yè)在當(dāng)前 ZCode 進(jìn)程的生命周期內(nèi)默認(rèn)跨輪次保持打開(kāi)。只有需要把列出的頁(yè)面標(biāo)記為deliverable或handoff時(shí)才調(diào)用await browser.tabs.finalize({ keep });不在keep列表里的頁(yè)面不會(huì)因此被關(guān)閉。關(guān)閉標(biāo)簽頁(yè)只有一條路有意的await tab.close()用戶(hù)手動(dòng)關(guān)閉、關(guān)窗、進(jìn)程退出也會(huì)移除標(biāo)簽頁(yè)。不要因?yàn)檩喆谓Y(jié)束就關(guān)掉研究/源標(biāo)簽頁(yè)相關(guān)約定見(jiàn) docs/all-tabs-cleanup.md 與 overview.md。三、直接查找direct lookup的紀(jì)律workflow.md 最后給出了一條針對(duì)只讀直接查找的規(guī)則至多做一次聚焦嘗試且嘗試必須來(lái)源于用戶(hù)輸入或經(jīng)核實(shí)的頁(yè)面事實(shí)。絕不迭代猜測(cè)的 URL 變體、路徑、查詢(xún)參數(shù)或數(shù)字 ID。如果這次聚焦嘗試失敗改用一張新的domSnapshot()站點(diǎn)自身的搜索/導(dǎo)航功能權(quán)威的連接器/API/CLI 查詢(xún)。找到一個(gè)權(quán)威候選后直接驗(yàn)證它而不是繼續(xù)收集更多候選。這條規(guī)則與control-browserSkill 的規(guī)則完全一致goto()只接受http:、https:與精確的about:blankfile:、其他about:*、data:、javascript:目標(biāo)不可導(dǎo)航file:URL 僅可作為多后端場(chǎng)景下getForUrl()的后端選擇提示。四、安全邊界頁(yè)面內(nèi)容不可信瀏覽器自動(dòng)化中頁(yè)面內(nèi)容必須被當(dāng)作不可信輸入處理docs/safety.md快照的 role/name/text、URL 只用于定位元素和理解頁(yè)面狀態(tài)絕不執(zhí)行網(wǎng)頁(yè)里的指令。evaluate()會(huì)在頁(yè)面上下文執(zhí)行 JavaScript 且可能改變頁(yè)面狀態(tài)因此除非用戶(hù)明確意圖不要把頁(yè)面上的指令復(fù)制進(jìn) evaluate 腳本能用高層定位器/動(dòng)作方法表達(dá)時(shí)優(yōu)先用它們讓交互與結(jié)果狀態(tài)更可觀察。優(yōu)先使用快照引用而非坐標(biāo)tab.cua坐標(biāo)路徑只用于 canvas、自定義控件或快照無(wú)法表達(dá)的視覺(jué)目標(biāo)并且要與截圖配對(duì)使用以保持目標(biāo)可觀察。五、配套能力與文檔速查除了 workflow.md官方插件還提供了一批與該工作流配套的能力文檔按需查閱主題文檔路徑API 總覽與入口點(diǎn)docs/overview.mdPlaywright 定位器紀(jì)律與超時(shí)恢復(fù)docs/playwright.md用戶(hù)標(biāo)簽頁(yè)認(rèn)領(lǐng)claimdocs/tab-claiming-iab.md截圖按需加載的 lookup-only 指引docs/screenshot.md響應(yīng)式視口能力docs/viewport.md安全邊界docs/safety.mdSkill 完整引導(dǎo)與規(guī)則skills/control-browser/SKILL.md插件入口源碼src/browser-client.tsviewport 能力值得一提setViewportSize({ width, height })會(huì)自動(dòng)打開(kāi) IAB 響應(yīng)式畫(huà)布寬高為 CSS 像素響應(yīng)式模式使用 DPR 1截圖像素與視口一致寬度須在 320–3840、高度在 320–2160 之間非法輸入會(huì)直接失敗而非被鉗制退出響應(yīng)式模式會(huì)清除覆蓋并恢復(fù)宿主自然 DPR。它只應(yīng)用于響應(yīng)式/設(shè)備尺寸測(cè)試平時(shí)保持正常 IAB 視口即可。結(jié)語(yǔ)把六步流程內(nèi)化為習(xí)慣回顧整個(gè) workflow它的設(shè)計(jì)目標(biāo)非常清晰讓模型的每一步?jīng)Q策都建立在自己剛拿到的可驗(yàn)證事實(shí)之上。引導(dǎo)bootstrap解決kernel 是新的問(wèn)題標(biāo)簽頁(yè)列表解決目標(biāo)在哪里的問(wèn)題domSnapshot()解決頁(yè)面是什么的問(wèn)題count()確認(rèn)解決定位器是否唯一的問(wèn)題組合觀察{ controlledTabs, userTabs }解決動(dòng)作有沒(méi)有生效的問(wèn)題finalize/close解決標(biāo)簽頁(yè)怎么收尾的問(wèn)題。按這套協(xié)議執(zhí)行瀏覽器自動(dòng)化軌跡會(huì)穩(wěn)定、可復(fù)現(xiàn)、且每一步都有據(jù)可查——這正是 ZCode Browser Use 在 workflow.md 中希望 Agent 內(nèi)化的行為準(zhǔn)則。贊分享人工智能大模型代碼智能體AI Agent桌面應(yīng)用后端前端CLI【免費(fèi)下載鏈接】ZCodeZCode 是 AI 編程工作臺(tái)提供桌面應(yīng)用、瀏覽器界面和終端 Agent。本倉(cāng)庫(kù)包含客戶(hù)端、后端服務(wù)、共享 UI以及 Agent CLI 與運(yùn)行時(shí)源碼。項(xiàng)目地址https://gitcode.com/zai-org/ZCode點(diǎn)擊查看免費(fèi)下載相關(guān)推薦ZCode Browser Use 工作流全解析基于 control-browser Skill 的瀏覽器自動(dòng)化操作規(guī)范ZCode Browser Use 工作流全解析基于 control browser Skill 的瀏覽器自動(dòng)化操作規(guī)范 本文以 ZCode 內(nèi)置瀏覽器自動(dòng)化ZCode Browser Use 瀏覽器自動(dòng)化實(shí)戰(zhàn)control-browser 技能完整指南ZCode Browser Use 瀏覽器自動(dòng)化實(shí)戰(zhàn)control browser 技能完整指南 本文以 control browser 技能文檔 httpsNode.js v0.10.44 安全維護(hù)版本深度解析npm 憑據(jù)泄露修復(fù)與 OpenSSL 弱密碼套件禁用Node.js v0.10.44 安全維護(hù)版本深度解析npm 憑據(jù)泄露修復(fù)與 OpenSSL 弱密碼套件禁用 Node.js v0.10.44 是 v0.10人工智能大模型代碼智能體AI Agent桌面應(yīng)用后端前端CLI插件系統(tǒng)上一篇ElastAlert 自定義規(guī)則開(kāi)發(fā)從 YAML 配置到 Python 插件編寫(xiě)下一篇twin.macro與Web Assembly交互樣式創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考