Claude Platform Docs
СообщенияИнструменты

Использование браузера и компьютера с наборами инструментов SDK

Запускайте инструмент использования браузера или инструмент использования компьютера из Python или TypeScript SDK. SDK выполняет цикл и настроенные вами проверки, а браузер или рабочий стол предоставляете вы.

С наборами инструментов браузера и компьютера в Python и TypeScript SDK вы можете создать агента, который работает в веб-браузере или на рабочем столе. Он может пользоваться приложениями, у которых нет программного интерфейса. С помощью инструмента использования браузера Claude переходит по веб-страницам, читает их и взаимодействует с ними. С помощью инструмента использования компьютера Claude видит рабочий стол на снимках экрана и пользуется мышью и клавиатурой.

SDK связывает Claude с вашим браузером или рабочим столом. Он выполняет настроенные вами проверки, например «URL policy» (политику URL) для navigate или обратный вызов подтверждения. Каждый вызов, прошедший проверки, он передаёт вашему «driver» (драйверу) и формирует tool_result из того, что возвращает драйвер. «Tool runner» (исполнитель инструментов) может выполнять «agentic loop» (агентный цикл) за вас.

Драйвер, который управляет браузером или рабочим столом, предоставляете вы. Начните с драйвера от провайдера. С ним агент занимает несколько строк кода. Если ни один не подходит, напишите собственный с помощью библиотеки автоматизации браузера, например Playwright.

Большая часть этой страницы посвящена набору инструментов браузера. В разделе Набор инструментов компьютера описано, чем он отличается.

Написание собственного драйвера — продвинутый сценарий, описанный в разделе Реализация драйвера. В репозитории claude-quickstarts также есть пример драйвера браузера на Python и TypeScript. Он управляет Chromium через Chrome DevTools Protocol (CDP) и не является кодом для продакшена.

Использование драйвера от провайдера

С драйвером от провайдера, например одного из перечисленных в разделе Интеграции партнёров, вы можете создать агента в несколько строк кода. Провайдер в этом примере вымышленный. Замените 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

Драйверы E2B для наборов инструментов браузера и компьютера.

Наши партнёры также публикуют код на GitHub: быстрый старт Browser Use, набор инструментов Browserbase и пример E2B.

Настройка драйвера

В примерах этого раздела используется MyBrowser — драйвер из продвинутого примера далее на этой странице.

Включение и отключение членов

configs принимает настройки для каждого члена, описанные в разделе Настройка набора инструментов. Перечислите только те члены, которые вы меняете:

# MyBrowser, который также реализует read_console
browser = MyBrowser(
    backend, configs={"read_console": {"enabled": True}, "navigate": {"enabled": False}}
)

SDK отклоняет вызов отключённого члена до того, как выполнится ваш код. Включение члена, которого ваш класс не реализует, является ошибкой конфигурации, если только класс не переопределяет execute.

Добавление хуков до и после

Переопределите execute и вызовите execute родителя. Код перед этим вызовом выполняется после политики URL, политики файлов и confirm, и он может изменить входные данные. SDK не проверяет изменённые входные данные повторно. Код после вызова получает результат и может его изменить. Вызовите исключение ToolError (в TypeScript — через throw), чтобы отклонить вызов.

Переопределение 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 меняет набор членов, отправляемых как отключённые (Добавление хуков до и после). Каждый член получает контекст вызова и входные данные члена в виде типизированного объекта, например BetaBrowserNavigateInput. Типы входных данных берутся из anthropic.types.beta. В разделе Инструменты-члены перечислены поля каждого типа входных данных.

В TypeScript пишите члены как методы, а не как поля со стрелочными функциями, потому что SDK находит их в прототипе. В TypeScript член type пишется как type_. В Python — type.

Возврат результатов

То, что возвращает член, определяет, что прочитает Claude. Успешный результат заканчивается блоком browser_state, построенным из вашего отчёта о состоянии. Результат с ошибкой не содержит этого блока.

ЧленВозвращаетClaude читает
screenshot, zoomBetaScreenshotResultОдин блок изображения
navigateBetaBrowserNavigateResultNavigated 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 сообщает о них Claude текстом вне блока browser_state: одной строкой для всех отклонённых навигаций, без URL, и по строке для каждого из первых трёх закрытых диалогов, а затем количеством остальных.

