使用 SDK 工具集進行瀏覽器與電腦操作
從 Python 或 TypeScript SDK 執行瀏覽器使用工具或電腦使用工具。SDK 會執行迴圈以及您設定的檢查,而您則提供瀏覽器或桌面環境。
透過 Python 和 TypeScript SDK 中的瀏覽器與電腦工具集,您可以建構在網頁瀏覽器或桌面上運作的代理程式。它可以使用沒有程式化介面的應用程式。透過瀏覽器使用工具,Claude 可以瀏覽、閱讀網頁並與之互動。透過電腦使用工具,Claude 可以透過螢幕截圖看到桌面,並使用滑鼠和鍵盤。
SDK 將 Claude 連接到您的瀏覽器或桌面。它會執行您設定的檢查,例如針對 navigate 的 URL 政策或核准回呼。它會將每個通過檢查的呼叫傳送給您的「driver」(驅動程式),並根據驅動程式傳回的內容建構 tool_result。工具執行器可以為您執行代理迴圈。
您需要提供驅動程式,由它來操作瀏覽器或桌面。請從供應商的驅動程式開始。使用供應商的驅動程式,只需幾行程式碼即可建立代理程式。如果沒有合適的驅動程式,請使用 Playwright 等瀏覽器自動化函式庫自行撰寫。
本頁大部分內容是關於瀏覽器工具集。電腦工具集說明了兩者的差異。
自行撰寫驅動程式屬於進階情境,請參閱實作驅動程式。claude-quickstarts 儲存庫中也有以 Python 和 TypeScript 撰寫的瀏覽器驅動程式範例。它透過 Chrome DevTools Protocol(CDP)控制 Chromium,且並非正式環境用的程式碼。
使用供應商的驅動程式
使用供應商提供的驅動程式(例如合作夥伴整合中列出的驅動程式),您只需幾行程式碼即可建構代理程式。此範例中的供應商是虛構的。請將 example_browser_provider 和 ExampleBrowser 替換為真實供應商的套件和驅動程式類別,並依照供應商文件的說明進行設定。
from anthropic import Anthropic
from example_browser_provider import ExampleBrowser # made-up provider
client = Anthropic()
with ExampleBrowser() as browser:
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser],
messages=[
{"role": "user", "content": "Open example.com and tell me its heading."}
],
)
for message in runner:
print(message)將驅動程式實例本身作為 tools 項目傳入。執行器永遠不會關閉工具集,因此一個實例可以服務多次執行。使用完畢後請自行關閉。
若要自行撰寫驅動程式,請參閱實作驅動程式。
合作夥伴整合
Browser Use、Browserbase、Daytona 和 E2B 為 SDK 工具集發布了各自的驅動程式。
適用於瀏覽器工具集的 Browser Use 驅動程式。
適用於瀏覽器工具集的 Browserbase Stagehand 驅動程式。
適用於電腦工具集的 Daytona 驅動程式。
適用於瀏覽器和電腦工具集的 E2B 驅動程式。
我們的合作夥伴也在 GitHub 上發布了程式碼:Browser Use 快速入門、Browserbase 工具集,以及 E2B 範例。
自訂驅動程式
本節中的範例使用 MyBrowser,即本頁稍後進階範例中的驅動程式。
啟用或停用成員
configs 接受設定工具集中所述的各成員設定。只需列出您要變更的成員:
# 同時實作 read_console 的 MyBrowser
browser = MyBrowser(
backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)SDK 會在您的程式碼執行之前拒絕對已停用成員的呼叫。啟用您的類別未實作的成員屬於設定錯誤,除非該類別覆寫了 execute。
新增前置與後置 hook
覆寫 execute 並呼叫父類別的 execute。該呼叫之前的程式碼會在 URL 政策、檔案政策和 confirm 之後執行,並且可以變更輸入。SDK 不會再次檢查變更後的輸入。該呼叫之後的程式碼會收到結果,並且可以變更結果。引發 ToolError(在 TypeScript 中則是拋出它)即可拒絕該呼叫。
覆寫 execute 會改變提供給 Claude 的成員。SDK 會將每個成員都視為已實作,因此 Claude 會獲得所有預設開啟的成員。進階範例中的 MyBrowser 只服務三個成員,因此下列 TracedBrowser 會向 Claude 提供它無法服務的成員。使用前請先透過 configs 關閉這些成員。
import time
class TracedBrowser(MyBrowser):
def execute(self, context, name, input):
started = time.monotonic()
result = super().execute(context, name, input)
elapsed_ms = (time.monotonic() - started) * 1000
call_id = context.tool_use.id if context.tool_use else "-"
log.info("%s %s %.0fms", call_id, name, elapsed_ms)
return redact(result) if name == "get_page_text" else result實作驅動程式
驅動程式是您對 BetaAbstractBrowserToolset20260801 的子類別。在其中覆寫您的瀏覽器所支援的成員。您未實作的成員會以停用狀態傳送給 API。如果 Claude 仍然呼叫它,SDK 會傳回錯誤,而執行會繼續。覆寫 execute 會改變哪些成員以停用狀態傳送(新增前置與後置 hook)。每個成員都會收到呼叫上下文,以及以型別物件表示的成員輸入,例如 BetaBrowserNavigateInput。輸入型別來自 anthropic.types.beta。成員工具列出了每個輸入的欄位。
在 TypeScript 中,請將成員撰寫為方法,而非箭頭函式欄位,因為 SDK 是在 prototype 上尋找它們。在 TypeScript 中,type 成員要拼寫為 type_。在 Python 中則為 type。
傳回結果
成員傳回的內容決定了 Claude 讀取的內容。成功的結果會以根據您的狀態報告所建構的 browser_state 區塊結尾。錯誤結果則不帶有此區塊。
| 成員 | 傳回 | Claude 讀取 |
|---|---|---|
screenshot、zoom | BetaScreenshotResult | 一個圖片區塊 |
navigate | BetaBrowserNavigateResult | Navigated to {url} — {title} (HTTP {status}) |
new_tab、switch_tab、list_tabs、close_tab | 一個分頁項目(new_tab、switch_tab)、分頁項目清單(list_tabs),或不傳回任何內容(close_tab) | 僅 browser_state 區塊 |
read_page、get_page_text、find、read_console、read_network、javascript_exec | 一個字串 | 該字串,若為空字串則為 (empty) |
| 其他所有成員 | 不傳回任何內容,或一行文字 | 簡短的確認訊息(例如 Clicked.),接著是位於獨立文字區塊中的傳回行 |
回報瀏覽器狀態
SDK 會在每個回傳結果的呼叫之後呼叫 _browser_state,包括被拒絕和失敗的呼叫。請回傳每個開啟的分頁,以及自上次報告以來的變更:
- 已開啟的分頁和下載事件。
- 針對您的請求掛鉤所封鎖的每次導覽,回傳一個
BetaNavigationRefused。 - 針對您的驅動程式所關閉的每個原生對話框,回傳一個
BetaDialogDismissed。
將這些全部放入 state_changes。在 Python 中,BetaNavigationRefused() 和 BetaDialogDismissed(kind=..., message=...) 來自 anthropic.tools.browser。在 TypeScript 中,它們是 { type: "navigation_refused" } 和 { type: "dialog_dismissed", kind, message }。
後兩者並非 API 狀態變更。SDK 會以 browser_state 區塊之外的文字將它們回報給 Claude:所有被拒絕的導覽合併為一行(不包含 URL),前三個被關閉的對話框各一行,其餘的則以數量表示。
當有任何分頁開啟時,必須恰好有一個分頁處於作用中狀態。每個接受 tab_id 的成員都必須作用於其指定的分頁。API 對報告的限制列於使用 browser_state 追蹤分頁。
Claude 會讀取每個分頁的 URL、每個下載的 URL,以及您的 navigate 回傳的 URL,內容與您的驅動程式所寫的一致。SDK 不會解析這些 URL。它會將換行符號和其他控制字元轉換為空格、修剪頭尾,並將每個 URL 截斷為 4,096 個字元。因此,data: URL 的內容、file: URL 的路徑,以及 URL 中的使用者名稱或密碼,都會傳達給 Claude。
處理錯誤
| 由成員或 SDK 引發 | Claude 讀取 | 執行 |
|---|---|---|
ToolError | 其訊息,作為錯誤結果 | 繼續 |
ToolsetUsageError | 無 | 停止 |
| 任何其他例外 | 在 Python 中為 ClassName: message,在 TypeScript 中為 Error: message,作為錯誤結果 | 繼續 |
SDK 會在發生設定錯誤、呼叫期間誤用 SDK,或在 close 之後進行呼叫時引發 ToolsetUsageError。當 _browser_state 引發任何例外(即使是 ToolError)時,SDK 也會引發此錯誤。工具執行器和 tool_result 不會捕捉它,因此它會傳達到它們的呼叫者。
SDK 不會隱藏成員錯誤文字中、left_click 等動作回傳的那行文字中、失敗下載的錯誤中,或被關閉對話框訊息中的本機路徑。
來自您的 URL 政策、檔案政策或 confirm 可呼叫物件的 ToolError 會原封不動地傳達給 Claude,因此請勿在其文字中包含本機路徑。請在您的成員中捕捉例外,並以您自己的文字引發 ToolError(在 TypeScript 中則是拋出它)。
在回應串流時啟動呼叫
預設情況下,工具執行器會在 Claude 的回應結束後才執行該回合的呼叫。使用「early start」(提前啟動)時,它可以在回應仍在串流時就啟動瀏覽器或電腦呼叫。
若要啟用此功能,請將 stream=True 和 run_tools_eagerly=True 傳給工具執行器,如進階範例所示。使用 stream 時,您對執行器的迴圈每次迭代都會得到一個串流,而非一則訊息。
提前啟動改變的是呼叫可以開始的時間點,而非工具集執行呼叫的方式:
- 檢查仍會先執行: SDK 會在呼叫執行之前呼叫您的 URL 政策、檔案政策和
confirm。使用提前啟動時,它可能會在回應仍在串流時呼叫confirm。 - 呼叫仍會逐一執行: 工具集會依照 Claude 撰寫的順序執行其呼叫。失敗的呼叫仍會停止該工具集在該回合中的後續呼叫。
- 已啟動的呼叫無法撤回: 如果回應隨後被截斷(例如達到
max_tokens),或您的迴圈提前停止,該動作仍會發生,而 Claude 永遠不會讀到其結果。
不使用工具執行器執行
在 tools 中傳入實例(在 TypeScript 中為 browser.toJSON()),並以 tool_result 回應每個成員呼叫。瀏覽器使用工具要求您在第一個失敗的呼叫處停止(批次動作)。
此迴圈使用進階範例中的 MyBrowser 和 URL 政策。在呼叫失敗後,它會回應該回合後續的呼叫而不執行它們:
from anthropic.types.beta import BetaMessageParam, BetaToolResultBlockParam
NOT_EXECUTED = "Not executed: an earlier action in this turn failed."
MAX_TURNS = 10
with MyBrowser(backend, url_policy=url_policy) as browser:
messages: list[BetaMessageParam] = [{"role": "user", "content": "Open example.com"}]
for _ in range(MAX_TURNS):
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser],
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
calls = [
block
for block in response.content
if block.type == "tool_use" and block.toolset_name == browser.toolset_name
]
if not calls:
break
results: list[BetaToolResultBlockParam] = []
failed = False
for call in calls:
if failed:
# 呼叫失敗後,該輪其餘部分僅會回覆,而不會執行。
results.append(
{
"type": "tool_result",
"tool_use_id": call.id,
"toolset_name": call.toolset_name,
"content": NOT_EXECUTED,
"is_error": True,
}
)
continue
result = browser.tool_result(call)
failed = bool(result.get("is_error"))
results.append(result)
messages.append({"role": "user", "content": results})對每個被略過呼叫的回應都帶有 is_error、該呼叫的 toolset_name,以及批次動作所要求的確切文字。工具執行器也會傳送相同的回應。
進階範例:自行撰寫驅動程式
此範例將實作驅動程式的主要部分整合到一個驅動程式 MyBrowser 中,並搭配工具執行器和提前啟動來執行。MyBrowser 實作了 navigate、screenshot 和 left_click,以及每個驅動程式都需要的狀態報告 _browser_state。
backend 代表您自己對瀏覽器自動化函式庫(例如 Playwright)的包裝。URL 政策僅為範例。撰寫 URL 政策會加以說明。
import re
from urllib.parse import urlsplit
from anthropic import Anthropic
from anthropic.tools import ToolError
from anthropic.tools.browser import (
BetaAbstractBrowserToolset20260801,
BetaBrowserNavigateResult,
BetaBrowserState,
BetaScreenshotResult,
BetaToolsetCallContext,
BetaURLContext,
)
from anthropic.types.beta import (
BetaBrowserLeftClickInput,
BetaBrowserNavigateInput,
BetaBrowserScreenshotInput,
BetaBrowserStateTabEntryParam,
)
class MyBrowser(BetaAbstractBrowserToolset20260801):
def __init__(self, backend, **options):
super().__init__(**options)
self.backend = backend
def _browser_state(self, context: BetaToolsetCallContext) -> BetaBrowserState:
return BetaBrowserState(
tabs=[
BetaBrowserStateTabEntryParam(
tab_id=tab.id,
title=tab.title,
url=tab.url,
active=tab.id == self.backend.active,
)
for tab in self.backend.tabs()
],
state_changes=self.backend.drain_changes(),
)
def navigate(
self, context: BetaToolsetCallContext, input: BetaBrowserNavigateInput
) -> BetaBrowserNavigateResult:
# input.url 為 "back"、"forward"、"reload",或是您的 URL 政策
# 所允許、由 Claude 撰寫的 URL。與 is_allowed 相同,backend.goto 會為
# 不以 scheme 開頭的 URL 加上 https://。
page = self.backend.goto(input.url, input.tab_id)
return BetaBrowserNavigateResult(
url=page.url, status=page.status, title=page.title
)
def screenshot(
self, context: BetaToolsetCallContext, input: BetaBrowserScreenshotInput
) -> BetaScreenshotResult:
data = self.backend.png_base64(input.tab_id)
return BetaScreenshotResult(data=data, media_type="image/png")
def left_click(
self, context: BetaToolsetCallContext, input: BetaBrowserLeftClickInput
) -> None:
# 無需回傳任何內容:Claude 會讀到 "Clicked."
self.backend.click(input.target, input.tab_id)
def close(self) -> None:
super().close() # first, so no call is still using the browser when it closes
if not self.backend.closed:
self.backend.close()
ALLOWED_HOSTS = ("example.com", "iana.org")
SCHEME_PREFIX = re.compile(r"[a-z][a-z0-9+.-]*:", re.IGNORECASE)
def is_allowed(url: str) -> bool:
# 此為範例,並非正式環境政策。請參閱「Write a URL policy」。
if url.lower() == "about:blank":
return True
with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
# 瀏覽器會將網頁 URL 中的 "\" 視為 "/"。
try:
parts = urlsplit(with_scheme.replace("\\", "/"))
except ValueError:
return False
host = parts.hostname or ""
listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
return parts.scheme in ("http", "https") and listed
def url_policy(context: BetaURLContext, url: str) -> None:
if not is_allowed(url):
raise ToolError(f"blocked: {url} is not on an allowed host")
client = Anthropic()
with MyBrowser(backend, url_policy=url_policy) as browser:
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser],
messages=[
{
"role": "user",
"content": "Open example.com and tell me the page heading.",
}
],
stream=True,
run_tools_eagerly=True, # so a call can start before the response ends
)
for stream in runner:
print(stream.get_final_message())安全地執行工具集
Claude 的下一個動作取決於它所讀取的頁面。頁面或注入其中的文字可能會嘗試存取內部服務,或從主機取出檔案。它也可能嘗試觸發具有實際影響的動作。在您對一次性瀏覽器以外的任何瀏覽器執行驅動程式之前,請採取以下六個步驟:
- 撰寫 URL 政策,拒絕任務不需要的每個
navigateURL。 - 在驅動程式中攔截請求,並以相同的規則檢查每個請求。
- 在容器上設定出口政策,讓網路封鎖驅動程式看不到的內容。
- 限制上傳和下載,或保持上傳關閉。
- 使用
confirm管控具重大影響的成員。 - 為每個工作階段在專用的容器或 VM 中隔離瀏覽器主機。
SDK 會透過您傳入的政策和可呼叫物件來套用步驟 1、4 和 5。步驟 2、3 和 6 則取決於您的驅動程式和部署。安全性考量中的預防措施同樣適用。
撰寫 URL 政策
「URL policy」(URL 政策)是您以 url_policy 傳入的函式。政策由您撰寫,您也有責任讓它達到正式環境的水準。在您的 navigate 執行之前,SDK 會以呼叫的上下文和 Claude 所寫的 URL 呼叫該政策。
不回傳任何內容即表示允許該 URL。若要拒絕,請引發 ToolError(在 TypeScript 中則是拋出它)。Claude 會讀取錯誤訊息,而 navigate 不會執行。此政策只檢查每個 navigate 呼叫中的 URL,它並非網路控制機制。
這是進階範例中的政策。它允許 about:blank,以及透過 http 和 https 存取兩個網站及其子網域:
import re
from urllib.parse import urlsplit
from anthropic.tools import ToolError
from anthropic.tools.browser import BetaURLContext
ALLOWED_HOSTS = ("example.com", "iana.org")
SCHEME_PREFIX = re.compile(r"[a-z][a-z0-9+.-]*:", re.IGNORECASE)
def is_allowed(url: str) -> bool:
# 僅為範例,並非正式環境的政策。
if url.lower() == "about:blank":
return True
with_scheme = url if SCHEME_PREFIX.match(url) else f"https://{url}"
# 瀏覽器會將網頁 URL 中的 "\" 視為 "/"。
try:
parts = urlsplit(with_scheme.replace("\\", "/"))
except ValueError:
return False
host = parts.hostname or ""
listed = any(host == name or host.endswith(f".{name}") for name in ALLOWED_HOSTS)
return parts.scheme in ("http", "https") and listed
def url_policy(context: BetaURLContext, url: str) -> None:
if not is_allowed(url):
raise ToolError(f"blocked: {url} is not on an allowed host")
browser = MyBrowser(backend, url_policy=url_policy)SDK 只會針對 navigate 呼叫此政策,而不會針對 "back"、"forward" 或 "reload" 呼叫。Claude 可能會省略 scheme,例如 example.com,因此範例在讀取主機之前,會為不以 scheme 開頭的 URL 加上 https://。您的 navigate 收到的是 Claude 所寫的 URL。請在那裡以相同的判斷加上 scheme,讓瀏覽器開啟的正是您的政策所檢查的 URL。
此範例並非正式環境等級。正式環境的政策需要更多處理,涵蓋以下等領域:
- Claude 點擊的連結、提交的表單、重新導向,以及頁面自行發出的請求。只有您的驅動程式看得到這些。請參閱在驅動程式中攔截請求。
- 來自頁面內容的主機名稱和 URL,攻擊者可以選擇這些內容。
- 以瀏覽器的方式解析每個 URL,而非以單純的字串分割來找出主機名稱。
- URL scheme。SDK 不會檢查它們,因此請拒絕您的驅動程式不打算開啟的每個 scheme。
- 容器上的出口規則,用於處理任何 URL 檢查都看不到的部分。請參閱在容器上設定出口政策。
如果您傳入 url_policy=None,SDK 會拒絕每個導向 URL 的 navigate,因此從您的設定中讀取到的 None 不會意外地讓 URL 未經檢查。在 TypeScript 中,undefined 會讓該選項保持未設定,與不傳入它相同。
在驅動程式中攔截請求
URL 政策只判斷 navigate 呼叫中的 URL。Claude 點擊的連結、提交的表單、重新導向,或已允許頁面上的圖片或指令碼,仍可能存取政策會拒絕的主機。請使用出口規則封鎖這些主機。
具備請求攔截功能的驅動程式也可以在其請求掛鉤中檢查這些請求,請求掛鉤是您的自動化函式庫在每個請求之前呼叫的函式。呼叫您的政策所使用的函式,這樣規則只需撰寫一次:
class MyBrowser(BetaAbstractBrowserToolset20260801):
...
# 透過 context.route("**/*", self._guard) 註冊於 Playwright 瀏覽器 context 上
def _guard(self, route):
# is_allowed 是「Write a URL policy」中的函式
if not is_allowed(route.request.url):
return route.abort("blockedbyclient")
route.continue_()像這樣的請求掛鉤看不到 WebSocket 交握、service worker 請求或重新導向的中繼跳轉。請封鎖 service worker。使用能看到 WebSocket 交握和重新導向跳轉的掛鉤來檢查它們。範例政策會拒絕 ws:// 和 wss:// URL,因此在將 WebSocket URL 傳給 is_allowed 之前,請將 ws:// 改為 http://,並將 wss:// 改為 https://。
在容器上設定出口政策
攔截無法看到瀏覽器發出的每個請求,而 URL 政策也看不到名稱解析到的位置。由容器網路強制執行的「egress policy」(出口政策)可以同時涵蓋這兩者:
- 封鎖 IPv4 和 IPv6 中的鏈路本機和私有位址範圍,其中包括雲端中繼資料位址
169.254.169.254。 - 只允許對任務所需主機的對外連線。如果您的規則比對的是 IP 位址,請在容器啟動時解析您允許的主機名稱。
- 只允許對容器解析器的 DNS。
在瀏覽器容器外部強制執行的規則(例如 Kubernetes NetworkPolicy 或雲端防火牆)看不到容器內部的 loopback。若只有這些規則,頁面就能存取在那裡監聽的任何服務,包括 DevTools 連接埠,該連接埠會顯示瀏覽器開啟的分頁清單以及控制每個分頁的位址。
限制上傳和下載
file_upload 預設為關閉。若沒有 file_policy,SDK 會拒絕每個提供路徑或文件 ID 的上傳。若要啟用上傳,請傳入一個 BetaLocalFilePolicy,並指定一個只存放任務檔案的上傳目錄:
from anthropic.tools.browser import BetaLocalFilePolicy
# 同時實作 file_upload 的 MyBrowser
browser = MyBrowser(
backend,
configs={"file_upload": {"enabled": True}},
confirm=make_confirm(), # required for file_upload; see Gate consequential members
url_policy=url_policy,
file_policy=BetaLocalFilePolicy(
upload_roots=["/task/uploads"],
download_dir="/task/downloads",
expose_download_paths=False,
),
)SDK 會解析每個上傳路徑(包括追蹤符號連結),並拒絕上傳根目錄之外的任何路徑。若下載目錄本身是上傳根目錄、位於上傳根目錄內,或包含上傳根目錄,檔案政策會引發設定錯誤。此檢查可能會漏掉符號連結,因此請勿讓符號連結使下載目錄與上傳根目錄相同,或使其中一個位於另一個之內。只有當 expose_download_paths 為 true 且檔案位於下載目錄內時,SDK 才會在狀態報告中包含下載的 path。
內建的檔案政策會在執行 SDK 的程序所在的檔案系統上檢查路徑。它只能保護與該檔案系統共用的瀏覽器。對於遠端瀏覽器,請改為遵循遠端與託管瀏覽器。
請以下列方式設定下載:
- 自行建立模式為
0700的下載目錄,並以noexec,nosuid,nodev掛載。 - 讓該目錄無法被 Claude 可呼叫的其他工具(例如 shell 或檔案工具)存取。
- 在
download_failed狀態變更中,將error寫成固定的措辭。例外的文字可能包含路徑或 URL。 - 在有人做出決定之前,不要將下載的檔案讀入對話中,也不要執行它。
管控具重大影響的成員
javascript_exec 和 file_upload 預設為關閉。如果您在沒有 confirm 可呼叫物件的情況下啟用其中任何一個,建構函式會引發設定錯誤。有 confirm 可呼叫物件時,SDK 會在每個即將執行的呼叫之前呼叫它。沒有的話,則不會詢問任何事。在 TypeScript 中,confirm: null 不會引發設定錯誤,而 SDK 會拒絕對任何成員的每個呼叫。
回傳 True 以執行呼叫,或回傳 False 以拒絕呼叫。當您的可呼叫物件詢問某人時,請向對方顯示成員、頁面的 URL 和呼叫的輸入。首先,請跳脫 URL 和輸入中所有可列印 ASCII 以外的字元,因為兩者都可能夾帶來自頁面的文字。
此範例透過您自己的 ask_user 函式詢問兩個受管控的成員,並核准其餘成員:
import json
from collections.abc import Callable
from urllib.parse import urlsplit
from anthropic.tools.browser import BetaConfirmContext
GATED = {"javascript_exec", "file_upload"}
def shown(value: object) -> str:
"""A value as JSON, with every character outside printable ASCII escaped."""
return json.dumps(value, ensure_ascii=True, indent=2)
def make_confirm() -> Callable[[BetaConfirmContext], bool]:
granted: set[tuple[str, str, str]] = set()
def confirm(context: BetaConfirmContext) -> bool:
name = context.member
if name not in GATED:
return True
detail = shown(context.input.to_dict())
page = context.tab_url
where = shown(page) if page else "a tab with no URL"
question = f"Allow {name} on {where}?\n{detail}"
if page is None or urlsplit(page).scheme not in ("http", "https"):
return ask_user(question) # no web origin: ask every time
# 核准僅適用於此頁面上的這個確切輸入。
key = (name, page, detail)
if key not in granted and ask_user(question):
granted.add(key)
return key in granted
return confirm
# 同時實作 javascript_exec 與 file_upload 的 MyBrowser
# file_upload 也需要「Confine uploads and downloads」中的 file_policy
browser = MyBrowser(
backend,
configs={"javascript_exec": {"enabled": True}, "file_upload": {"enabled": True}},
confirm=make_confirm(),
url_policy=url_policy,
)每次呼叫 make_confirm() 都會回傳一個沒有任何核准紀錄的可呼叫物件。請為每個工具集呼叫一次,並為每位使用者提供各自的工具集。
核准涵蓋的是上次狀態報告所顯示的頁面,而頁面可能在呼叫執行之前發生變化。購買、傳送訊息和接受條款都是透過 left_click 和 type 等一般成員進行的,因此 confirm 無法依名稱將它們挑出。若要讓某人核准這些動作,也請針對這些成員進行詢問。
除非出口規則將瀏覽器的對外連線限制在任務所需的主機,否則請勿啟用 javascript_exec。指令碼可以將頁面內容傳送到瀏覽器能存取的任何位址。
navigate 呼叫中的 javascript: URL 也會在目前頁面中執行指令碼,即使 javascript_exec 已關閉也一樣。confirm 會將該呼叫視為 navigate,因此範例中的 confirm 可呼叫物件會核准它。您的驅動程式必須拒絕 javascript: URL。在範例中,URL 政策會在 confirm 執行之前拒絕它們。
隔離瀏覽器主機
為每個工作階段在專用、最低權限的容器或 VM 中執行瀏覽器:
- 以非 root 使用者身分執行,並在瀏覽器允許的情況下使用唯讀的根檔案系統。
- 除了您所設定的上傳和下載目錄(如果有的話)之外,不要從主機掛載任何內容。
- 讓環境中不含憑證,並從全新的瀏覽器設定檔開始。
- 不與 Claude 可呼叫的其他工具共用任何檔案系統。
請在瀏覽器容器之外執行呼叫 API 的程式碼,因為該程式碼持有您的 API 金鑰和對話內容。工具執行器和 tool_result 都會在該程式碼的程序中執行工具集,因此瀏覽器不會與工具集共用檔案系統。請將瀏覽器視為遠端:遠端與託管瀏覽器適用於此情況。
請將頁面傳回的所有內容視為不受信任,包括頁面文字、螢幕截圖、主控台和網路項目、分頁標題以及下載名稱。
遠端與託管瀏覽器
有些瀏覽器不會與執行 SDK 的程序共用檔案系統。例如位於另一個容器中的瀏覽器、您透過 DevTools URL 存取的瀏覽器,以及來自託管瀏覽器服務的瀏覽器。對於這些瀏覽器,URL 政策、請求攔截和 confirm 仍會在您的程序中執行。
使用託管瀏覽器時,由供應商控制出口和主機隔離。您自己的出口政策在那裡並不適用,因此驅動程式的請求 hook 是您對瀏覽器請求的唯一檢查。請了解瀏覽器的網路可以存取哪些位置。
內建的路徑檢查無法保護遠端瀏覽器。BetaLocalFilePolicy 會在執行 SDK 的程序所在的檔案系統上檢查路徑,而瀏覽器讀寫的是它自己的檔案系統。
SDK 無法偵測瀏覽器是否為遠端。因此對於遠端瀏覽器,請保持 file_upload 關閉,除非您的驅動程式使用自己的 BetaFilePolicy,在瀏覽器執行的位置檢查上傳路徑。BetaFilePolicy 會審查每個上傳的路徑和文件 ID,並決定 Claude 是否能看到下載的路徑。
請讓遠端瀏覽器拒絕下載,除非其本身的主機具備限制上傳和下載中的下載設定。
請勿讓供應商的 API 金鑰和工作階段的連線 URL(其中可能包含金鑰)出現在日誌、工具結果和錯誤文字中。如果供應商會錄製工作階段,該錄製內容就是 Claude 所看到和輸入的所有內容的另一份副本,且供應商的保留條款適用於它。
電腦工具集
BetaAbstractComputerToolset20260801 是用於電腦使用工具的類別。驅動程式是一個子類別,每個工具對應一個方法,例如 screenshot 或 left_click。您可以從合作夥伴整合下的合作夥伴驅動程式開始。若要自行撰寫,claude-quickstarts 儲存庫提供了以 Python 和 TypeScript 撰寫的最小 VNC 範例。它透過 VNC 控制桌面,並非正式環境用的程式碼。
此類別接受 configs、confirm 和 tool_configs,傳入方式與瀏覽器類別相同。自訂驅動程式、處理錯誤、在回應串流時啟動呼叫和不使用工具執行器執行也適用於它,但有以下差異:
- 僅限瀏覽器的選項: 此類別沒有 URL 政策、沒有檔案政策,也沒有狀態報告。
configs: 您實作的每個工具預設都是開啟的。各工具的設定列於工具參數。confirm: 如果您的類別實作了type、key或hold_key,除非您傳入confirm可呼叫物件或使用configs關閉這些工具,否則建構函式會引發設定錯誤。覆寫execute的類別會被視為實作了這三者。- 被略過的呼叫: 在電腦呼叫失敗後,工具執行器會以
Not executed: an earlier computer action in this turn failed.回應該回合的後續電腦呼叫。瀏覽器呼叫則會收到不同的文字。在您自行撰寫的迴圈中,請以該確切文字回應被略過的電腦呼叫(批次動作)。 execute: 在 TypeScript 中,execute覆寫會將name的型別設為BetaComputerMemberName,並將input的型別設為BetaComputerMemberInput。兩者都來自@anthropic-ai/sdk/resources/beta。- 錯誤文字: SDK 不會隱藏電腦工具錯誤文字中或工具回傳文字中的本機路徑。Claude 會讀取兩者,並截斷為 4,096 個字元。請在您的工具中捕捉例外,並以您自己的文字引發
ToolError(在 TypeScript 中則是拋出它)。
撰寫桌面驅動程式
此驅動程式實作了可用動作中所列 17 個工具中的五個。您未實作的工具會以停用狀態傳送至 API,如實作驅動程式所述。在範例中,display 代表您自己對任何控制桌面之工具(例如 VNC 用戶端或 xdotool)的包裝,而 ask_user 代表您自己用來詢問使用者的函式。
from anthropic import Anthropic
from anthropic.tools.computer import (
BetaAbstractComputerToolset20260801,
BetaComputerCursorPositionResult,
BetaScreenshotResult,
BetaToolsetCallContext,
)
from anthropic.types.beta import (
BetaComputerCursorPositionInput,
BetaComputerKeyInput,
BetaComputerLeftClickInput,
BetaComputerScreenshotInput,
BetaComputerTypeInput,
)
class MyDesktop(BetaAbstractComputerToolset20260801):
def __init__(self, display, **options):
super().__init__(**options)
self.display = display
def screenshot(
self, context: BetaToolsetCallContext, input: BetaComputerScreenshotInput
) -> BetaScreenshotResult:
# Claude 的座標是此影像中的像素位置。
data = self.display.png_base64()
return BetaScreenshotResult(data=data, media_type="image/png")
def cursor_position(
self, context: BetaToolsetCallContext, input: BetaComputerCursorPositionInput
) -> BetaComputerCursorPositionResult:
x, y = self.display.cursor()
return BetaComputerCursorPositionResult(x=x, y=y)
def left_click(
self, context: BetaToolsetCallContext, input: BetaComputerLeftClickInput
) -> None:
# input.coordinate 為 [x, y],或為 None 表示在游標處點擊。input.text 是
# 要按住的修飾鍵,例如 "shift"。無需回傳:Claude 會讀到 "Clicked."
self.display.click(input.coordinate, input.text)
def type(
self, context: BetaToolsetCallContext, input: BetaComputerTypeInput
) -> None:
self.display.type(input.text)
def key(self, context: BetaToolsetCallContext, input: BetaComputerKeyInput) -> None:
self.display.press(input.text, input.repeat or 1)
def close(self) -> None:
super().close() # first, so no call is still using the desktop when it closes
self.display.close()
client = Anthropic()
with MyDesktop(
display,
# type 與 key 必填;請參閱「安全地執行 computer 工具集」
confirm=lambda context: ask_user(f'Allow the "{context.member}" tool?'),
) as desktop:
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[desktop],
messages=[
{"role": "user", "content": "Open the calculator and compute 17 * 23."}
],
)
for message in runner:
print(message)有四個工具沒有輸入欄位:screenshot、cursor_position、left_mouse_down 和 left_mouse_up。每個輸入型別(包括它們的)都來自 anthropic.types.beta。
在 TypeScript 中,請將工具寫成方法,而非箭頭函式欄位。實作驅動程式說明了原因。
縮放座標並調整圖片大小
Claude 傳送的每個點都是您所回傳螢幕截圖中的像素位置:每個 coordinate 和 start_coordinate,以及 zoom region 的兩個角。SDK 會將它們原封不動地傳遞給您的工具。
如果您的螢幕截圖與顯示器的大小不同,請在您的驅動程式中進行雙向轉換:將 Claude 的點轉換為顯示器像素,並將您回報的游標位置轉換為螢幕截圖像素。在整個工作階段中保持螢幕截圖大小不變,並使其具有與顯示器相同的長寬比:
from anthropic.tools import ToolError
SCREENSHOT_SIZE = (1280, 720) # the size of every full screenshot the driver returns
DISPLAY_SIZE = (2560, 1440)
def to_display(coordinate: list[int]) -> tuple[int, int]:
x, y = coordinate
width, height = SCREENSHOT_SIZE
if not (0 <= x < width and 0 <= y < height):
# 拒絕此座標點。若將其移入範圍內,會點擊到 Claude 未選擇的位置。
raise ToolError(f"[{x}, {y}] is outside the {width}x{height} screenshot")
return x * DISPLAY_SIZE[0] // width, y * DISPLAY_SIZE[1] // height
def to_screenshot(x: int, y: int) -> tuple[int, int]:
width, height = DISPLAY_SIZE
return x * SCREENSHOT_SIZE[0] // width, y * SCREENSHOT_SIZE[1] // height
to_display([640, 360]) # (1280, 720)在對每個 coordinate 和 start_coordinate 採取動作之前,先對其呼叫 to_display,並對 cursor_position 回報的位置呼叫 to_screenshot。沒有 coordinate 的點擊或捲動會在游標處執行,因此沒有需要轉換的內容。以相同的比例縮放 zoom region。zoom 圖片不會改變這一點:Claude 接下來傳送的點仍然是完整螢幕截圖中的像素位置。
SDK 從不讀取或調整圖片大小。API 會拒絕超出模型限制的圖片,這會結束執行。請在驅動程式中縮放每張螢幕截圖和 zoom 圖片。請參閱調整螢幕截圖大小以符合圖片限制。在長時間執行中,一個請求可能包含超過 20 張圖片,此時 API 會對每張圖片套用更嚴格的限制。請參閱管理螢幕截圖歷史記錄。
輸入檢查與結果
不要依賴 SDK 來檢查呼叫的輸入。讓您的驅動程式檢查它所使用的欄位,並拒絕它無法執行的呼叫:引發 ToolError(在 TypeScript 中則是拋出它)。Claude 會讀取該訊息,並可以傳送修正後的呼叫。
兩個 SDK 都不會限制 duration、repeat 次數或 scroll_amount。安全地執行電腦工具集下的步驟 4 涵蓋了這些內容。
工具回傳的內容決定了 Claude 讀取的內容:
| 工具 | 回傳 | Claude 讀取 |
|---|---|---|
screenshot、zoom | BetaScreenshotResult | 一個圖片區塊 |
cursor_position | BetaComputerCursorPositionResult | X={x},Y={y} |
| 其他所有工具 | 無,或一行文字 | 簡短的確認訊息,例如 Clicked.,接著是回傳的那一行,位於其自己的文字區塊中 |
安全地執行電腦工具集
螢幕上的內容會引導 Claude 的下一個動作。網頁或通知可能會試圖讓 Claude 在錯誤的視窗中輸入文字,或觸發具有實際影響的動作。
在您對拋棄式機器以外的任何環境執行驅動程式之前,請採取以下五個步驟:
- 隔離桌面。為每個工作階段在專用、最低權限的容器或虛擬機器中執行它,不含任何憑證,也不掛載主機。只安裝任務所需的應用程式。在其外部執行呼叫 API 的程式碼。
- 使用
confirm把關具有實際影響的動作。購買或傳送訊息只是一般的left_click、type或key,而confirm接收的是工具及其輸入,而非螢幕畫面。因此,請根據 Claude 在桌面上可以存取的應用程式,選擇哪些工具需要核准。 - 除非任務需要,否則不要在桌面上放置終端機、執行對話方塊和啟動器。當其中之一取得鍵盤焦點時,
type、key和hold_key會執行 Claude 輸入的任何內容,而點擊可能會啟動程式。如果任務需要其中之一,請在鍵盤輸入和點擊之前詢問使用者,並隔離桌面,使指令的影響範圍不超出任務所需。 - 拒絕您的桌面不應接受的輸入,例如螢幕截圖範圍外的點、長達數分鐘的
wait,或非常大的repeat或scroll_amount。引發ToolError(在 TypeScript 中則是拋出它),而不是將該值調整到範圍內。 - 將螢幕上的所有內容視為不受信任的,包括視窗標題和剪貼簿文字。不要在未經檢查的情況下執行或傳遞它們。
SDK 透過您傳入的可呼叫物件套用步驟 2。其他步驟則取決於您的驅動程式和部署。安全性考量下的預防措施也同樣適用。
以下的 confirm 會在任何鍵盤輸入之前,透過您自己的 ask_user 函式詢問使用者,並核准其他所有呼叫。context.member 是工具的名稱。若也要讓使用者核准點擊,請將 ASK_FIRST 設定為 KEYBOARD | CLICKS。
from anthropic.tools.computer import BetaComputerConfirmContext
KEYBOARD = {"type", "key", "hold_key"}
CLICKS = {
"left_click",
"right_click",
"middle_click",
"double_click",
"triple_click",
"left_click_drag",
"left_mouse_down",
"left_mouse_up",
}
ASK_FIRST = KEYBOARD
def confirm(context: BetaComputerConfirmContext) -> bool:
if context.member not in ASK_FIRST:
return True
# 此處顯示的是「Gate consequential members」中的函式。
detail = shown(context.input.to_dict())
return ask_user(f'Allow the "{context.member}" tool with this input?\n{detail}')
desktop = MyDesktop(display, confirm=confirm)被拒絕的呼叫會得到錯誤結果,而執行會繼續進行。對於被拒絕的 type 呼叫,Claude 會讀取 The user did not grant permission to run 'type'. Do not retry it unless the user asks you to.
此 confirm 會核准所有不在 ASK_FIRST 中的工具,包括您之後新增的任何工具。若要採取失敗即關閉(fail closed)的方式,請只核准對桌面沒有影響的工具(screenshot、zoom、cursor_position 和 wait),並對其他所有工具進行詢問。
在一個請求中使用兩個工具集
在 tools 中傳入兩個實例。每個呼叫都帶有其 toolset_name(browser 或 computer),而工具執行器會將呼叫傳送到相符的實例。在呼叫失敗後,工具執行器只會略過該回合中對同一工具集的後續呼叫,因此失敗的電腦呼叫不會略過該回合的瀏覽器呼叫。
# MyBrowser、backend 與 url_policy 取自進階範例。
# confirm 是「安全地執行 computer 工具集」中的函式。
with (
MyBrowser(backend, url_policy=url_policy) as browser,
MyDesktop(display, confirm=confirm) as desktop,
):
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[browser, desktop],
messages=[
{
"role": "user",
"content": "Copy the heading on example.com into the open text editor.",
}
],
stream=True,
run_tools_eagerly=True, # so a call can start before the response ends
)
for stream in runner:
print(stream.get_final_message())在您自己撰寫的迴圈中,請使用 toolset_name 與呼叫的 toolset_name 相符之實例上的 tool_result 來回應每個呼叫。在每個回合中,分別追蹤每個工具集的失敗呼叫,並以該工具集自己的文字回應被略過的呼叫。不使用工具執行器執行提供了瀏覽器的文字,而本節開頭的「被略過的呼叫」項目則提供了電腦的文字。
參考
建構函式選項在兩個 SDK 中具有相同的意義:
| Python | TypeScript | 設定 |
|---|---|---|
configs | configs | 啟用哪些成員 |
confirm | confirm | 核准或拒絕每個呼叫的可呼叫物件 |
url_policy | urlPolicy | 您的 URL 政策,SDK 會在每次 navigate 至某個 URL 之前呼叫它 |
file_policy | filePolicy | 上傳根目錄和下載路徑的公開方式 |
tool_configs | toolConfigs | tools 項目的欄位,例如 cache_control |
_browser_state 方法 | browserState | 狀態報告 |
瀏覽器類別接受表格中的所有選項。電腦類別接受 configs、confirm 和 tool_configs。
建構後您無法變更選項。Python 瀏覽器工具集指南、TypeScript 瀏覽器工具集指南、Python 電腦工具集指南和 TypeScript 電腦工具集指南說明了每個選項。Python 指南也涵蓋了非同步類別 BetaAsyncAbstractBrowserToolset20260801 和 BetaAsyncAbstractComputerToolset20260801。
限制
- SDK 不包含瀏覽器、桌面、現成的驅動程式或 URL 政策: 這些需由您提供。請參閱合作夥伴整合、實作驅動程式和撰寫 URL 政策。
- URL 政策只會看到
navigate呼叫:它看不到網頁發出的請求,也看不到以其他方式開啟的網頁,例如從 Claude 點擊的連結或重新導向開啟的網頁。請參閱在驅動程式中攔截請求。 - 核准是根據上次的狀態報告: 頁面可能在該報告之後發生變化。SDK 不會在呼叫執行之前再次檢查頁面。
- 同一個工具集上的呼叫一次執行一個: 您無法關閉此行為。
- SDK 不會檢查分頁 ID 是否唯一或分頁數量,也不一定會檢查是否只有一個分頁處於作用中:API 會拒絕違反這些規則的報告。
- 對於電腦呼叫,
confirm接收的是工具及其輸入,而非螢幕畫面:您無法從中判斷在特定點的點擊會產生什麼效果。 - SDK 不會調整電腦工具集圖片的大小:API 會拒絕超出模型限制的圖片。請參閱縮放座標並調整圖片大小。
後續步驟
成員工具、browser_state 區塊,以及該工具的安全性考量。
17 個工具、座標與圖片限制,以及該工具的安全性考量。
SDK 如何執行迴圈,以及如何變更它傳送的訊息。
適用於任何讀取不受信任內容之應用程式的防護措施。
Was this page helpful?