iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
AI 自動化

測試的前提正在改變:AI 時代下 QA 的思維轉換系列 第 13 篇

[Day13] AI E2E - Browser-use 詳細介紹,底層運作邏輯與常用功能

  • 分享至 

  • xImage
  •  

上一篇把 Browser-use 裝好,也跑完第一支不用寫 selector 的自動化了。

但真的要拿來跑測試,光那幾行不夠。

要跟團隊的系統、腳本長期整合,就得先知道它能「控制」到什麼程度、能「留下」什麼證據。

這篇先看底層,再看六類常用功能:

分類 解決什麼問題
底層運作 它是怎麼從一句話,找到畫面上那顆按鈕的
Agent 設定 控制 AI 怎麼跑、跑多久、遵守什麼規則
Browser 設定 控制瀏覽器環境,讓每次執行條件一致
Tools 自訂動作 把不該讓 AI 摸索的步驟,收回來自己寫
Error Handling 出錯時它會怎麼處理、我們能控制到哪
敏感資料 帳密不要送進 LLM
結果輸出與證據 失敗時能回頭查,成功時能自己驗證

Browser-use 詳細底層運作

上一篇講過它大致的流程,但這裡要詳細拆開來看每一塊原理。

https://ithelp.ithome.com.tw/upload/images/20260926/20121445KyA8qEmbE0.png

1. LLM 把 Prompt 轉成結構化指令

例如使用 GPT、Claude 等理解 Prompt 後產出一份 JSON,內容包含:

  • 行為:click / verify
  • 元件類型:button / text
  • 文字:登入
  • 位置:右上角

2. 擷取當前頁面:整頁 HTML + 截圖

3. 核心技術:自動找出定位點

做法 怎麼做
視覺為主 把截圖+HTML 片段餵給有視覺能力的模型,問「哪個是右上角的登入按鈕?」,讓它回傳元素與 XPath
結構為主 先用 HTML 分析工具整理出所有 button / a / div,再讓 LLM 挑出最符合語意的那一個

4. 核心技術:行為執行器

把 JSON 指令轉成真正的瀏覽器操作:

def execute_action(action, driver):
    if action["type"] == "click":
        element = find_element(action["target"], driver)
        element.click()
    # verify、input、scroll... 每種行為都要自己寫

5. 核心技術:自我修復

  • 多策略定位:先用 ID 或唯一的 class,找不到改用文字,再不行改用相對座標
  • 重試+LLM 協助:還是找不到,就拿最新畫面再問一次模型「登入按鈕跑去哪了?」,或在畫面上標出所有可疑元件,重新分析

Browser-use 就是把這幾塊都包好了,所以我們一句話就能跑。

而後面要講的常用功能,其實就是在調整每一塊:

底層環節 對應的功能
LLM 解析 Prompt task、extend_system_message
擷取畫面(HTML+截圖) use_vision、Browser 設定
行為執行器 Tools 自訂動作
自我修復 max_failures、Error Handling

注意:這段的理解非常重要!後續文章會提到這個概念


Borwser-use 常用功能

一、Agent 設定:控制 AI 怎麼跑

from browser_use import Agent, ChatOpenAI

agent = Agent(
    task="搜尋「QA 三十天養成日記」,確認搜尋結果有出現列表",
    llm=ChatOpenAI(model="gpt-4.1-mini"),
    initial_actions=[{"navigate": {"url": "https://www.google.com"}}],
    extend_system_message="禁止點擊任何「刪除」「送出訂單」按鈕。",
    max_failures=3,
    max_actions_per_step=3,
)

history = await agent.run(max_steps=20)
參數 為什麼 QA 會需要
max_steps(放在 run()) 最多走幾步,預設 100。不設的話,卡住時它會一直繞
max_failures 同一步最多重試幾次,控制「自我修復」能修到什麼程度
max_actions_per_step 每一步最多做幾個動作,調小一點過程比較好追
use_vision 要不要把截圖給模型看,關掉省 token,但判斷畫面的能力會變弱
initial_actions 開跑前先執行的固定動作,不經過 AI,前置步驟不用讓它猜
extend_system_message 團隊的測試規則寫在這,每支測試都自動帶上

固定的步驟寫死,AI 只負責真正需要判斷的部分。


二、Browser 設定:讓每次執行條件一致

from browser_use import Browser

browser = Browser(
    headless=True,
    window_size={"width": 1920, "height": 1080},
    allowed_domains=["*.google.com"],
    wait_for_network_idle_page_load_time=3.0,
    record_video_dir="logs/videos",
)
參數 為什麼 QA 會需要
headless 不開視窗跑,接 CI 一定要開
window_size / viewport 桌機版、手機版畫面不一樣,要固定
allowed_domains 限制只能去哪些網域
wait_for_network_idle_page_load_time 頁面慢的系統要調大,不然畫面還沒出來就開始判斷
record_video_dir 錄下整段執行影片,失敗時直接看最快

自我修復有時候「想辦法」的方式,就是跑去別的網站找答案,限制網域等於幫它畫好邊界。


三、Tools 自訂動作:把關鍵步驟收回來自己寫

內建動作就是點擊、輸入、捲動這些,不想讓 AI 自己摸索的步驟,就自己寫:

from browser_use import Tools, ActionResult, BrowserSession

tools = Tools()

@tools.action(description="用測試帳號登入後台")
async def login_with_test_account(browser_session: BrowserSession):
    page = await browser_session.must_get_current_page()

    account = await page.get_elements_by_css_selector("#account")
    password = await page.get_elements_by_css_selector("#password")
    submit = await page.get_elements_by_css_selector("button[type='submit']")

    await account[0].type("qa_test_user")
    await password[0].type("qa_test_password")
    await submit[0].click()

    return ActionResult(extracted_content="已用測試帳號登入")