Когда открыта хотя бы одна вкладка, ровно одна должна быть активной. Каждый член, принимающий tab_id, должен действовать на указанную вкладку. Ограничения API на отчёт перечислены в разделе Отслеживание вкладок с помощью browser_state.

Claude читает URL каждой вкладки, URL каждой загрузки и URL, который возвращает ваш navigate, в том виде, в каком их записал ваш драйвер. SDK не разбирает эти URL. Он заменяет переводы строк и другие управляющие символы пробелами, обрезает пробелы по краям и обрезает каждый URL до 4 096 символов. Поэтому содержимое URL data:, путь URL file:, а также имя пользователя или пароль в URL — всё это доходит до Claude.

Обработка ошибок

Вызвано членом или SDKClaude читаетВыполнение
ToolErrorЕго сообщение в виде результата с ошибкойПродолжается
ToolsetUsageErrorНичегоОстанавливается
Любое другое исключениеClassName: message в Python или Error: message в TypeScript в виде результата с ошибкойПродолжается

SDK вызывает ToolsetUsageError при ошибке конфигурации, неправильном использовании SDK во время вызова или вызове после close. Он также вызывает его, когда _browser_state вызывает любое исключение, даже ToolError. Исполнитель инструментов и tool_result не перехватывают его, поэтому оно доходит до вызывающего их кода.

SDK не скрывает локальные пути в тексте ошибки члена, в строке, которую возвращает действие вроде left_click, в ошибке неудавшейся загрузки или в сообщении закрытого диалога.

ToolError из вашей политики URL, политики файлов или вызываемого объекта confirm доходит до Claude в исходном виде, поэтому не включайте в его текст локальные пути. Перехватывайте исключения в своих членах и вызывайте ToolError (в TypeScript — через throw) с собственным текстом.

Запуск вызовов во время потоковой передачи ответа

По умолчанию исполнитель инструментов выполняет вызовы хода после завершения ответа Claude. С ранним запуском он может начать вызов браузера или компьютера, пока ответ ещё передаётся потоком.

Чтобы включить его, передайте исполнителю инструментов stream=True и run_tools_eagerly=True, как в продвинутом примере. С stream каждая итерация вашего цикла по исполнителю даёт вам поток вместо сообщения.

Ранний запуск меняет то, когда вызов может начаться, но не то, как набор инструментов его выполняет:

  • Проверки по-прежнему выполняются первыми: SDK вызывает вашу политику URL, политику файлов и confirm до выполнения вызова. С ранним запуском он может вызвать confirm, пока ответ ещё передаётся потоком.
  • Вызовы по-прежнему выполняются по одному: Набор инструментов выполняет свои вызовы в том порядке, в котором их написал Claude. Неудавшийся вызов по-прежнему останавливает последующие вызовы этого набора инструментов в ходе.
  • Начатый вызов нельзя отменить: Если ответ затем обрывается, например на max_tokens, или ваш цикл останавливается раньше, действие всё равно происходит, а Claude так и не прочитает его результат.

Запуск без исполнителя инструментов

Передайте экземпляр в tools (browser.toJSON() в TypeScript) и отвечайте на каждый вызов члена с помощью 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, который ваша политика URL
        # разрешила, в том виде, как его написал Claude. Как и is_allowed, backend.goto добавляет https://
        # к URL, который не начинается со схемы.
        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:
    # Это пример, а не политика для продакшена. См. «Написание политики URL».
    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 зависит от страниц, которые он читает. Страница или внедрённый в неё текст может попытаться получить доступ к внутренним сервисам или вытащить файлы с хоста. Она также может попытаться запустить действия с реальными последствиями. Прежде чем запускать драйвер с чем-либо, кроме одноразового браузера, выполните эти шесть шагов:

  1. Напишите политику URL, которая отклоняет каждый URL navigate, не нужный для задачи.
  2. Перехватывайте запросы в драйвере и проверяйте каждый запрос по тем же правилам.
  3. Примените «egress policy» (политику исходящего трафика) к контейнеру, чтобы сеть блокировала то, чего не видит драйвер.
  4. Ограничьте загрузки и скачивания или оставьте загрузки отключёнными.
  5. Контролируйте значимые члены с помощью confirm.
  6. Изолируйте хост браузера в выделенном контейнере либо в «virtual machine» (виртуальной машине), или VM, для каждого сеанса.

