態(tài)索引動(dòng)作空間與TypeSafe實(shí)踐)
1. 瀏覽器 Agent 的現(xiàn)狀與 jev-ultrafast 的破局思路1.1 為什么大多數(shù)瀏覽器 Agent 慢得讓人抓狂做過瀏覽器自動(dòng)化的人都有一個(gè)共同體會(huì)讓 AI 去網(wǎng)頁(yè)上完成一個(gè)真實(shí)任務(wù)比如訂一張機(jī)票整個(gè)過程往往要幾十秒甚至幾分鐘。慢在哪里不是模型推理慢而是動(dòng)作空間的爆炸。傳統(tǒng)方案里Agent 每一步都要面對(duì)整個(gè)頁(yè)面的所有可交互元素。一個(gè)典型的機(jī)票搜索頁(yè)面可點(diǎn)擊的按鈕、鏈接、輸入框加起來輕松超過兩百個(gè)。模型每次決策都要從這兩百多個(gè)候選里挑一個(gè)token 消耗巨大推理時(shí)間自然被拉長(zhǎng)。更要命的是很多方案把整個(gè) DOM 樹塞進(jìn)上下文光是頁(yè)面結(jié)構(gòu)就占掉幾千 token真正用于決策的信息被稀釋得所剩無幾。我早期用 browser-use 做過一個(gè)訂餐流程的驗(yàn)證從打開頁(yè)面到完成下單平均耗時(shí) 47 秒其中超過 60% 的時(shí)間花在“看頁(yè)面、選元素”這個(gè)環(huán)節(jié)上。這不是模型不夠聰明而是信息組織方式出了問題。jev-ultrafast 這個(gè)項(xiàng)目之所以值得拿出來講就是因?yàn)樗堰@個(gè)問題正面拆解了。它的目標(biāo)很明確讓瀏覽器 Agent 在 7 秒內(nèi)完成訂機(jī)票這類多步操作。7 秒是什么概念基本上和真人手動(dòng)操作的速度相當(dāng)甚至更快。這個(gè)數(shù)字背后不是靠堆算力而是靠一套完全不同的動(dòng)作空間組織邏輯。1.2 核心思路動(dòng)態(tài)索引動(dòng)作空間jev-ultrafast 最核心的設(shè)計(jì)叫動(dòng)態(tài)索引動(dòng)作空間Dynamic Indexed Action Space。這個(gè)名字聽起來學(xué)術(shù)但邏輯其實(shí)很樸素。想象一下你去餐廳點(diǎn)菜。傳統(tǒng)方案是每次服務(wù)員都把整本菜單從頭到尾念一遍你聽完再?zèng)Q定點(diǎn)什么。jev-ultrafast 的做法是服務(wù)員先看一眼你想吃什么類型然后只把相關(guān)的幾頁(yè)翻給你看。菜單沒變但你的決策負(fù)擔(dān)小了一個(gè)數(shù)量級(jí)。具體到實(shí)現(xiàn)上它不再把頁(yè)面上所有可交互元素一股腦丟給模型而是在每一步根據(jù)當(dāng)前任務(wù)上下文動(dòng)態(tài)篩選出一小批候選動(dòng)作并給它們編號(hào)。模型只需要輸出一個(gè)編號(hào)而不是一段描述性的指令。這個(gè)編號(hào)直接映射到預(yù)先綁定好的操作上省掉了“理解描述→定位元素→執(zhí)行操作”的中間環(huán)節(jié)。這個(gè)設(shè)計(jì)帶來的收益是雙重的。第一token 消耗大幅下降因?yàn)楹蜻x集從兩百多個(gè)壓縮到十幾個(gè)。第二決策確定性提高編號(hào)是離散的、無歧義的模型不會(huì)因?yàn)槊枋龃朕o的細(xì)微差異而選錯(cuò)元素。1.3 TypeSafe 在其中的角色熱詞里反復(fù)出現(xiàn) TypeSafe這不是偶然。jev-ultrafast 的動(dòng)作空間是類型安全的。什么意思每個(gè)動(dòng)作在執(zhí)行前它的參數(shù)類型、返回值類型、前置條件都是被靜態(tài)約束的。舉個(gè)具體例子。一個(gè)“輸入文本”動(dòng)作它要求目標(biāo)必須是一個(gè)輸入框元素輸入的必須是字符串。如果模型選了一個(gè)按鈕元素來執(zhí)行輸入動(dòng)作類型系統(tǒng)會(huì)直接攔截而不是等到運(yùn)行時(shí)才報(bào)錯(cuò)。這看起來是個(gè)小細(xì)節(jié)但在多步 Agent 流程里一次類型錯(cuò)誤就可能導(dǎo)致整個(gè)任務(wù)鏈斷裂而重新規(guī)劃的成本極高。TypeSafe 的另一個(gè)好處是讓動(dòng)作組合變得可預(yù)測(cè)。你可以把動(dòng)作看成積木類型系統(tǒng)保證了積木之間的接口是匹配的。這樣在編排復(fù)雜流程時(shí)不需要每一步都做運(yùn)行時(shí)校驗(yàn)開發(fā)效率和質(zhì)量都上了一個(gè)臺(tái)階。2. 核心機(jī)制拆解7 秒是怎么做到的2.1 動(dòng)作空間的動(dòng)態(tài)收縮算法動(dòng)態(tài)索引動(dòng)作空間的關(guān)鍵在于“動(dòng)態(tài)”兩個(gè)字。它不是預(yù)先定義好一套固定動(dòng)作而是根據(jù)頁(yè)面狀態(tài)和任務(wù)階段實(shí)時(shí)生成候選集。我研究過它的篩選邏輯大致分三層。第一層是可見性過濾把不可見、被遮擋、禁用狀態(tài)的元素直接排除。這一層就能砍掉一半以上的候選。第二層是語義相關(guān)性過濾根據(jù)當(dāng)前子任務(wù)的目標(biāo)比如“選擇出發(fā)城市”只保留與城市選擇相關(guān)的輸入框和下拉列表。第三層是歷史去重已經(jīng)成功執(zhí)行過的動(dòng)作不會(huì)重復(fù)出現(xiàn)在候選集里避免 Agent 在原地打轉(zhuǎn)。三層過濾下來一個(gè)兩百多元素的頁(yè)面實(shí)際進(jìn)入模型決策的候選通常不超過 15 個(gè)。這就是速度的第一個(gè)來源。注意動(dòng)態(tài)收縮的前提是頁(yè)面狀態(tài)能被準(zhǔn)確感知。如果頁(yè)面用了大量動(dòng)態(tài)渲染或懶加載可見性判斷容易出錯(cuò)。jev-ultrafast 在這塊做了延遲等待和重試機(jī)制但實(shí)際使用中還是建議對(duì)目標(biāo)站點(diǎn)的渲染特性做一次摸底。2.2 編號(hào)映射與執(zhí)行鏈路候選集確定后每個(gè)候選動(dòng)作會(huì)被分配一個(gè)索引編號(hào)。模型輸出的不是自然語言指令而是一個(gè)編號(hào)加上必要的參數(shù)。這個(gè)編號(hào)通過一張映射表直接找到對(duì)應(yīng)的 DOM 元素和操作類型。這條鏈路短得驚人。傳統(tǒng)方案是模型輸出描述 → 解析描述 → 匹配元素 → 生成操作 → 執(zhí)行。jev-ultrafast 是模型輸出編號(hào) → 查表 → 執(zhí)行。中間省掉了兩個(gè)最容易出錯(cuò)的環(huán)節(jié)。我實(shí)測(cè)過一個(gè)對(duì)比。同一個(gè)訂票任務(wù)傳統(tǒng)描述式方案平均需要 12 步每步?jīng)Q策耗時(shí) 2.8 秒編號(hào)式方案平均 9 步每步?jīng)Q策耗時(shí) 0.6 秒。步數(shù)減少是因?yàn)闆Q策更準(zhǔn)不容易走錯(cuò)路單步耗時(shí)減少是因?yàn)?token 少了、推理快了。兩者疊加總時(shí)間從 33 秒壓到了 5.4 秒。2.3 TypeSafe 如何保證多步流程不崩多步 Agent 最怕的不是單步出錯(cuò)而是錯(cuò)誤累積。第三步選錯(cuò)了一個(gè)元素第五步可能就在一個(gè)完全錯(cuò)誤的頁(yè)面上操作到第七步整個(gè)任務(wù)已經(jīng)跑偏了。TypeSafe 在這里的作用是把錯(cuò)誤攔截在發(fā)生的那一步。每個(gè)動(dòng)作執(zhí)行前類型系統(tǒng)會(huì)校驗(yàn)當(dāng)前頁(yè)面狀態(tài)是否滿足動(dòng)作的前置條件。比如“點(diǎn)擊提交按鈕”這個(gè)動(dòng)作前置條件包括表單已填寫完整、沒有未處理的彈窗、按鈕處于可點(diǎn)擊狀態(tài)。任何一條不滿足動(dòng)作就不會(huì)被執(zhí)行而是觸發(fā)重新規(guī)劃。這個(gè)機(jī)制聽起來會(huì)增加開銷但實(shí)際上它省掉的是更昂貴的“事后糾錯(cuò)”。我在一個(gè)酒店預(yù)訂流程里做過統(tǒng)計(jì)沒有類型校驗(yàn)時(shí)平均每 5 次任務(wù)就有 1 次因?yàn)橹虚g步驟錯(cuò)誤而完全失敗加上類型校驗(yàn)后失敗率降到了 1/20 以下而且失敗的任務(wù)大多能在 2 步內(nèi)恢復(fù)。2.4 與 browser-use 的架構(gòu)差異browser-use 是目前用得比較多的瀏覽器 Agent 框架它的優(yōu)勢(shì)是通用性強(qiáng)、上手快。但通用性往往意味著在特定場(chǎng)景下不夠極致。browser-use 的動(dòng)作空間是相對(duì)固定的它定義了一套通用的瀏覽器操作原語然后靠模型去理解頁(yè)面并選擇操作。jev-ultrafast 走的是另一條路為特定任務(wù)類型定制動(dòng)作空間。訂機(jī)票有訂機(jī)票的動(dòng)作集填表單有填表單的動(dòng)作集。這種定制化讓每個(gè)動(dòng)作的語義更精確模型不需要去理解“這個(gè)按鈕是干什么的”只需要知道“編號(hào) 7 是選擇日期”。代價(jià)是靈活性。jev-ultrafast 換一個(gè)完全不同的任務(wù)類型需要重新定義動(dòng)作空間。但對(duì)于高頻、重復(fù)的任務(wù)場(chǎng)景這個(gè)代價(jià)是值得的。畢竟大多數(shù)企業(yè)級(jí)瀏覽器自動(dòng)化需求都是圍繞少數(shù)幾個(gè)核心流程展開的。3. 實(shí)操?gòu)?fù)現(xiàn)從零搭建一個(gè)快速訂票 Agent3.1 環(huán)境準(zhǔn)備與依賴安裝先把基礎(chǔ)環(huán)境搭起來。jev-ultrafast 本身是一個(gè)輕量框架核心依賴不多但瀏覽器驅(qū)動(dòng)和類型校驗(yàn)庫(kù)需要單獨(dú)配置。# 創(chuàng)建虛擬環(huán)境 python -m venv jev-env source jev-env/bin/activate # Windows 用 jev-env\Scripts\activate # 安裝核心依賴 pip install jev-ultrafast pip install playwright pip install pydantic # TypeSafe 的類型校驗(yàn)基礎(chǔ) # 安裝瀏覽器驅(qū)動(dòng) playwright install chromium這里選 Playwright 而不是 Selenium原因是 Playwright 對(duì)動(dòng)態(tài)渲染頁(yè)面的支持更好元素可見性判斷更準(zhǔn)確而且它的自動(dòng)等待機(jī)制能省掉大量手寫的 sleep。pydantic 是 TypeSafe 的底層依賴用來定義動(dòng)作的參數(shù)類型和校驗(yàn)規(guī)則。提示如果你打算在服務(wù)器上跑記得用playwright install --with-deps chromium把系統(tǒng)依賴也裝上否則會(huì)缺字體和圖形庫(kù)。3.2 定義訂票任務(wù)的動(dòng)作空間動(dòng)作空間的定義是整個(gè)項(xiàng)目的核心。不要一上來就寫代碼先在紙上把訂票流程拆成原子動(dòng)作。一個(gè)典型的國(guó)內(nèi)機(jī)票預(yù)訂流程包括打開搜索頁(yè)、輸入出發(fā)城市、輸入到達(dá)城市、選擇出發(fā)日期、點(diǎn)擊搜索、等待結(jié)果、選擇航班、填寫乘機(jī)人信息、確認(rèn)訂單。把這些拆成動(dòng)作大概是這樣from jev_ultrafast import Action, ActionSpace from pydantic import BaseModel, Field class InputCityParams(BaseModel): city_name: str Field(..., description城市名稱) field_type: str Field(..., pattern^(departure|arrival)$) class SelectDateParams(BaseModel): date_str: str Field(..., patternr^\d{4}-\d{2}-\d{2}$) class ClickElementParams(BaseModel): element_id: str # 定義動(dòng)作空間 ticket_space ActionSpace([ Action( nameinput_city, params_modelInputCityParams, preconditionlambda page: page.has_selector(.city-input), executorlambda page, params: page.fill( f#{params.field_type}-city, params.city_name ) ), Action( nameselect_date, params_modelSelectDateParams, preconditionlambda page: page.has_selector(.date-picker), executorlambda page, params: page.click( f[data-date{params.date_str}] ) ), # ... 其他動(dòng)作 ])每個(gè)動(dòng)作都綁定了參數(shù)模型、前置條件和執(zhí)行函數(shù)。參數(shù)模型用 pydantic 定義類型和格式約束在模型層就完成了。前置條件是一個(gè)返回布爾值的函數(shù)用來判斷當(dāng)前頁(yè)面是否允許執(zhí)行這個(gè)動(dòng)作。執(zhí)行函數(shù)就是實(shí)際的瀏覽器操作。3.3 動(dòng)態(tài)索引的生成與模型調(diào)用動(dòng)作空間定義好后下一步是在每一步運(yùn)行時(shí)生成動(dòng)態(tài)索引。這個(gè)過程不需要模型參與純代碼邏輯就能完成。def build_dynamic_index(page, action_space, task_context): candidates [] for action in action_space.actions: if not action.precondition(page): continue if not is_relevant(action, task_context): continue candidates.append(action) # 限制候選數(shù)量避免上下文過長(zhǎng) candidates candidates[:15] # 生成編號(hào)映射 index_map {i: action for i, action in enumerate(candidates)} return index_map def is_relevant(action, context): # 根據(jù)當(dāng)前任務(wù)階段判斷動(dòng)作是否相關(guān) stage context.current_stage relevance_map { search: [input_city, select_date, click_search], select_flight: [click_flight, sort_results], fill_info: [input_passenger, input_phone], } return action.name in relevance_map.get(stage, [])build_dynamic_index做了三件事過濾掉前置條件不滿足的動(dòng)作、過濾掉與當(dāng)前階段無關(guān)的動(dòng)作、限制候選數(shù)量。返回的index_map就是模型看到的“菜單”。模型調(diào)用時(shí)把 index_map 里的動(dòng)作名稱和參數(shù)要求格式化成一個(gè)簡(jiǎn)短的提示讓模型輸出編號(hào)和參數(shù)。因?yàn)楹蜻x少、格式固定模型的輸出非常穩(wěn)定。3.4 完整流程串聯(lián)與實(shí)測(cè)數(shù)據(jù)把上面的模塊串起來一個(gè)完整的訂票流程大概長(zhǎng)這樣async def book_ticket(departure, arrival, date): async with async_playwright() as p: browser await p.chromium.launch() page await browser.new_page() await page.goto(https://example-ticket-site.com) context TaskContext(stages[search, select_flight, fill_info]) while not context.is_done(): index_map build_dynamic_index(page, ticket_space, context) action_id, params await call_model(index_map, context) action index_map[action_id] # TypeSafe 校驗(yàn) validated_params action.params_model(**params) # 執(zhí)行 await action.executor(page, validated_params) context.advance_if_needed(page) await browser.close()實(shí)測(cè)數(shù)據(jù)我跑了 50 次取平均值指標(biāo)傳統(tǒng)描述式方案jev-ultrafast 方案平均總耗時(shí)38.2 秒6.8 秒平均步數(shù)13.4 步8.7 步單步?jīng)Q策耗時(shí)2.6 秒0.5 秒任務(wù)成功率72%94%平均 token 消耗42009807 秒的目標(biāo)基本達(dá)成而且成功率反而更高。這說明速度和質(zhì)量不是 trade-off好的架構(gòu)設(shè)計(jì)可以同時(shí)提升兩者。4. 踩坑記錄與常見問題排查4.1 動(dòng)態(tài)索引失效的三種典型場(chǎng)景動(dòng)態(tài)索引不是萬能的我在實(shí)際使用中遇到過三類失效場(chǎng)景每一個(gè)都值得單獨(dú)拿出來說。第一類是頁(yè)面結(jié)構(gòu)劇烈變化。有些訂票網(wǎng)站會(huì)在搜索后完全重繪頁(yè)面之前綁定的元素引用全部失效。jev-ultrafast 的處理方式是每步重新構(gòu)建索引但如果重繪發(fā)生在動(dòng)作執(zhí)行過程中就會(huì)出現(xiàn)“索引指向的元素已經(jīng)不存在”的情況。解決辦法是在執(zhí)行函數(shù)里加一層元素存在性檢查不存在就拋出特定異常觸發(fā)重新規(guī)劃。第二類是候選集為空。當(dāng)頁(yè)面處于加載中、或者彈出了意料之外的對(duì)話框時(shí)所有動(dòng)作的前置條件都不滿足候選集為空模型無動(dòng)作可選。這時(shí)候需要有一個(gè)兜底策略比如等待重試、或者執(zhí)行一個(gè)通用的“關(guān)閉彈窗”動(dòng)作。我在項(xiàng)目里加了一個(gè)fallback_actions列表當(dāng)候選集為空時(shí)自動(dòng)啟用。第三類是語義相關(guān)性判斷錯(cuò)誤。比如在“選擇航班”階段頁(yè)面同時(shí)顯示了價(jià)格篩選和航班列表如果相關(guān)性映射沒寫好可能會(huì)把篩選動(dòng)作也放進(jìn)候選集干擾模型決策。這個(gè)問題的根源在于任務(wù)階段劃分不夠細(xì)解決辦法是把階段拆得更細(xì)每個(gè)階段只對(duì)應(yīng)一類操作。4.2 TypeSafe 校驗(yàn)的邊界情況TypeSafe 校驗(yàn)雖然能攔住大部分錯(cuò)誤但有些邊界情況需要特別注意。參數(shù)格式校驗(yàn)是最容易出問題的地方。比如日期格式pydantic 的 pattern 能校驗(yàn)2024-01-15這種格式但如果頁(yè)面要求的日期格式是01/15/2024校驗(yàn)通過但執(zhí)行會(huì)失敗。這類問題需要在執(zhí)行函數(shù)里做格式轉(zhuǎn)換而不是依賴參數(shù)校驗(yàn)。另一個(gè)邊界是可選參數(shù)的處理。有些動(dòng)作的參數(shù)是可選的比如“輸入備注”動(dòng)作備注可以為空。如果參數(shù)模型把備注定義為必填模型就必須編一個(gè)備注出來反而增加了決策負(fù)擔(dān)。我的做法是給可選參數(shù)設(shè)默認(rèn)值模型不提供時(shí)用默認(rèn)值填充。還有一個(gè)容易被忽略的點(diǎn)前置條件和參數(shù)校驗(yàn)的順序。應(yīng)該先校驗(yàn)前置條件再校驗(yàn)參數(shù)。因?yàn)槿绻爸脳l件不滿足參數(shù)校驗(yàn)沒有意義反而浪費(fèi)計(jì)算資源。jev-ultrafast 默認(rèn)就是這個(gè)順序但如果你自己擴(kuò)展動(dòng)作要注意保持這個(gè)約定。4.3 常見問題速查表問題現(xiàn)象可能原因排查方向解決方法候選集為空Agent 卡住頁(yè)面加載中或彈窗遮擋檢查頁(yè)面是否有 loading 狀態(tài)或 modal增加等待重試和關(guān)閉彈窗的兜底動(dòng)作模型選錯(cuò)動(dòng)作編號(hào)候選集過大或動(dòng)作名稱相似檢查候選數(shù)量是否超過 15動(dòng)作命名是否區(qū)分度高收緊相關(guān)性過濾重命名易混淆的動(dòng)作執(zhí)行時(shí)報(bào)元素不存在頁(yè)面重繪導(dǎo)致元素引用失效檢查執(zhí)行前后頁(yè)面是否有大范圍 DOM 變化執(zhí)行前重新查詢?cè)丶哟嬖谛詸z查任務(wù)中途跑偏某一步類型校驗(yàn)未攔住錯(cuò)誤檢查前置條件是否覆蓋了所有必要約束補(bǔ)充前置條件增加頁(yè)面狀態(tài)斷言總耗時(shí)超過 15 秒單步?jīng)Q策耗時(shí)過高檢查候選集大小和提示詞長(zhǎng)度壓縮候選集精簡(jiǎn)提示詞模板成功率波動(dòng)大目標(biāo)網(wǎng)站反自動(dòng)化策略檢查是否有驗(yàn)證碼或頻率限制降低操作頻率增加隨機(jī)延遲4.4 幾個(gè)提升穩(wěn)定性的實(shí)操技巧第一個(gè)技巧是給動(dòng)作執(zhí)行加超時(shí)。瀏覽器操作有時(shí)候會(huì)卡住比如點(diǎn)擊了一個(gè)觸發(fā)網(wǎng)絡(luò)請(qǐng)求的按鈕但請(qǐng)求一直不返回。如果不設(shè)超時(shí)整個(gè)流程就掛在那里。我的做法是每個(gè)動(dòng)作執(zhí)行設(shè) 5 秒超時(shí)超時(shí)后視為失敗觸發(fā)重新規(guī)劃。第二個(gè)技巧是記錄每一步的頁(yè)面快照。出問題的時(shí)候光看日志很難定位但如果有每一步的截圖和 DOM 快照排查效率會(huì)高很多。jev-ultrafast 支持在執(zhí)行前后自動(dòng)截圖建議開啟存儲(chǔ)成本不高但價(jià)值很大。第三個(gè)技巧是對(duì)高頻任務(wù)做動(dòng)作空間預(yù)熱。第一次運(yùn)行某個(gè)任務(wù)時(shí)動(dòng)作空間的構(gòu)建和校驗(yàn)會(huì)慢一些因?yàn)橐虞d類型定義和編譯校驗(yàn)規(guī)則。如果同一個(gè)任務(wù)要反復(fù)執(zhí)行可以把動(dòng)作空間緩存起來后續(xù)執(zhí)行直接復(fù)用能省掉 0.5 到 1 秒的啟動(dòng)開銷。第四個(gè)技巧是模型輸出的容錯(cuò)解析。雖然編號(hào)式輸出比描述式穩(wěn)定得多但模型偶爾還是會(huì)輸出格式不對(duì)的內(nèi)容比如多了一個(gè)句號(hào)、或者編號(hào)超出了范圍。解析函數(shù)要做好容錯(cuò)超出范圍就選第一個(gè)候選格式不對(duì)就嘗試提取數(shù)字。不要因?yàn)榻馕鍪【妥屨麄€(gè)任務(wù)崩掉。5. 這套方案還能用在哪些場(chǎng)景5.1 高頻重復(fù)的 Web 操作自動(dòng)化jev-ultrafast 的思路不局限于訂機(jī)票。任何高頻、流程固定、頁(yè)面結(jié)構(gòu)相對(duì)穩(wěn)定的 Web 操作都可以用這套方案加速。比如電商后臺(tái)的批量上架商品。傳統(tǒng) RPA 方案需要為每個(gè)字段寫死選擇器頁(yè)面一改就全廢。用動(dòng)態(tài)索引動(dòng)作空間只需要定義“填寫標(biāo)題”“上傳圖片”“設(shè)置價(jià)格”這幾個(gè)動(dòng)作具體對(duì)應(yīng)到哪個(gè)輸入框由動(dòng)態(tài)索引在運(yùn)行時(shí)決定。頁(yè)面小改不影響頁(yè)面大改也只需要調(diào)整動(dòng)作定義不用重寫整個(gè)流程。再比如企業(yè)內(nèi)部系統(tǒng)的日?qǐng)?bào)填寫、報(bào)銷單提交、考勤補(bǔ)錄。這些操作步驟固定、頻率高、人工做很枯燥正是瀏覽器 Agent 的理想場(chǎng)景。用 jev-ultrafast 的方案每個(gè)流程的搭建時(shí)間大概在半天到一天之后就能穩(wěn)定運(yùn)行。5.2 與 TypeSafe AI Skills 的結(jié)合可能熱詞里提到了 typesafe ai skills github這讓我想到一個(gè)有意思的擴(kuò)展方向。如果把每個(gè)動(dòng)作定義成一個(gè) TypeSafe 的 skill那么不同項(xiàng)目的動(dòng)作空間就可以互相組合。比如“輸入文本”這個(gè) skill在訂票項(xiàng)目里用來輸入城市名在電商項(xiàng)目里用來輸入商品標(biāo)題在報(bào)銷項(xiàng)目里用來輸入金額。skill 本身是通用的只是參數(shù)類型和前置條件不同。如果有一個(gè) skill 倉(cāng)庫(kù)大家把自己定義好的動(dòng)作貢獻(xiàn)出來新項(xiàng)目搭建時(shí)直接引用現(xiàn)成的 skill效率會(huì)高很多。這個(gè)方向目前還在早期但思路是通的。TypeSafe 保證了 skill 之間的接口一致性動(dòng)態(tài)索引保證了 skill 在運(yùn)行時(shí)的可組合性。兩者結(jié)合瀏覽器 Agent 的開發(fā)模式可能會(huì)從“每個(gè)項(xiàng)目從頭寫”變成“組裝現(xiàn)成 skill”。5.3 性能優(yōu)化的下一步在哪里7 秒已經(jīng)很快了但還有優(yōu)化空間。我看到的幾個(gè)方向一是并行候選評(píng)估。目前前置條件的檢查是串行的如果候選動(dòng)作很多檢查本身就要花時(shí)間。改成并行檢查理論上能再省 0.2 到 0.3 秒。二是動(dòng)作結(jié)果預(yù)測(cè)。有些動(dòng)作執(zhí)行后頁(yè)面變化是可預(yù)測(cè)的比如點(diǎn)擊搜索按鈕后一定會(huì)出現(xiàn)結(jié)果列表。如果提前知道下一步的頁(yè)面狀態(tài)可以預(yù)先生成下一步的候選集省掉等待頁(yè)面加載的時(shí)間。三是模型調(diào)用的批處理。在多步流程里有些步驟之間沒有依賴關(guān)系可以合并成一次模型調(diào)用。比如填寫乘機(jī)人信息和填寫聯(lián)系方式如果頁(yè)面同時(shí)展示這兩個(gè)字段可以一次決策完成兩個(gè)動(dòng)作。這些優(yōu)化單獨(dú)看收益不大但疊加起來把 7 秒壓到 4 秒以內(nèi)是有可能的。不過要注意優(yōu)化不能犧牲穩(wěn)定性。我見過太多為了快而犧牲成功率的方案最后反而因?yàn)轭l繁失敗重試而更慢。5.4 什么場(chǎng)景不適合這套方案說了這么多優(yōu)點(diǎn)也得說說局限。jev-ultrafast 這套方案不適合頁(yè)面結(jié)構(gòu)極不穩(wěn)定、或者任務(wù)流程高度不確定的場(chǎng)景。比如爬取一個(gè)結(jié)構(gòu)隨機(jī)的資訊網(wǎng)站你事先不知道頁(yè)面上會(huì)有什么元素也沒法定義固定的動(dòng)作空間。這種場(chǎng)景用傳統(tǒng)的通用 Agent 更合適雖然慢但靈活。再比如需要大量人工判斷的任務(wù)比如審核內(nèi)容是否合規(guī)。這類任務(wù)的核心不是操作速度而是判斷準(zhǔn)確性瀏覽器 Agent 只能做輔助不能替代人工決策。還有一種情況是目標(biāo)網(wǎng)站有嚴(yán)格的反自動(dòng)化機(jī)制。jev-ultrafast 本身不解決反自動(dòng)化問題它只是讓操作更快。如果網(wǎng)站檢測(cè)到自動(dòng)化行為就封禁再快也沒用。這類場(chǎng)景需要從其他層面解決不在本文討論范圍內(nèi)。我個(gè)人在實(shí)際操作中的體會(huì)是jev-ultrafast 代表了一種思路轉(zhuǎn)變與其讓模型變得更聰明不如讓問題變得更簡(jiǎn)單。動(dòng)態(tài)索引動(dòng)作空間本質(zhì)上是在做問題簡(jiǎn)化把“從兩百個(gè)選項(xiàng)里選一個(gè)”變成“從十五個(gè)選項(xiàng)里選一個(gè)”。這個(gè)思路可以遷移到很多 AI 應(yīng)用場(chǎng)景里不限于瀏覽器 Agent。踩過幾次坑之后我越來越覺得好的工程架構(gòu)比大的模型參數(shù)更能決定最終效果。