---
title: 使用 SDK 工具集進行瀏覽器與電腦操作
url: https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk
description: 從 Python 或 TypeScript SDK 執行瀏覽器使用工具或電腦使用工具。SDK 會執行迴圈以及您設定的檢查，而您則提供瀏覽器或桌面環境。
---

透過 Python 和 TypeScript SDK 中的瀏覽器與電腦工具集，您可以建構在網頁瀏覽器或桌面上運作的代理程式。它可以使用沒有程式化介面的應用程式。透過[瀏覽器使用工具](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool)，Claude 可以瀏覽、閱讀網頁並與之互動。透過[電腦使用工具](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool)，Claude 可以透過螢幕截圖看到桌面，並使用滑鼠和鍵盤。

SDK 將 Claude 連接到您的瀏覽器或桌面。它會執行您設定的檢查，例如針對 `navigate` 的 URL 政策或核准回呼。它會將每個通過檢查的呼叫傳送給您的「driver」（驅動程式），並根據驅動程式傳回的內容建構 `tool_result`。[工具執行器](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/tool-runner)可以為您執行代理迴圈。

您需要提供驅動程式，由它來操作瀏覽器或桌面。請從[供應商的驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#use-a-driver-from-a-provider)開始。使用供應商的驅動程式，只需幾行程式碼即可建立代理程式。如果沒有合適的驅動程式，請使用 Playwright 等瀏覽器自動化函式庫自行撰寫。

本頁大部分內容是關於瀏覽器工具集。[電腦工具集](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#computer-toolset)說明了兩者的差異。

自行撰寫驅動程式屬於進階情境，請參閱[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)。claude-quickstarts 儲存庫中也有以 Python 和 TypeScript 撰寫的[瀏覽器驅動程式範例](https://github.com/anthropics/claude-quickstarts/tree/main/browser-toolset)。它透過 Chrome DevTools Protocol（CDP）控制 Chromium，且並非正式環境用的程式碼。

<Note>
  瀏覽器工具集類別目前為 beta 版，可在 [Python SDK](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md) 和 [TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md) 中使用。電腦工具集類別在相同的 SDK 中同樣為 beta 版，這些 SDK 提供了 [Python 電腦工具集指南](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md)和 [TypeScript 電腦工具集指南](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md)。
</Note>

## 使用供應商的驅動程式

使用供應商提供的驅動程式（例如[合作夥伴整合](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#partner-integrations)中列出的驅動程式），您只需幾行程式碼即可建構代理程式。此範例中的供應商是虛構的。請將 `example_browser_provider` (typescript: `example-browser-provider`) 和 `ExampleBrowser` 替換為[真實供應商](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#partner-integrations)的套件和驅動程式類別，並依照供應商文件的說明進行設定。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { ExampleBrowser } from "example-browser-provider"; // made-up provider

  const client = new Anthropic();
  const browser = new ExampleBrowser();
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [browser],
      messages: [{ role: "user", content: "Open example.com and tell me its heading." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

將驅動程式實例本身作為 `tools` 項目傳入。執行器永遠不會關閉工具集，因此一個實例可以服務多次執行。使用完畢後請自行關閉。

若要自行撰寫驅動程式，請參閱[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)。

## 合作夥伴整合

Browser Use、Browserbase、Daytona 和 E2B 為 SDK 工具集發布了各自的驅動程式。

<CardGroup cols={2}>
  <Card id="browser-use" title="Browser Use" image="https://platform.claude.com/docs/images/partner-logos/browser-use.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browser-use-dark.svg" href="https://docs.browser-use.com/open-source/customize/integrations/toolsets-for-claude">
    適用於瀏覽器工具集的 Browser Use 驅動程式。
  </Card>

  <Card id="browserbase" title="Browserbase" image="https://platform.claude.com/docs/images/partner-logos/browserbase.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/browserbase-dark.svg" href="https://docs.stagehand.dev/v4/integrations/agent-frameworks/claude-cua-toolset-quickstart">
    適用於瀏覽器工具集的 Browserbase Stagehand 驅動程式。
  </Card>

  <Card id="daytona" title="Daytona" image="https://platform.claude.com/docs/images/partner-logos/daytona.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/daytona-dark.svg" href="https://www.daytona.io/docs/en/guides/claude/claude-draws-daytona-sandbox/">
    適用於電腦工具集的 Daytona 驅動程式。
  </Card>

  <Card id="e2-b" title="E2B" image="https://platform.claude.com/docs/images/partner-logos/e2b.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/e2b-dark.svg" href="https://docs.e2b.dev/agents/claude-toolsets">
    適用於瀏覽器和電腦工具集的 E2B 驅動程式。
  </Card>
</CardGroup>

我們的合作夥伴也在 GitHub 上發布了程式碼：[Browser Use 快速入門](https://github.com/browser-use/browser-use/tree/main/examples/integrations/toolsets-for-claude)、[Browserbase 工具集](https://github.com/browserbase/claude-cua-toolset)，以及 [E2B 範例](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js)。

<Warning>
  在您對一次性瀏覽器以外的任何瀏覽器執行驅動程式之前，請先完成以下兩件事：

  * [撰寫 URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)。若沒有 URL 政策，SDK 不會檢查任何 URL。對於由供應商託管的瀏覽器，也請閱讀[遠端與託管瀏覽器](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers)。
  * 使用的瀏覽器設定檔不得登入任何您不願將其資料或操作交給 Claude 的帳戶。請參閱[隔離瀏覽器主機](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host)。

  [安全地執行工具集](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#run-the-toolset-safely)涵蓋了其餘事項。
</Warning>

## 自訂驅動程式

本節中的範例使用 `MyBrowser`，即本頁稍後[進階範例](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#quick-start)中的驅動程式。

### 啟用或停用成員

`configs` 接受[設定工具集](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset)中所述的各成員設定。只需列出您要變更的成員：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # 同時實作 read_console 的 MyBrowser
  browser = MyBrowser(
      backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
  )
  ```

  ```typescript TypeScript
  // 同時實作 read_console 的 MyBrowser
  const browser = new MyBrowser(backend, {
    configs: { read_console: { enabled: true }, navigate: { enabled: false } }
  });
  ```
</CodeGroup>

SDK 會在您的程式碼執行之前拒絕對已停用成員的呼叫。啟用您的類別未實作的成員屬於設定錯誤，除非該類別覆寫了 `execute`。

### 新增前置與後置 hook

覆寫 `execute` 並呼叫父類別的 `execute`。該呼叫之前的程式碼會在 [URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)、[檔案政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads)和 [`confirm`](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members) 之後執行，並且可以變更輸入。SDK 不會再次檢查變更後的輸入。該呼叫之後的程式碼會收到結果，並且可以變更結果。引發 `ToolError`（在 TypeScript 中則是拋出它）即可拒絕該呼叫。

覆寫 `execute` 會改變提供給 Claude 的成員。SDK 會將每個成員都視為已實作，因此 Claude 會獲得所有預設開啟的成員。[進階範例](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#quick-start)中的 `MyBrowser` 只服務三個成員，因此下列 `TracedBrowser` 會向 Claude 提供它無法服務的成員。使用前請先透過 `configs` 關閉這些成員。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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
  ```

  ```typescript TypeScript
  import type { BetaToolsetCallContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaBrowserMemberInput,
    BetaBrowserMemberName
  } from "@anthropic-ai/sdk/resources/beta";

  class TracedBrowser extends MyBrowser {
    protected override async execute(
      ctx: BetaToolsetCallContext,
      name: BetaBrowserMemberName,
      input: BetaBrowserMemberInput
    ) {
      const started = performance.now();
      const result = await super.execute(ctx, name, input);
      log.info(`${ctx.toolUse?.id ?? "-"} ${name} ${Math.round(performance.now() - started)}ms`);
      return name === "get_page_text" ? redact(result as string) : result;
    }
  }
  ```
</CodeGroup>

## 實作驅動程式

驅動程式是您對 `BetaAbstractBrowserToolset20260801` 的子類別。在其中覆寫您的瀏覽器所支援的成員。您未實作的成員會以停用狀態傳送給 API。如果 Claude 仍然呼叫它，SDK 會傳回錯誤，而執行會繼續。覆寫 `execute` 會改變哪些成員以停用狀態傳送（[新增前置與後置 hook](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#add-before-and-after-hooks)）。每個成員都會收到呼叫上下文，以及以型別物件表示的成員輸入，例如 `BetaBrowserNavigateInput`。輸入型別來自 `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`)。[成員工具](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#member-tools)列出了每個輸入的欄位。

在 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` (typescript: `browserState`)，包括被拒絕和失敗的呼叫。請回傳每個開啟的分頁，以及自上次報告以來的變更：

* 已開啟的分頁和下載事件。
* 針對您的[請求掛鉤](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver)所封鎖的每次導覽，回傳一個 `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` 追蹤分頁](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-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` (typescript: `browserState`) 引發任何例外（即使是 `ToolError`）時，SDK 也會引發此錯誤。工具執行器和 `tool_result` (typescript: `toolResult`) 不會捕捉它，因此它會傳達到它們的呼叫者。

SDK 不會隱藏成員錯誤文字中、`left_click` 等動作回傳的那行文字中、失敗下載的錯誤中，或被關閉對話框訊息中的本機路徑。

來自您的 URL 政策、檔案政策或 `confirm` 可呼叫物件的 `ToolError` 會原封不動地傳達給 Claude，因此請勿在其文字中包含本機路徑。請在您的成員中捕捉例外，並以您自己的文字引發 `ToolError`（在 TypeScript 中則是拋出它）。

### 在回應串流時啟動呼叫

預設情況下，工具執行器會在 Claude 的回應結束後才執行該回合的呼叫。使用「early start」（提前啟動）時，它可以在回應仍在串流時就啟動瀏覽器或電腦呼叫。

若要啟用此功能，請將 `stream=True` (typescript: `stream: true`) 和 `run_tools_eagerly=True` (typescript: `runToolsEagerly: true`) 傳給工具執行器，如[進階範例](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#quick-start)所示。使用 `stream` 時，您對執行器的迴圈每次迭代都會得到一個串流，而非一則訊息。

提前啟動改變的是呼叫可以開始的時間點，而非工具集執行呼叫的方式：

* **檢查仍會先執行：** SDK 會在呼叫執行之前呼叫您的 URL 政策、檔案政策和 `confirm`。使用提前啟動時，它可能會在回應仍在串流時呼叫 `confirm`。
* **呼叫仍會逐一執行：** 工具集會依照 Claude 撰寫的順序執行其呼叫。失敗的呼叫仍會停止該工具集在該回合中的後續呼叫。
* **已啟動的呼叫無法撤回：** 如果回應隨後被截斷（例如達到 `max_tokens`），或您的迴圈提前停止，該動作仍會發生，而 Claude 永遠不會讀到其結果。

### 不使用工具執行器執行

在 `tools` 中傳入實例（在 TypeScript 中為 `browser.toJSON()`），並以 `tool_result` (typescript: `toolResult`) 回應每個成員呼叫。瀏覽器使用工具要求您在第一個失敗的呼叫處停止（[批次動作](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#batch-actions)）。

此迴圈使用[進階範例](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#quick-start)中的 `MyBrowser` 和 URL 政策。在呼叫失敗後，它會回應該回合後續的呼叫而不執行它們：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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})
  ```

  ```typescript TypeScript
  const NOT_EXECUTED = "Not executed: an earlier action in this turn failed.";
  const MAX_TURNS = 10;

  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const messages: Anthropic.Beta.BetaMessageParam[] = [
      { role: "user", content: "Open example.com" }
    ];
    for (let turn = 0; turn < MAX_TURNS; turn++) {
      const response = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        tools: [browser.toJSON()],
        messages
      });
      messages.push({ role: "assistant", content: response.content });
      const calls = response.content.filter(
        (block): block is Anthropic.Beta.BetaToolUseBlock =>
          block.type === "tool_use" && block.toolset_name === browser.toolsetName
      );
      if (calls.length === 0) {
        break;
      }
      const results: Anthropic.Beta.BetaToolResultBlockParam[] = [];
      let failed = false;
      for (const call of calls) {
        if (failed) {
          // 呼叫失敗後，該回合其餘部分僅會回覆，不會執行。
          results.push({
            type: "tool_result",
            tool_use_id: call.id,
            toolset_name: call.toolset_name,
            content: NOT_EXECUTED,
            is_error: true
          });
          continue;
        }
        const result = await browser.toolResult(call);
        failed = result.is_error === true;
        results.push(result);
      }
      messages.push({ role: "user", content: results });
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

對每個被略過呼叫的回應都帶有 `is_error`、該呼叫的 `toolset_name`，以及[批次動作](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#batch-actions)所要求的確切文字。工具執行器也會傳送相同的回應。

## 進階範例：自行撰寫驅動程式

此範例將[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)的主要部分整合到一個驅動程式 `MyBrowser` 中，並搭配工具執行器和[提前啟動](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams)來執行。`MyBrowser` 實作了 `navigate`、`screenshot` 和 `left_click`，以及每個驅動程式都需要的狀態報告 `_browser_state` (typescript: `browserState`)。

`backend` 代表您自己對瀏覽器自動化函式庫（例如 Playwright）的包裝。URL 政策僅為範例。[撰寫 URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)會加以說明。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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())
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import {
    BetaAbstractBrowserToolset20260801,
    type BetaBrowserNavigateResult,
    type BetaBrowserToolsetOptions,
    type BetaScreenshotResult,
    type BetaToolsetCallContext,
    type BetaURLContext,
    ToolError
  } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaBrowserLeftClickInput,
    BetaBrowserNavigateInput,
    BetaBrowserScreenshotInput
  } from "@anthropic-ai/sdk/resources/beta";

  class MyBrowser extends BetaAbstractBrowserToolset20260801 {
    constructor(
      private backend: Backend,
      options: Omit<BetaBrowserToolsetOptions, "browserState"> = {}
    ) {
      super({
        ...options,
        // 必要：每個開啟的分頁，以及自上次回報以來的變更。
        browserState: () => ({
          tabs: backend.tabs().map((tab) => ({
            tab_id: tab.id,
            title: tab.title,
            url: tab.url,
            active: tab.id === backend.active
          })),
          state_changes: backend.drainChanges()
        })
      });
    }

    protected override async navigate(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserNavigateInput
    ): Promise<BetaBrowserNavigateResult> {
      // input.url 為 "back"、"forward"、"reload"，或是您的 URL 政策
      // 所允許、由 Claude 寫出的 URL。與 isAllowed 相同，backend.goto 會為
      // 未以 scheme 開頭的 URL 加上 https://。
      const page = await this.backend.goto(input.url, input.tab_id);
      return { url: page.url, status: page.status, title: page.title };
    }

    protected override async screenshot(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserScreenshotInput
    ): Promise<BetaScreenshotResult> {
      return { data: await this.backend.pngBase64(input.tab_id), mediaType: "image/png" };
    }

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaBrowserLeftClickInput
    ): Promise<void> {
      // 無需回傳任何內容：Claude 會讀到 "Clicked."
      await this.backend.click(input.target, input.tab_id);
    }

    override async close(): Promise<void> {
      await super.close(); // first, so no call is still using the browser when it closes
      if (!this.backend.closed) {
        await this.backend.close();
      }
    }
  }

  const ALLOWED_HOSTS = ["example.com", "iana.org"];
  const SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:/i;

  // 這只是範例，並非正式環境政策。請參閱「Write a URL policy」。
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // 瀏覽器會將網頁 URL 中的 "\" 視為 "/"。
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

  function urlPolicy(ctx: BetaURLContext, url: string): void {
    if (!isAllowed(url)) {
      throw new ToolError(`blocked: ${url} is not on an allowed host`);
    }
  }

  const client = new Anthropic();
  const browser = new MyBrowser(backend, { urlPolicy });
  try {
    const runner = client.beta.messages.toolRunner({
      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,
      runToolsEagerly: true // so a call can start before the response ends
    });
    for await (const stream of runner) {
      console.log(await stream.finalMessage());
    }
  } finally {
    await browser.close();
  }
  ```
</CodeGroup>

## 安全地執行工具集

Claude 的下一個動作取決於它所讀取的頁面。頁面或注入其中的文字可能會嘗試存取內部服務，或從主機取出檔案。它也可能嘗試觸發具有實際影響的動作。在您對一次性瀏覽器以外的任何瀏覽器執行驅動程式之前，請採取以下六個步驟：

1. [撰寫 URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)，拒絕任務不需要的每個 `navigate` URL。
2. [在驅動程式中攔截請求](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver)，並以相同的規則檢查每個請求。
3. [在容器上設定出口政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container)，讓網路封鎖驅動程式看不到的內容。
4. [限制上傳和下載](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads)，或保持上傳關閉。
5. 使用 `confirm` [管控具重大影響的成員](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#gate-consequential-members)。
6. 為每個工作階段在專用的容器或 VM 中[隔離瀏覽器主機](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#isolate-the-browser-host)。

SDK 會透過您傳入的政策和可呼叫物件來套用步驟 1、4 和 5。步驟 2、3 和 6 則取決於您的驅動程式和部署。[安全性考量](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#security-considerations)中的預防措施同樣適用。

<Note>
  Anthropic 的「prompt injection」（提示注入）分類器（如[安全性考量](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#security-considerations)中所述）目前不會在透過 Amazon Bedrock 傳送的瀏覽器工具集請求上執行。
</Note>

### 撰寫 URL 政策

<Warning>
  沒有 URL 政策時，SDK 不會檢查任何 URL，而 API 也不會過濾 Claude 開啟的 URL。即使有政策，Claude 點擊的連結或重新導向仍可能將瀏覽器帶往 loopback（`localhost`、`127.0.0.1`）、鏈路本機位址（例如雲端中繼資料位址 `169.254.169.254`），以及私有網路範圍。[容器上的出口規則](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container)可以封鎖鏈路本機和私有位址。在容器外部強制執行的規則無法封鎖 loopback。

  SDK 不會檢查 URL 的 scheme，因此您的驅動程式必須拒絕所有它不打算開啟的 scheme，例如 `javascript:`、`view-source:`、`data:` 和 `file:`。大多數驅動程式只需要 `http` 和 `https`。下方的範例政策只允許 `http` 和 `https` URL，以及 `about:blank`。

  `file:` URL 讓 Claude 能夠讀取瀏覽器主機上的檔案。`javascript:` URL 會在目前頁面中執行指令碼，即使 `javascript_exec` 已關閉也一樣。出口規則無法阻止這兩者。
</Warning>

「URL policy」（URL 政策）是您以 `url_policy` (typescript: `urlPolicy`) 傳入的函式。政策由您撰寫，您也有責任讓它達到正式環境的水準。在您的 `navigate` 執行之前，SDK 會以呼叫的上下文和 Claude 所寫的 URL 呼叫該政策。

不回傳任何內容即表示允許該 URL。若要拒絕，請引發 `ToolError`（在 TypeScript 中則是拋出它）。Claude 會讀取錯誤訊息，而 `navigate` 不會執行。此政策只檢查每個 `navigate` 呼叫中的 URL，它並非網路控制機制。

這是[進階範例](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#quick-start)中的政策。它允許 `about:blank`，以及透過 `http` 和 `https` 存取兩個網站及其子網域：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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)
  ```

  ```typescript TypeScript
  import { type BetaURLContext, ToolError } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const ALLOWED_HOSTS = ["example.com", "iana.org"];
  const SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:/i;

  // 僅為範例，並非正式環境的政策。
  function isAllowed(url: string): boolean {
    if (url.toLowerCase() === "about:blank") {
      return true;
    }
    const withScheme = SCHEME_PREFIX.test(url) ? url : `https://${url}`;
    // 瀏覽器會將網頁 URL 中的 "\" 視為 "/"。
    const parsed = URL.parse(withScheme.replaceAll("\\", "/"));
    const host = parsed?.hostname ?? "";
    const listed = ALLOWED_HOSTS.some((name) => host === name || host.endsWith(`.${name}`));
    return (parsed?.protocol === "http:" || parsed?.protocol === "https:") && listed;
  }

  function urlPolicy(ctx: BetaURLContext, url: string): void {
    if (!isAllowed(url)) {
      throw new ToolError(`blocked: ${url} is not on an allowed host`);
    }
  }

  const browser = new MyBrowser(backend, { urlPolicy });
  ```
</CodeGroup>

SDK 只會針對 `navigate` 呼叫此政策，而不會針對 `"back"`、`"forward"` 或 `"reload"` 呼叫。Claude 可能會省略 scheme，例如 `example.com`，因此範例在讀取主機之前，會為不以 scheme 開頭的 URL 加上 `https://`。您的 `navigate` 收到的是 Claude 所寫的 URL。請在那裡以相同的判斷加上 scheme，讓瀏覽器開啟的正是您的政策所檢查的 URL。

此範例並非正式環境等級。正式環境的政策需要更多處理，涵蓋以下等領域：

* Claude 點擊的連結、提交的表單、重新導向，以及頁面自行發出的請求。只有您的驅動程式看得到這些。請參閱[在驅動程式中攔截請求](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver)。
* 來自頁面內容的主機名稱和 URL，攻擊者可以選擇這些內容。
* 以瀏覽器的方式解析每個 URL，而非以單純的字串分割來找出主機名稱。
* URL scheme。SDK 不會檢查它們，因此請拒絕您的驅動程式不打算開啟的每個 scheme。
* 容器上的出口規則，用於處理任何 URL 檢查都看不到的部分。請參閱[在容器上設定出口政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container)。

如果您傳入 `url_policy=None` (typescript: `urlPolicy: null`)，SDK 會拒絕每個導向 URL 的 `navigate`，因此從您的設定中讀取到的 `None` (typescript: `null`) 不會意外地讓 URL 未經檢查。在 TypeScript 中，`undefined` 會讓該選項保持未設定，與不傳入它相同。

### 在驅動程式中攔截請求

URL 政策只判斷 `navigate` 呼叫中的 URL。Claude 點擊的連結、提交的表單、重新導向，或已允許頁面上的圖片或指令碼，仍可能存取政策會拒絕的主機。請使用[出口規則](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container)封鎖這些主機。

具備請求攔截功能的驅動程式也可以在其請求掛鉤中檢查這些請求，請求掛鉤是您的自動化函式庫在每個請求之前呼叫的函式。呼叫您的政策所使用的函式，這樣規則只需撰寫一次：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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_()
  ```

  ```typescript TypeScript
  // 在 driver 類別中，於 Playwright 瀏覽器 context 上。
  // isAllowed 是「Write a URL policy」中的函式。
  await context.route("**/*", (route) =>
    isAllowed(route.request().url()) ? route.continue() : route.abort("blockedbyclient")
  );
  ```
</CodeGroup>

像這樣的請求掛鉤看不到 WebSocket 交握、service worker 請求或重新導向的中繼跳轉。請封鎖 service worker。使用能看到 WebSocket 交握和重新導向跳轉的掛鉤來檢查它們。範例政策會拒絕 `ws://` 和 `wss://` URL，因此在將 WebSocket URL 傳給 `is_allowed` (typescript: `isAllowed`) 之前，請將 `ws://` 改為 `http://`，並將 `wss://` 改為 `https://`。

### 在容器上設定出口政策

攔截無法看到瀏覽器發出的每個請求，而 URL 政策也看不到名稱解析到的位置。由容器網路強制執行的「egress policy」（出口政策）可以同時涵蓋這兩者：

* 封鎖 IPv4 和 IPv6 中的鏈路本機和私有位址範圍，其中包括雲端中繼資料位址 `169.254.169.254`。
* 只允許對任務所需主機的對外連線。如果您的規則比對的是 IP 位址，請在容器啟動時解析您允許的主機名稱。
* 只允許對容器解析器的 DNS。

在瀏覽器容器外部強制執行的規則（例如 Kubernetes NetworkPolicy 或雲端防火牆）看不到容器內部的 loopback。若只有這些規則，頁面就能存取在那裡監聽的任何服務，包括 DevTools 連接埠，該連接埠會顯示瀏覽器開啟的分頁清單以及控制每個分頁的位址。

### 限制上傳和下載

`file_upload` 預設為關閉。若沒有 `file_policy` (typescript: `filePolicy`)，SDK 會拒絕每個提供路徑或[文件 ID](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool#upload-files) 的上傳。若要啟用上傳，請傳入一個 `BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`)，並指定一個只存放任務檔案的上傳目錄：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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,
      ),
  )
  ```

  ```typescript TypeScript
  import { BetaNodeFilePolicy } from "@anthropic-ai/sdk/helpers/beta/toolsets/node";

  // 同時實作 file_upload 的 MyBrowser
  const browser = new MyBrowser(backend, {
    configs: { file_upload: { enabled: true } },
    confirm: makeConfirm(), // required for file_upload; see Gate consequential members
    urlPolicy,
    filePolicy: new BetaNodeFilePolicy({
      uploadRoots: ["/task/uploads"],
      downloadDir: "/task/downloads",
      exposeDownloadPaths: false
    })
  });
  ```
</CodeGroup>

SDK 會解析每個上傳路徑（包括追蹤符號連結），並拒絕上傳根目錄之外的任何路徑。若下載目錄本身是上傳根目錄、位於上傳根目錄內，或包含上傳根目錄，檔案政策會引發設定錯誤。此檢查可能會漏掉符號連結，因此請勿讓符號連結使下載目錄與上傳根目錄相同，或使其中一個位於另一個之內。只有當 `expose_download_paths` (typescript: `exposeDownloadPaths`) 為 true 且檔案位於下載目錄內時，SDK 才會在狀態報告中包含下載的 `path`。

內建的檔案政策會在執行 SDK 的程序所在的檔案系統上檢查路徑。它只能保護與該檔案系統共用的瀏覽器。對於遠端瀏覽器，請改為遵循[遠端與託管瀏覽器](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers)。

請以下列方式設定下載：

* 自行建立模式為 `0700` 的下載目錄，並以 `noexec,nosuid,nodev` 掛載。
* 讓該目錄無法被 Claude 可呼叫的其他工具（例如 shell 或檔案工具）存取。
* 在 `download_failed` 狀態變更中，將 `error` 寫成固定的措辭。例外的文字可能包含路徑或 URL。
* 在有人做出決定之前，不要將下載的檔案讀入對話中，也不要執行它。

### 管控具重大影響的成員

`javascript_exec` 和 `file_upload` 預設為關閉。如果您在沒有 `confirm` 可呼叫物件的情況下啟用其中任何一個，建構函式會引發設定錯誤。有 `confirm` 可呼叫物件時，SDK 會在每個即將執行的呼叫之前呼叫它。沒有的話，則不會詢問任何事。在 TypeScript 中，`confirm: null` 不會引發設定錯誤，而 SDK 會拒絕對任何成員的每個呼叫。

回傳 `True` (typescript: `true`) 以執行呼叫，或回傳 `False` (typescript: `false`) 以拒絕呼叫。當您的可呼叫物件詢問某人時，請向對方顯示成員、頁面的 URL 和呼叫的輸入。首先，請跳脫 URL 和輸入中所有可列印 ASCII 以外的字元，因為兩者都可能夾帶來自頁面的文字。

此範例透過您自己的 `ask_user` (typescript: `askUser`) 函式詢問兩個受管控的成員，並核准其餘成員：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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,
  )
  ```

  ```typescript TypeScript
  import type { BetaConfirmContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const GATED = new Set(["javascript_exec", "file_upload"]);

  /** A value as JSON, with every character outside printable ASCII escaped. */
  const shown = (value: unknown): string =>
    JSON.stringify(value, null, 2).replace(
      /[^\n\x20-\x7e]/g,
      (match) => `\\u${match.charCodeAt(0).toString(16).padStart(4, "0")}`
    );

  const makeConfirm = () => {
    const granted = new Set<string>();
    return async (ctx: BetaConfirmContext): Promise<boolean> => {
      const name = ctx.member;
      if (!GATED.has(name)) {
        return true;
      }
      const detail = shown(ctx.input);
      const page = ctx.tabURL;
      const where = page ? shown(page) : "a tab with no URL";
      const question = `Allow ${name} on ${where}?\n${detail}`;
      const scheme = page === undefined ? undefined : URL.parse(page)?.protocol;
      if (scheme !== "http:" && scheme !== "https:") {
        return askUser(question); // no web origin: ask every time
      }
      // 核准僅涵蓋此頁面上的這個確切輸入。
      const key = `${name} ${page} ${detail}`;
      if (!granted.has(key) && (await askUser(question))) {
        granted.add(key);
      }
      return granted.has(key);
    };
  };

  // 同時實作 javascript_exec 與 file_upload 的 MyBrowser
  // file_upload 也需要「Confine uploads and downloads」中的 filePolicy
  const browser = new MyBrowser(backend, {
    configs: { javascript_exec: { enabled: true }, file_upload: { enabled: true } },
    confirm: makeConfirm(),
    urlPolicy
  });
  ```
</CodeGroup>

每次呼叫 `make_confirm()` (typescript: `makeConfirm()`) 都會回傳一個沒有任何核准紀錄的可呼叫物件。請為每個工具集呼叫一次，並為每位使用者提供各自的工具集。

核准涵蓋的是上次狀態報告所顯示的頁面，而頁面可能在呼叫執行之前發生變化。購買、傳送訊息和接受條款都是透過 `left_click` 和 `type` 等一般成員進行的，因此 `confirm` 無法依名稱將它們挑出。若要讓某人核准這些動作，也請針對這些成員進行詢問。

除非[出口規則](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#put-egress-policy-on-the-container)將瀏覽器的對外連線限制在任務所需的主機，否則請勿啟用 `javascript_exec`。指令碼可以將頁面內容傳送到瀏覽器能存取的任何位址。

`navigate` 呼叫中的 `javascript:` URL 也會在目前頁面中執行指令碼，即使 `javascript_exec` 已關閉也一樣。`confirm` 會將該呼叫視為 `navigate`，因此範例中的 `confirm` 可呼叫物件會核准它。您的驅動程式必須拒絕 `javascript:` URL。在範例中，[URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)會在 `confirm` 執行之前拒絕它們。

### 隔離瀏覽器主機

為每個工作階段在專用、最低權限的容器或 VM 中執行瀏覽器：

* 以非 root 使用者身分執行，並在瀏覽器允許的情況下使用唯讀的根檔案系統。
* 除了您所設定的上傳和下載目錄（如果有的話）之外，不要從主機掛載任何內容。
* 讓環境中不含憑證，並從全新的瀏覽器設定檔開始。
* 不與 Claude 可呼叫的其他工具共用任何檔案系統。

<Warning>
  瀏覽器設定檔不得登入任何您不願將其資料或操作交給 Claude 的帳戶。`javascript_exec` 會以頁面本身的權限執行：包括其 cookie、其儲存空間，以及其已登入的工作階段。
</Warning>

請在瀏覽器容器之外執行呼叫 API 的程式碼，因為該程式碼持有您的 API 金鑰和對話內容。工具執行器和 `tool_result` (typescript: `toolResult`) 都會在該程式碼的程序中執行工具集，因此瀏覽器不會與工具集共用檔案系統。請將瀏覽器視為遠端：[遠端與託管瀏覽器](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#remote-and-hosted-browsers)適用於此情況。

請將頁面傳回的所有內容視為不受信任，包括頁面文字、螢幕截圖、主控台和網路項目、分頁標題以及下載名稱。

### 遠端與託管瀏覽器

有些瀏覽器不會與執行 SDK 的程序共用檔案系統。例如位於另一個容器中的瀏覽器、您透過 DevTools URL 存取的瀏覽器，以及來自託管瀏覽器服務的瀏覽器。對於這些瀏覽器，URL 政策、請求攔截和 `confirm` 仍會在您的程序中執行。

使用託管瀏覽器時，由供應商控制出口和主機隔離。您自己的出口政策在那裡並不適用，因此驅動程式的請求 hook 是您對瀏覽器請求的唯一檢查。請了解瀏覽器的網路可以存取哪些位置。

內建的路徑檢查無法保護遠端瀏覽器。`BetaLocalFilePolicy` (typescript: `BetaNodeFilePolicy`) 會在執行 SDK 的程序所在的檔案系統上檢查路徑，而瀏覽器讀寫的是它自己的檔案系統。

SDK 無法偵測瀏覽器是否為遠端。因此對於遠端瀏覽器，請保持 `file_upload` 關閉，除非您的驅動程式使用自己的 `BetaFilePolicy`，在瀏覽器執行的位置檢查上傳路徑。`BetaFilePolicy` 會審查每個上傳的路徑和文件 ID，並決定 Claude 是否能看到下載的路徑。

請讓遠端瀏覽器拒絕下載，除非其本身的主機具備[限制上傳和下載](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads)中的下載設定。

請勿讓供應商的 API 金鑰和工作階段的連線 URL（其中可能包含金鑰）出現在日誌、工具結果和錯誤文字中。如果供應商會錄製工作階段，該錄製內容就是 Claude 所看到和輸入的所有內容的另一份副本，且供應商的保留條款適用於它。

## 電腦工具集

`BetaAbstractComputerToolset20260801` 是用於[電腦使用工具](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool)的類別。驅動程式是一個子類別，每個工具對應一個方法，例如 `screenshot` 或 `left_click`。您可以從[合作夥伴整合](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#partner-integrations)下的合作夥伴驅動程式開始。若要自行撰寫，claude-quickstarts 儲存庫提供了以 Python 和 TypeScript 撰寫的[最小 VNC 範例](https://github.com/anthropics/claude-quickstarts/tree/main/computer-toolset)。它透過 VNC 控制桌面，並非正式環境用的程式碼。

此類別接受 `configs`、`confirm` 和 `tool_configs` (typescript: `toolConfigs`)，傳入方式與瀏覽器類別相同。[自訂驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#customize-a-driver)、[處理錯誤](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#handle-errors)、[在回應串流時啟動呼叫](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#start-calls-while-the-response-streams)和[不使用工具執行器執行](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner)也適用於它，但有以下差異：

* **僅限瀏覽器的選項：** 此類別沒有 [URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)、沒有[檔案政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#confine-uploads-and-downloads)，也沒有[狀態報告](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#report-browser-state)。
* **`configs`：** 您實作的每個工具預設都是開啟的。各工具的設定列於[工具參數](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#tool-parameters)。
* **`confirm`：** 如果您的類別實作了 `type`、`key` 或 `hold_key`，除非您傳入 `confirm` 可呼叫物件或使用 `configs` 關閉這些工具，否則建構函式會引發設定錯誤。覆寫 `execute` 的類別會被視為實作了這三者。
* **被略過的呼叫：** 在電腦呼叫失敗後，工具執行器會以 `Not executed: an earlier computer action in this turn failed.` 回應該回合的後續電腦呼叫。瀏覽器呼叫則會收到不同的文字。在您自行撰寫的迴圈中，請以該確切文字回應被略過的電腦呼叫（[批次動作](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#batch-actions)）。
* **`execute`：** 在 TypeScript 中，`execute` 覆寫會將 `name` 的型別設為 `BetaComputerMemberName`，並將 `input` 的型別設為 `BetaComputerMemberInput`。兩者都來自 `@anthropic-ai/sdk/resources/beta`。
* **錯誤文字：** SDK 不會隱藏電腦工具錯誤文字中或工具回傳文字中的本機路徑。Claude 會讀取兩者，並截斷為 4,096 個字元。請在您的工具中捕捉例外，並以您自己的文字引發 `ToolError`（在 TypeScript 中則是拋出它）。

### 撰寫桌面驅動程式

此驅動程式實作了[可用動作](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#available-actions)中所列 17 個工具中的五個。您未實作的工具會以停用狀態傳送至 API，如[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)所述。在範例中，`display` 代表您自己對任何控制桌面之工具（例如 VNC 用戶端或 `xdotool`）的包裝，而 `ask_user` (typescript: `askUser`) 代表您自己用來詢問使用者的函式。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import {
    BetaAbstractComputerToolset20260801,
    type BetaComputerCursorPositionResult,
    type BetaComputerToolsetOptions,
    type BetaScreenshotResult,
    type BetaToolsetCallContext
  } from "@anthropic-ai/sdk/helpers/beta/toolsets";
  import type {
    BetaComputerCursorPositionInput,
    BetaComputerKeyInput,
    BetaComputerLeftClickInput,
    BetaComputerScreenshotInput,
    BetaComputerTypeInput
  } from "@anthropic-ai/sdk/resources/beta";

  class MyDesktop extends BetaAbstractComputerToolset20260801 {
    constructor(
      private display: Display,
      options: BetaComputerToolsetOptions = {}
    ) {
      super(options);
    }

    protected override async screenshot(
      ctx: BetaToolsetCallContext,
      input: BetaComputerScreenshotInput
    ): Promise<BetaScreenshotResult> {
      // Claude 的座標是此影像中的像素位置。
      return { data: await this.display.pngBase64(), mediaType: "image/png" };
    }

    protected override async cursor_position(
      ctx: BetaToolsetCallContext,
      input: BetaComputerCursorPositionInput
    ): Promise<BetaComputerCursorPositionResult> {
      const { x, y } = await this.display.cursor();
      return { x, y };
    }

    protected override async left_click(
      ctx: BetaToolsetCallContext,
      input: BetaComputerLeftClickInput
    ): Promise<void> {
      // input.coordinate 為 [x, y]，若為 null 或 undefined 則在游標處點擊。input.text
      // 為要按住的修飾鍵，例如 "shift"。無需回傳：Claude 會讀到 "Clicked."
      await this.display.click(input.coordinate, input.text);
    }

    // type 工具在 TypeScript 中寫作 type_。
    protected override async type_(
      ctx: BetaToolsetCallContext,
      input: BetaComputerTypeInput
    ): Promise<void> {
      await this.display.type(input.text);
    }

    protected override async key(
      ctx: BetaToolsetCallContext,
      input: BetaComputerKeyInput
    ): Promise<void> {
      await this.display.press(input.text, input.repeat ?? 1);
    }

    override async close(): Promise<void> {
      await super.close(); // first, so no call is still using the desktop when it closes
      await this.display.close();
    }
  }

  const client = new Anthropic();
  const desktop = new MyDesktop(display, {
    // type 與 key 必填；請參閱「安全地執行 computer 工具集」
    confirm: (ctx) => askUser(`Allow the "${ctx.member}" tool?`)
  });
  try {
    const runner = client.beta.messages.toolRunner({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      tools: [desktop],
      messages: [{ role: "user", content: "Open the calculator and compute 17 * 23." }]
    });
    for await (const message of runner) {
      console.log(message);
    }
  } finally {
    await desktop.close();
  }
  ```
</CodeGroup>

<Warning>
  此範例中的 `confirm` 會在驅動程式執行的每個呼叫之前詢問某人。在您對拋棄式機器以外的任何環境執行驅動程式之前，請閱讀[安全地執行電腦工具集](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely)。
</Warning>

有四個工具沒有輸入欄位：`screenshot`、`cursor_position`、`left_mouse_down` 和 `left_mouse_up`。每個輸入型別（包括它們的）都來自 `anthropic.types.beta` (typescript: `@anthropic-ai/sdk/resources/beta`)。

在 TypeScript 中，請將工具寫成方法，而非箭頭函式欄位。[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)說明了原因。

### 縮放座標並調整圖片大小

Claude 傳送的每個點都是您所回傳螢幕截圖中的像素位置：每個 `coordinate` 和 `start_coordinate`，以及 `zoom` `region` 的兩個角。SDK 會將它們原封不動地傳遞給您的工具。

如果您的螢幕截圖與顯示器的大小不同，請在您的驅動程式中進行雙向轉換：將 Claude 的點轉換為顯示器像素，並將您回報的游標位置轉換為螢幕截圖像素。在整個工作階段中保持螢幕截圖大小不變，並使其具有與顯示器相同的長寬比：

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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)
  ```

  ```typescript TypeScript
  import { ToolError } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const SCREENSHOT_SIZE = { width: 1280, height: 720 }; // the size of every full screenshot
  const DISPLAY_SIZE = { width: 2560, height: 1440 };

  function toDisplay(coordinate: number[]): [number, number] {
    const [x, y] = coordinate;
    const { width, height } = SCREENSHOT_SIZE;
    if (x < 0 || x >= width || y < 0 || y >= height) {
      // 拒絕此座標點。若將其移入範圍內，會點擊到 Claude 並未選擇的位置。
      throw new ToolError(`[${x}, ${y}] is outside the ${width}x${height} screenshot`);
    }
    return [
      Math.floor((x * DISPLAY_SIZE.width) / width),
      Math.floor((y * DISPLAY_SIZE.height) / height)
    ];
  }

  function toScreenshot(x: number, y: number): [number, number] {
    return [
      Math.floor((x * SCREENSHOT_SIZE.width) / DISPLAY_SIZE.width),
      Math.floor((y * SCREENSHOT_SIZE.height) / DISPLAY_SIZE.height)
    ];
  }

  toDisplay([640, 360]); // [1280, 720]
  ```
</CodeGroup>

在對每個 `coordinate` 和 `start_coordinate` 採取動作之前，先對其呼叫 `to_display` (typescript: `toDisplay`)，並對 `cursor_position` 回報的位置呼叫 `to_screenshot` (typescript: `toScreenshot`)。沒有 `coordinate` 的點擊或捲動會在游標處執行，因此沒有需要轉換的內容。以相同的比例縮放 `zoom` `region`。`zoom` 圖片不會改變這一點：Claude 接下來傳送的點仍然是完整螢幕截圖中的像素位置。

SDK 從不讀取或調整圖片大小。API 會拒絕超出模型限制的圖片，這會結束執行。請在驅動程式中縮放每張螢幕截圖和 `zoom` 圖片。請參閱[調整螢幕截圖大小以符合圖片限制](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions)。在長時間執行中，一個請求可能包含超過 20 張圖片，此時 API 會對每張圖片套用更嚴格的限制。請參閱[管理螢幕截圖歷史記錄](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history)。

### 輸入檢查與結果

不要依賴 SDK 來檢查呼叫的輸入。讓您的驅動程式檢查它所使用的欄位，並拒絕它無法執行的呼叫：引發 `ToolError`（在 TypeScript 中則是拋出它）。Claude 會讀取該訊息，並可以傳送修正後的呼叫。

兩個 SDK 都不會限制 `duration`、`repeat` 次數或 `scroll_amount`。[安全地執行電腦工具集](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#run-the-computer-toolset-safely)下的步驟 4 涵蓋了這些內容。

工具回傳的內容決定了 Claude 讀取的內容：

| 工具                  | 回傳                                 | Claude 讀取                                   |
| ------------------- | ---------------------------------- | ------------------------------------------- |
| `screenshot`、`zoom` | `BetaScreenshotResult`             | 一個圖片區塊                                      |
| `cursor_position`   | `BetaComputerCursorPositionResult` | `X={x},Y={y}`                               |
| 其他所有工具              | 無，或一行文字                            | 簡短的確認訊息，例如 `Clicked.`，接著是回傳的那一行，位於其自己的文字區塊中 |

### 安全地執行電腦工具集

螢幕上的內容會引導 Claude 的下一個動作。網頁或通知可能會試圖讓 Claude 在錯誤的視窗中輸入文字，或觸發具有實際影響的動作。

<Warning>
  實作 `type`、`key` 或 `hold_key` 的驅動程式需要一個 `confirm` 可呼叫物件。若沒有它，建構函式會引發設定錯誤，除非 `configs` 關閉了這些工具。SDK 本身不會詢問任何人：它會執行您的 `confirm` 核准的每個呼叫，而在沒有 `confirm` 的情況下，它會執行每個呼叫，包括每次點擊。您實作的每個電腦工具預設都是啟用的。
</Warning>

在您對拋棄式機器以外的任何環境執行驅動程式之前，請採取以下五個步驟：

1. 隔離桌面。為每個工作階段在專用、最低權限的容器或虛擬機器中執行它，不含任何憑證，也不掛載主機。只安裝任務所需的應用程式。在其外部執行呼叫 API 的程式碼。
2. 使用 `confirm` 把關具有實際影響的動作。購買或傳送訊息只是一般的 `left_click`、`type` 或 `key`，而 `confirm` 接收的是工具及其輸入，而非螢幕畫面。因此，請根據 Claude 在桌面上可以存取的應用程式，選擇哪些工具需要核准。
3. 除非任務需要，否則不要在桌面上放置終端機、執行對話方塊和啟動器。當其中之一取得鍵盤焦點時，`type`、`key` 和 `hold_key` 會執行 Claude 輸入的任何內容，而點擊可能會啟動程式。如果任務需要其中之一，請在鍵盤輸入和點擊之前詢問使用者，並隔離桌面，使指令的影響範圍不超出任務所需。
4. 拒絕您的桌面不應接受的輸入，例如螢幕截圖範圍外的點、長達數分鐘的 `wait`，或非常大的 `repeat` 或 `scroll_amount`。引發 `ToolError`（在 TypeScript 中則是拋出它），而不是將該值調整到範圍內。
5. 將螢幕上的所有內容視為不受信任的，包括視窗標題和剪貼簿文字。不要在未經檢查的情況下執行或傳遞它們。

SDK 透過您傳入的可呼叫物件套用步驟 2。其他步驟則取決於您的驅動程式和部署。[安全性考量](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool#security-considerations)下的預防措施也同樣適用。

以下的 `confirm` 會在任何鍵盤輸入之前，透過您自己的 `ask_user` (typescript: `askUser`) 函式詢問使用者，並核准其他所有呼叫。`context.member` (typescript: `ctx.member`) 是工具的名稱。若也要讓使用者核准點擊，請將 `ASK_FIRST` 設定為 `KEYBOARD | CLICKS` (typescript: `new Set([...KEYBOARD, ...CLICKS])`)。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  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)
  ```

  ```typescript TypeScript
  import type { BetaComputerConfirmContext } from "@anthropic-ai/sdk/helpers/beta/toolsets";

  const KEYBOARD = ["type", "key", "hold_key"];
  const CLICKS = [
    "left_click",
    "right_click",
    "middle_click",
    "double_click",
    "triple_click",
    "left_click_drag",
    "left_mouse_down",
    "left_mouse_up"
  ];
  const ASK_FIRST = new Set(KEYBOARD);

  async function confirm(ctx: BetaComputerConfirmContext): Promise<boolean> {
    if (!ASK_FIRST.has(ctx.member)) {
      return true;
    }
    // 此處顯示的是「Gate consequential members」中的函式。
    return askUser(`Allow the "${ctx.member}" tool with this input?\n${shown(ctx.input)}`);
  }

  const desktop = new MyDesktop(display, { confirm });
  ```
</CodeGroup>

被拒絕的呼叫會得到錯誤結果，而執行會繼續進行。對於被拒絕的 `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`），而工具執行器會將呼叫傳送到相符的實例。在呼叫失敗後，工具執行器只會略過該回合中對同一工具集的後續呼叫，因此失敗的電腦呼叫不會略過該回合的瀏覽器呼叫。

<CodeGroup exclude="shell:cURL, shell:CLI, csharp, go, java, php, ruby">
  ```python Python
  # 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())
  ```

  ```typescript TypeScript
  // MyBrowser、backend 與 urlPolicy 來自進階範例。
  // confirm 是「安全地執行 computer 工具集」中的函式。
  const browser = new MyBrowser(backend, { urlPolicy });
  const desktop = new MyDesktop(display, { confirm });
  try {
    const runner = client.beta.messages.toolRunner({
      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,
      runToolsEagerly: true // so a call can start before the response ends
    });
    for await (const stream of runner) {
      console.log(await stream.finalMessage());
    }
  } finally {
    await Promise.all([browser.close(), desktop.close()]);
  }
  ```
</CodeGroup>

在您自己撰寫的迴圈中，請使用 `toolset_name` (typescript: `toolsetName`) 與呼叫的 `toolset_name` 相符之實例上的 `tool_result` (typescript: `toolResult`) 來回應每個呼叫。在每個回合中，分別追蹤每個工具集的失敗呼叫，並以該工具集自己的文字回應被略過的呼叫。[不使用工具執行器執行](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#run-without-the-tool-runner)提供了瀏覽器的文字，而本節開頭的「被略過的呼叫」項目則提供了電腦的文字。

## 參考

建構函式選項在兩個 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` (typescript: `toolConfigs`)。

建構後您無法變更選項。[Python 瀏覽器工具集指南](https://github.com/anthropics/anthropic-sdk-python/blob/main/browser-toolset.md)、[TypeScript 瀏覽器工具集指南](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/browser-toolset.md)、[Python 電腦工具集指南](https://github.com/anthropics/anthropic-sdk-python/blob/main/computer-toolset.md)和 [TypeScript 電腦工具集指南](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/computer-toolset.md)說明了每個選項。Python 指南也涵蓋了非同步類別 `BetaAsyncAbstractBrowserToolset20260801` 和 `BetaAsyncAbstractComputerToolset20260801`。

## 限制

* **SDK 不包含瀏覽器、桌面、現成的驅動程式或 URL 政策：** 這些需由您提供。請參閱[合作夥伴整合](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#partner-integrations)、[實作驅動程式](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#implement-a-driver)和[撰寫 URL 政策](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#write-a-url-policy)。
* **URL 政策只會看到 `navigate` 呼叫：**&#x5B83;看不到網頁發出的請求，也看不到以其他方式開啟的網頁，例如從 Claude 點擊的連結或重新導向開啟的網頁。請參閱[在驅動程式中攔截請求](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#intercept-requests-in-the-driver)。
* **核准是根據上次的狀態報告：** 頁面可能在該報告之後發生變化。SDK 不會在呼叫執行之前再次檢查頁面。
* **同一個工具集上的呼叫一次執行一個：** 您無法關閉此行為。
* **SDK 不會檢查分頁 ID 是否唯一或分頁數量，也不一定會檢查是否只有一個分頁處於作用中：**&#x41;PI 會拒絕違反這些規則的報告。
* **對於電腦呼叫，`confirm` 接收的是工具及其輸入，而非螢幕畫面：**&#x60A8;無法從中判斷在特定點的點擊會產生什麼效果。
* **SDK 不會調整電腦工具集圖片的大小：**&#x41;PI 會拒絕超出模型限制的圖片。請參閱[縮放座標並調整圖片大小](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-sdk#scale-coordinates-and-size-images)。

## 後續步驟

<CardGroup cols={2}>
  <Card title="瀏覽器使用工具" icon="browser" href="https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/browser-use-tool">
    成員工具、`browser_state` 區塊，以及該工具的安全性考量。
  </Card>

  <Card title="電腦使用工具" icon="computer" href="https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/computer-use-tool">
    17 個工具、座標與圖片限制，以及該工具的安全性考量。
  </Card>

  <Card title="工具執行器（SDK）" icon="arrows-clockwise" href="https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/tool-runner">
    SDK 如何執行迴圈，以及如何變更它傳送的訊息。
  </Card>

  <Card title="緩解越獄與提示注入" icon="shield" href="https://platform.claude.com/docs/zh-TW/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    適用於任何讀取不受信任內容之應用程式的防護措施。
  </Card>
</CardGroup>