SDK применяет шаги 1, 4 и 5 через переданные вами политики и вызываемый объект. Шаги 2, 3 и 6 зависят от вашего драйвера и вашего развёртывания. Меры предосторожности из раздела Соображения безопасности также применяются.

Написание политики URL

«URL policy» (политика URL) — это функция, которую вы передаёте как url_policy. Вы пишете политику и отвечаете за её готовность к продакшену. Перед выполнением вашего navigate SDK вызывает политику с контекстом вызова и URL в том виде, в каком его написал Claude.

Ничего не возвращайте, чтобы разрешить URL. Чтобы отклонить его, вызовите ToolError (в TypeScript — через throw). Claude читает сообщение ошибки, и navigate не выполняется. Политика проверяет только URL в каждом вызове navigate. Она не является средством сетевого контроля.

Это политика из продвинутого примера. Она разрешает 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 может опустить схему, как в example.com, поэтому перед чтением хоста пример добавляет https:// к URL, который не начинается со схемы. Ваш navigate получает URL в том виде, в каком его написал Claude. Добавьте схему там с той же проверкой, чтобы браузер открыл именно тот URL, который проверила ваша политика.

Пример не готов для продакшена. Политике для продакшена нужно гораздо больше, в таких областях, как:

  • Ссылки, по которым кликает Claude, формы, которые он отправляет, перенаправления и запросы, которые страница делает сама. Их видит только ваш драйвер. См. Перехват запросов в драйвере.
  • Имена хостов и URL, взятые из содержимого страницы, которые может выбрать злоумышленник.
  • Разбор каждого URL так же, как это делает браузер, а не поиск имени хоста простым разбиением строки.
  • Схемы URL. SDK их не проверяет, поэтому отклоняйте каждую схему, которую ваш драйвер не собирается открывать.
  • Правила исходящего трафика на контейнере для того, что не видит ни одна проверка URL. См. Применение политики исходящего трафика к контейнеру.

Если вы передадите url_policy=None, SDK отклонит каждый navigate на URL, поэтому None, прочитанный из вашей конфигурации, не может случайно оставить URL непроверенными. В TypeScript undefined оставляет параметр незаданным — так же, как если бы вы его не передали.

Перехват запросов в драйвере

Политика URL оценивает только URL в вызове navigate. Ссылка, по которой кликает Claude, отправляемая им форма, перенаправление или изображение либо скрипт на разрешённой странице всё ещё могут достучаться до хоста, который политика отклонила бы. Используйте правила исходящего трафика, чтобы заблокировать такие хосты.

Драйвер с перехватом запросов также может проверять эти запросы в своём хуке запросов — функции, которую ваша библиотека автоматизации вызывает перед каждым запросом. Вызывайте функцию, которую использует ваша политика, чтобы писать правила один раз:

class MyBrowser(BetaAbstractBrowserToolset20260801):
    ...

    # Регистрируется в контексте браузера Playwright через context.route("**/*", self._guard)
    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 и шаги перенаправлений с помощью хука, который их видит. Политика-пример отклоняет URL ws:// и wss://, поэтому замените ws:// на http:// и wss:// на https://, прежде чем передавать URL WebSocket в is_allowed.

Применение политики исходящего трафика к контейнеру

Перехват не может увидеть каждый запрос, который делает браузер, а политика URL не видит, во что разрешается имя. Политика исходящего трафика, которую обеспечивает сеть контейнера, покрывает и то, и другое:

  • Блокируйте link-local и частные диапазоны адресов в 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

# MyBrowser, который также реализует file_upload
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 разрешает каждый путь загрузки, следуя по символическим ссылкам, и отклоняет любой путь вне корней загрузки. Политика файлов вызывает ошибку конфигурации для каталога скачивания, который является корнем загрузки, находится внутри него или содержит его. Эта проверка может пропустить символическую ссылку, поэтому не допускайте, чтобы ссылка делала каталог скачивания тем же, что и корень загрузки, или помещала один внутрь другого. SDK включает path скачивания в отчёт о состоянии, только когда expose_download_paths имеет значение true и файл находится внутри каталога скачивания.