agent = Agent(task="登入後台,確認首頁有顯示使用者名稱", llm=llm, tools=tools)
  • description 一定要寫清楚:AI 靠這段決定「什麼時候該用這個動作」
  • 回傳 ActionResult:內容會寫進執行紀錄,AI 也會讀到

把 AI 的判斷留給真的需要判斷的地方。


四、Error Handling:出錯時它會怎麼處理

出錯的地方 它會怎麼做 用什麼控制
動作失敗(找不到元素、點不到) 換個方式再試(自我修復) max_failures
LLM API 出錯(429 流量限制、逾時) 自動重試,不行就切換備援模型 fallback_llm
單一步驟卡太久 直接中斷這一步 step_timeout(預設 180 秒)
agent = Agent(
    task="...",
    llm=ChatOpenAI(model="gpt-4.1-mini"),
    fallback_llm=ChatOpenAI(model="gpt-4.1"),
    max_failures=3,
    step_timeout=60,
)

批次跑幾十支測試時,沒設 fallback_llm,一支撞到 429,後面整批跟著全紅,看起來像系統壞了,其實是 API 被限流。

自訂動作的錯誤:該停就停

金額算錯這種錯誤,AI 自我修復的結果可能是繞過去、走完流程,最後還回報成功。

這種時候,直接在自訂動作裡結束整個任務:

EXPECTED_TOTAL = "1,280"

@tools.action(description="檢查購物車總金額是否正確")
async def check_cart_total(browser_session: BrowserSession):
    page = await browser_session.must_get_current_page()
    elements = await page.get_elements_by_css_selector(".cart-total")
    actual = await elements[0].get_text()

    if actual != EXPECTED_TOTAL:
        return ActionResult(
            extracted_content=f"金額錯誤:預期 {EXPECTED_TOTAL},實際 {actual}",
            is_done=True,
            success=False,
        )

    return ActionResult(extracted_content="金額正確")

is_done=True 讓 Agent 立刻停下,success=False 明確標記失敗。

自我修復適合用在「怎麼走到那裡」,不適合用在「結果對不對」。


五、敏感資料:帳密不要送進 LLM

帳密如果寫在 task 裡,會整段送到 LLM 的 API 上,改用 sensitive_data:

sensitive_data = {
    "https://*.example-staging.com": {
        "x_user": "qa_test_user",
        "x_pass": "qa_test_password",
    },
}

agent = Agent(
    task="用 x_user 和 x_pass 登入 example-staging.com",
    sensitive_data=sensitive_data,
    use_vision=False,
    llm=llm,
)

LLM 只看得到 x_user 這種代號,真正的值會直接填進表單,而且只在綁定的網域才會填入。

use_vision 關掉,是因為截圖可能會拍到填進去的帳密。


六、結果輸出與證據:失敗能回頭查,成功能自己驗證

失敗時想回頭查,記得開 save_conversation_path(AI 的判斷過程)跟 record_video_dir(執行影片)。

save_conversation_path 是唯一能看到「AI 為什麼這樣做」的東西,一定要開。

agent.run() 回傳的結果,常用的方法:

history = await agent.run(max_steps=20)

history.action_names()     # 實際做了哪些動作
history.model_thoughts()   # 每一步的思考過程
history.errors()           # 每一步的錯誤
history.final_result()     # 最後的回答
history.is_successful()    # AI 自己判斷有沒有成功

⚠️ is_successful() 是 AI 自己說的

官方文件原文:

"is_done() does not imply success. is_successful() reports the agent's own assessment; verify important external outcomes."

白話:is_successful() 只是 AI 對自己的評價,重要的結果要自己去驗。

做法是讓 AI 回傳固定格式的資料,再自己寫斷言:

from pydantic import BaseModel

class SearchResult(BaseModel):
    keyword: str
    result_count: int
    first_title: str

agent = Agent(
    task="搜尋「QA 三十天養成日記」,回傳第一筆結果的標題與結果數量",
    llm=llm,
    output_model_schema=SearchResult,
)

history = await agent.run()
result = SearchResult.model_validate_json(history.final_result())

assert result.result_count > 0
assert "QA 三十天養成日記" in result.first_title

判斷成功與否的權力,要拿回在我們自己手上。

Hooks:每一步都留紀錄

想在每一步結束時做點事(截圖、記網址、寫 log),可以用 hooks:

async def log_each_step(agent: Agent):
    url = await agent.browser_session.get_current_page_url()
    print(f"這一步結束,目前在:{url}")

await agent.run(on_step_end=log_each_step)

on_step_start 跟 on_step_end 兩個時機可以掛。


🧨 踩坑:網路上的範例,一半都跑不起來

Browser-use 改版很快,網路上的教學(包含我自己以前的筆記)很多都是舊寫法。

舊寫法 新寫法
Controller() Tools()(controller= 參數還能用,是 tools= 的別名)
BrowserProfile(...) / BrowserConfig(...) Browser(...)
from langchain_openai import ChatOpenAI from browser_use import ChatOpenAI
自訂動作參數寫 browser: Browser 要寫 browser_session: BrowserSession

官方文件特別警告:自訂動作的參數名稱是靠「名字」對應注入的,名字寫錯,動作會默默失效,不會報錯。

所以看到範例時,先對一下官方文件的版本,再動手貼。


參考來源


上一篇
[Day12] AI E2E - Browser-use 從零開始介紹與安裝
下一篇
[Day14] AI E2E - Browser-use 自動化穩定性與學習成本
系列文
測試的前提正在改變:AI 時代下 QA 的思維轉換 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言