Поставляемая политика файлов проверяет пути в файловой системе процесса, в котором работает SDK. Она защищает только браузер, использующий ту же файловую систему. Для удалённого браузера следуйте разделу Удалённые и размещённые браузеры.

Настройте скачивания следующим образом:

  • Создайте каталог скачивания самостоятельно с режимом 0700 и смонтируйте его с noexec,nosuid,nodev.
  • Держите каталог вне досягаемости других инструментов, которые может вызывать Claude, например оболочки или файлового инструмента.
  • В изменении состояния download_failed записывайте error как фиксированную фразу. Текст исключения может содержать путь или URL.
  • Не считывайте скачанный файл в разговор и не запускайте его, пока это не решит человек.

Контроль значимых членов

javascript_exec и file_upload по умолчанию отключены. Если вы включите любой из них без вызываемого объекта confirm, конструктор вызовет ошибку конфигурации. С вызываемым объектом confirm SDK вызывает его перед каждым вызовом, который вот-вот выполнится. Без него ничего не запрашивается. В TypeScript confirm: null не вызывает ошибку конфигурации, и SDK отклоняет каждый вызов любого члена.

Верните True, чтобы выполнить вызов, или False, чтобы отклонить его. Когда ваш вызываемый объект спрашивает человека, покажите ему член, URL страницы и входные данные вызова. Сначала экранируйте каждый символ вне печатаемого ASCII в URL и входных данных, потому что и то, и другое может содержать текст со страницы.

Этот пример спрашивает о двух защищённых членах через вашу собственную функцию 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


# MyBrowser, который также реализует javascript_exec и file_upload
# Для file_upload также нужна 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, если правила исходящего трафика не ограничивают исходящие соединения браузера хостами, нужными для задачи. Скрипт может отправить содержимое страницы на любой адрес, доступный браузеру.

URL javascript: в вызове navigate также выполняет скрипт на текущей странице, даже когда javascript_exec отключён. confirm видит этот вызов как navigate, поэтому вызываемый объект confirm из примера одобрил бы его. Ваш драйвер должен отклонять URL javascript:. В примере политика URL отклоняет их до выполнения confirm.

Изоляция хоста браузера

Запускайте браузер в выделенном контейнере или VM с минимальными привилегиями для каждого сеанса:

  • Запускайте от имени пользователя без прав root, с корневой файловой системой только для чтения, если браузер это допускает.
  • Не монтируйте с хоста ничего, кроме настроенных вами каталогов загрузки и скачивания, если они есть.
  • Не допускайте учётных данных в окружении и начинайте со свежего профиля браузера.
  • Не используйте общую файловую систему с другими инструментами, которые может вызывать Claude.

Запускайте код, который вызывает API, вне контейнера браузера, потому что этот код хранит ваш ключ API и разговор. Исполнитель инструментов и tool_result оба выполняют набор инструментов в процессе этого кода, поэтому браузер не использует файловую систему набора инструментов. Считайте браузер удалённым: применяется раздел Удалённые и размещённые браузеры.

Считайте недоверенным всё, что возвращает страница, включая текст страницы, снимки экрана, записи консоли и сети, заголовки вкладок и имена скачанных файлов.

Удалённые и размещённые браузеры

Некоторые браузеры не используют общую файловую систему с процессом, который запускает SDK. Примеры — браузер в другом контейнере, браузер, к которому вы обращаетесь по URL DevTools, и браузер из сервиса размещённых браузеров. В этих случаях политика URL, перехват запросов и confirm по-прежнему выполняются в вашем процессе.

В случае размещённого браузера исходящий трафик и изоляцию хоста контролирует провайдер. Ваша собственная политика исходящего трафика там не применяется, поэтому хук запросов драйвера — ваша единственная проверка запросов браузера. Выясните, к чему может получить доступ сеть браузера.

Поставляемые проверки путей не защищают удалённый браузер. BetaLocalFilePolicy проверяет пути в файловой системе процесса, в котором работает SDK, а браузер читает и пишет в собственную файловую систему.

SDK не может определить, что браузер удалённый. Поэтому для удалённого браузера держите file_upload отключённым, если только ваш драйвер не проверяет пути загрузки там, где работает браузер, с помощью собственной BetaFilePolicy. BetaFilePolicy проверяет пути и ID документов каждой загрузки и решает, видит ли Claude путь скачанного файла.

Настройте удалённый браузер так, чтобы он отклонял скачивания, если только на его собственном хосте нет настройки скачиваний из раздела Ограничение загрузок и скачиваний.

Не допускайте попадания ключа API провайдера и URL подключения сеанса, который может содержать ключ, в журналы, результаты инструментов и текст ошибок. Если провайдер записывает сеансы, запись является ещё одной копией всего, что Claude видел и вводил, и к ней применяются условия хранения данных провайдера.

Набор инструментов компьютера

BetaAbstractComputerToolset20260801 — это класс для инструмента использования компьютера. Драйвер — это подкласс с одним методом на каждый инструмент, например screenshot или left_click. Начните с драйвера партнёра из раздела Интеграции партнёров. Чтобы написать собственный, воспользуйтесь минимальным примером VNC на Python и TypeScript в репозитории claude-quickstarts. Он управляет рабочим столом через 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 — через throw) с собственным текстом.

Написание драйвера рабочего стола

Этот драйвер реализует пять из 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, а также оба угла region в zoom. 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)

Вызывайте to_display для каждой coordinate и start_coordinate, прежде чем выполнять действие, и to_screenshot для позиции, которую сообщает cursor_position. Щелчок или прокрутка без coordinate выполняется в позиции курсора, поэтому преобразовывать нечего. Масштабируйте region в zoom с тем же коэффициентом. Изображение zoom этого не меняет: следующие точки, которые отправляет Claude, по-прежнему являются позициями в пикселях на полном скриншоте.

SDK никогда не читает и не изменяет размер изображений. API отклоняет изображение, превышающее ограничения модели, что завершает выполнение. Масштабируйте каждый скриншот и изображение zoom в драйвере. См. Подбор размера снимков экрана под ограничения изображений. При длительном выполнении один запрос может содержать более 20 изображений, и тогда API применяет к каждому из них более строгое ограничение. См. Управление историей скриншотов.

Проверка входных данных и результаты

Не полагайтесь на SDK в проверке входных данных вызова. Пусть ваш драйвер проверяет используемые им поля и отклоняет вызов, который он не может выполнить: вызовите исключение ToolError (в TypeScript — выбросьте его через throw). Claude прочитает сообщение и сможет отправить исправленный вызов.

Ни один из SDK не ограничивает duration, количество repeat или scroll_amount. Они рассматриваются в шаге 4 раздела Безопасный запуск набора инструментов компьютера.

То, что возвращает инструмент, определяет, что прочитает Claude:

ИнструментВозвращаетClaude читает
screenshot, zoomBetaScreenshotResultОдин блок изображения
cursor_positionBetaComputerCursorPositionResultX={x},Y={y}
Любой другой инструментНичего или одну строку текстаКраткое подтверждение, например Clicked., а затем возвращённую строку в отдельном текстовом блоке

Безопасный запуск набора инструментов компьютера

То, что находится на экране, направляет следующее действие Claude. Страница или уведомление могут попытаться заставить Claude ввести текст не в то окно или запустить действие с реальными последствиями.

Прежде чем запускать драйвер на чём-либо, кроме одноразовой машины, выполните следующие пять шагов:

  1. Изолируйте рабочий стол. Запускайте его в выделенном контейнере или виртуальной машине с минимальными привилегиями для каждого сеанса, без учётных данных и без подключений хоста. Устанавливайте только приложения, необходимые для задачи. Код, вызывающий API, запускайте вне этой среды.
  2. Ограничивайте действия с реальными последствиями с помощью confirm. Покупка или отправленное сообщение — это обычный left_click, type или key, а confirm получает инструмент и его входные данные, а не экран. Поэтому выбирайте, какие инструменты требуют одобрения, исходя из приложений, к которым Claude может получить доступ на рабочем столе.
  3. Не держите на рабочем столе терминалы, диалоги запуска и средства запуска приложений, если они не нужны для задачи. Когда один из них находится в фокусе клавиатуры, type, key и hold_key выполняют всё, что вводит Claude, а щелчок может запустить программу. Если задача требует такого средства, спрашивайте человека перед вводом с клавиатуры и щелчками и изолируйте рабочий стол так, чтобы команда не могла выйти за пределы того, что нужно для задачи.
  4. Отклоняйте входные данные, которые ваш рабочий стол не должен принимать, например точку за пределами скриншота, wait длительностью в несколько минут или очень большое значение repeat или scroll_amount. Вызывайте ToolError (в TypeScript — выбрасывайте через throw) вместо того, чтобы приводить значение к допустимому диапазону.
  5. Считайте всё на экране недоверенным, включая заголовки окон и текст буфера обмена. Не выполняйте это и не передавайте дальше без проверки.

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 — это функция из раздела «Run the computer toolset safely».
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())

В собственном цикле отвечайте на каждый вызов с помощью tool_result на экземпляре, чей toolset_name совпадает с toolset_name вызова. В каждом ходе отслеживайте неудачные вызовы для каждого набора инструментов отдельно и отвечайте на пропущенный вызов собственным текстом этого набора инструментов. Текст для браузера приведён в разделе Запуск без исполнителя инструментов, а текст для компьютера — в пункте о пропущенных вызовах в начале этого раздела.

Справочник

Параметры конструктора имеют одинаковое значение в обоих SDK:

PythonTypeScriptЗадаёт
configsconfigsКакие члены включены
confirmconfirmВызываемый объект, который одобряет или отклоняет каждый вызов
url_policyurlPolicyВашу политику URL, которую SDK вызывает перед каждым navigate на URL
file_policyfilePolicyКорневые каталоги для загрузки файлов и раскрытие пути скачивания
tool_configstoolConfigsПоля для записи tools, например cache_control
Метод _browser_statebrowserStateОтчёт о состоянии

Класс браузера принимает все параметры из таблицы. Класс компьютера принимает configs, confirm и tool_configs.

Изменить параметр после создания экземпляра нельзя. Каждый параметр описан в руководстве по набору инструментов браузера для Python, руководстве по набору инструментов браузера для TypeScript, руководстве по набору инструментов компьютера для Python и руководстве по набору инструментов компьютера для TypeScript. Руководства для Python также охватывают асинхронные классы BetaAsyncAbstractBrowserToolset20260801 и BetaAsyncAbstractComputerToolset20260801.

Ограничения

  • SDK не включает браузер, рабочий стол, готовый драйвер или политику URL: Их предоставляете вы. См. разделы Интеграции партнёров, Реализация драйвера и Написание политики URL.
  • Политика URL видит только вызовы navigate: Она не видит запросы, которые делает страница, или страницы, открывающиеся любым другим способом, например по ссылке, на которую щёлкает Claude, или через перенаправление. См. Перехват запросов в драйвере.
  • Одобрение основано на последнем отчёте о состоянии: Страница может измениться после этого отчёта. SDK не проверяет страницу повторно перед выполнением вызова.
  • Вызовы в одном наборе инструментов выполняются по одному: Это нельзя отключить.
  • SDK не проверяет уникальность идентификаторов вкладок и их количество, а также не всегда проверяет, что активна ровно одна вкладка: API отклоняет отчёт, нарушающий эти правила.
  • Для вызова компьютера confirm получает инструмент и его входные данные, а не экран: По ним нельзя определить, что делает щелчок в заданной точке.
  • SDK не изменяет размер изображений набора инструментов компьютера: API отклоняет изображение, превышающее ограничения модели. См. Масштабирование координат и размер изображений.

Следующие шаги

Инструменты-члены, блок browser_state и соображения безопасности для этого инструмента.

17 инструментов, координаты и ограничения на изображения, а также соображения безопасности для этого инструмента.

Как SDK выполняет цикл и как изменить отправляемые им сообщения.

Защитные механизмы для любого приложения, которое читает недоверенный контент.

Was this page helpful?