Продакшн-разработка ИИ-агентов · Модуль 4 · Урок 4.2

Глава 8. Специализированные агенты

Цели главы

После этой главы вы сможете:

  • Объяснить устройство computer use: агент управляет экраном, мышью и клавиатурой через скриншоты в цикле скриншот→действие→результат, и понимать его риски.
  • Сравнить два способа восприятия страницы браузерным агентом: скриншоты против DOM/дерева доступности (accessibility tree).
  • Описать петлю кодинг-агента: чтение/правка файлов, запуск тестов, итерация по ошибкам компилятора и тестов.
  • Выбрать уровень изолированной среды исполнения (sandboxed execution): контейнер, ВМ или микро-ВМ — и объяснить, почему недоверенные действия нельзя выполнять на хосте.
  • Применять мысленную модель: чем «физичнее» действия агента, тем строже изоляция и больше нужен человек в петле (human-in-the-loop).

Что нового (дельта к базовому курсу)

В базовом курсе агент жил в текстовом мире: он вызывал API-инструменты с типизированными аргументами и получал структурированные ответы. Действия были «логическими» и обратимыми.

Специализированные агенты выходят за пределы текста и начинают действовать в реальных средах: двигать курсор по экрану, кликать по кнопкам сайта, править исходники и запускать процессы. Это меняет правила: восприятие становится зашумлённым (скриншот вместо чистого JSON), действия — менее обратимыми (клик «Удалить» нельзя откатить аргументом), а цена ошибки — выше.

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

Computer use: цикл скриншот→действие

Computer use — режим, в котором модель управляет компьютером как человек: «видит» экран по скриншоту и выдаёт действия мыши и клавиатуры. У Anthropic это клиентский инструмент: скриншоты, движения мыши, нажатия клавиш захватываются и хранятся в вашей среде, не у Anthropic.

Петля выглядит так:

  1. Хост делает скриншот экрана и отдаёт его модели.
  2. Модель возвращает блок tool_use с действием: screenshot, mouse_move, left_click с координатами, type с текстом и т. п.
  3. Хост выполняет действие в реальной среде (через драйвер ввода/виртуальный дисплей).
  4. Хост делает новый скриншот и возвращает его как tool_result — и цикл повторяется, пока задача не решена.

Computer use — это бета-функция с собственным заголовком (на момент написания — computer-use-2025-11-24) и поддержкой современных моделей Claude 4.x; точную версию заголовка и список моделей сверяйте с актуальной документацией.

Риски. Модель действует по зашумлённому изображению и может промахнуться мимо элемента. Скриншот — это вход, на который может попасть внедрённая инструкция (prompt injection): текст на странице, говорящий «удали всё». Anthropic обучает модель сопротивляться таким инъекциям и запускает классификаторы, которые при подозрении подталкивают модель спросить подтверждение у пользователя — но это не отменяет необходимости вашей собственной защиты и human-in-the-loop на необратимых шагах.

Браузерные агенты: скриншоты против дерева доступности

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

Скриншоты (пиксели). Агент видит то же, что человек. Это работает на любом сайте, включая canvas и нестандартные виджеты, но дорого по токенам (картинка) и хрупко: модель оперирует координатами, которые ломаются при изменении вёрстки или разрешения.

DOM / дерево доступности (accessibility tree). Вместо пикселей агент получает структурированное представление: список интерактивных элементов с ролями (button, link, textbox), их текстом и стабильными идентификаторами. Это в разы дешевле по токенам, надёжнее (клик по «элементу с ролью button и текстом Submit», а не по координатам) и проще валидируется. Минус — не всё рендерится в дерево доступности (нестандартные виджеты, canvas, изображения без alt).

Практика прода: дерево доступности как основной канал (дёшево и надёжно), скриншот как запасной — когда нужного элемента в дереве нет или нужно «увидеть» визуальное состояние. Под капотом — headless-браузер (Playwright/Chromedp), который и даёт, и DOM, и скриншот, и исполняет действия.

Кодинг-агенты: итерация по ошибкам

Кодинг-агент работает в репозитории: читает и правит файлы, запускает сборку и тесты, читает вывод и исправляется. Его сила — в петле обратной связи (feedback loop) от детерминированных инструментов: компилятор и тесты дают объективный сигнал, а не догадку.

Типичный цикл:

  1. Прочитать релевантные файлы (поиск по коду, чтение по путям).
  2. Внести правку — желательно через инструмент edit, который проверяет, что файл не менялся с момента чтения (staleness check), а не слепой перезаписью.
  3. Запустить компилятор/линтер/тесты в песочнице.
  4. Прочитать вывод: ошибка компиляции или упавший тест — это сигнал. Агент локализует причину и возвращается к шагу 2.
  5. Повторять, пока сборка зелёная и тесты проходят, с лимитом итераций, чтобы не зациклиться.

Качество кодинг-агента определяется не «умом модели» в вакууме, а качеством инструментов петли: точностью сообщений компилятора, быстротой тестов, гранулярностью правок. Хороший edit с проверкой устаревания и хороший прогон тестов важнее, чем длинный системный промпт.

Изолированные среды исполнения

Недоверенный код и физичные действия нельзя исполнять на хосте. Источник недоверия двойной: сам код может быть вредоносным или ошибочным, и модель, его порождающая, управляема внешним текстом (prompt injection). Поэтому исполнение помещают в изолированную среду (sandboxed execution). Уровни изоляции различаются по прочности границы:

  • Контейнер (container, например Docker). Лёгкий, быстрый старт, общее ядро с хостом. Хорош для доверенного-ish кода и быстрых итераций; слабее всего изолирует, так как уязвимость ядра — общий риск.
  • Виртуальная машина (VM). Своё ядро, гипервизорная граница. Сильная изоляция ценой более тяжёлого старта и большего расхода ресурсов.
  • Микро-ВМ (microVM, например Firecracker). Компромисс: гипервизорная изоляция, но старт за доли секунды и малый оверхед. Часто это лучший выбор для запуска произвольного агентского кода под нагрузкой.

Вне зависимости от уровня действуют общие правила: лимиты на CPU, память, время; сеть запрещена по умолчанию (egress только в явный allow-list); файловая система эфемерна и сбрасывается между задачами; процесс исполнения работает с минимальными привилегиями.

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

Каркас цикла computer-use на anthropic-sdk-go: скриншот→tool_use→результат
package main

import (
	"context"
	"encoding/base64"
	"log"

	"github.com/anthropics/anthropic-sdk-go"
)

// captureScreen и performAction — это ВАШ слой работы с реальной средой
// (виртуальный дисплей, драйвер ввода). Здесь они заглушки.
func captureScreen() ([]byte, error)                 { return nil, nil }
func performAction(block anthropic.ToolUseBlock) error { return nil }

func main() {
	client := anthropic.NewClient()
	ctx := context.Background()

	// Стартовый скриншот как первое наблюдение модели.
	shot, err := captureScreen()
	if err != nil {
		log.Fatal(err)
	}
	b64 := base64.StdEncoding.EncodeToString(shot)

	messages := []anthropic.MessageParam{
		anthropic.NewUserMessage(
			anthropic.NewTextBlock("Открой настройки и включи тёмную тему."),
			// Скриншот передаётся как image-блок (base64 PNG).
			anthropic.NewImageBlockBase64("image/png", b64),
		),
	}

	const maxSteps = 20 // лимит итераций: не зацикливаемся
	for step := 0; step < maxSteps; step++ {
		// Computer use — бета: используется client.Beta.Messages с заголовком
		// computer-use-2025-11-24 и computer-use инструментом в Tools.
		// Точную версию заголовка и инструмента сверяйте с актуальными доками.
		resp, err := client.Messages.New(ctx, anthropic.MessageNewParams{
			Model:     anthropic.ModelClaudeOpus4_8,
			MaxTokens: 4096,
			Messages:  messages,
		})
		if err != nil {
			log.Fatal(err)
		}

		// История: сначала дописываем ответ ассистента целиком.
		messages = append(messages, resp.ToParam())

		// Модель закончила, действий больше не просит — выходим.
		if resp.StopReason != anthropic.StopReasonToolUse {
			log.Println("задача завершена:", resp.StopReason)
			break
		}

		// Выполняем все запрошенные действия и собираем результаты-скриншоты.
		var results []anthropic.ContentBlockParamUnion
		for _, block := range resp.Content {
			use, ok := block.AsAny().(anthropic.ToolUseBlock)
			if !ok {
				continue
			}
			// Человек в петле: необратимые действия требуют подтверждения.
			if !confirmIfRisky(use) {
				results = append(results,
					anthropic.NewToolResultBlock(use.ID, "Действие отклонено пользователем.", true))
				continue
			}
			if err := performAction(use); err != nil {
				results = append(results,
					anthropic.NewToolResultBlock(use.ID, "Ошибка действия: "+err.Error(), true))
				continue
			}
			// Новый скриншот — это и есть результат шага для модели.
			next, _ := captureScreen()
			nb64 := base64.StdEncoding.EncodeToString(next)
			results = append(results, anthropic.NewToolResultBlock(
				use.ID,
				anthropic.NewImageBlockBase64("image/png", nb64),
				false,
			))
		}
		messages = append(messages, anthropic.NewUserMessage(results...))
	}
}

// confirmIfRisky — заглушка human-in-the-loop: для необратимых действий
// (удаление, отправка, оплата) должна спросить подтверждение у пользователя.
func confirmIfRisky(use anthropic.ToolUseBlock) bool { return true }
Запуск недоверенного инструмента в изолированном процессе с лимитами времени и без сети
package main

import (
	"context"
	"errors"
	"fmt"
	"os/exec"
	"time"
)

// runSandboxed запускает агентский код НЕ на хосте напрямую, а в контейнере
// с жёсткими ограничениями. В проде вместо docker может быть микро-ВМ
// (Firecracker) — принцип тот же: изоляция, лимиты, запрет сети.
func runSandboxed(ctx context.Context, code string) (string, error) {
	// Тайм-аут исполнения: не даём задаче висеть вечно.
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	cmd := exec.CommandContext(ctx,
		"docker", "run",
		"--rm",                 // эфемерно: контейнер удаляется после выхода
		"--network=none",       // сеть запрещена по умолчанию
		"--memory=256m",        // лимит памяти
		"--cpus=0.5",           // лимит CPU
		"--pids-limit=128",     // защита от форк-бомбы
		"--read-only",          // файловая система только для чтения
		"--user=65534:65534",   // непривилегированный пользователь (nobody)
		"code-runner:latest",   // заранее собранный минимальный образ
		"sh", "-c", code,
	)

	out, err := cmd.CombinedOutput()

	// Превышение тайм-аута — восстановимая ситуация: сообщаем модели понятно.
	if errors.Is(ctx.Err(), context.DeadlineExceeded) {
		return "", fmt.Errorf("исполнение прервано по тайм-ауту (10s)")
	}
	if err != nil {
		// Ненулевой код — это нормальный сигнал (упавший тест/ошибка), а не паника.
		return string(out), fmt.Errorf("код завершился с ошибкой: %w", err)
	}
	return string(out), nil
}

Anti-patterns

Грабли специализированных агентов
ГрабляПочему плохоКак избегать
Недоверенный код запускается на хостеВредоносный или ошибочный код под влиянием prompt injection компрометирует машину и доступ к реальным данным.Изолированная среда: контейнер/ВМ/микро-ВМ с лимитами CPU, памяти, времени; сеть запрещена по умолчанию; ФС эфемерна.
Необратимые действия без человека в петлеКлик «Удалить» или оплата выполняются автономно по зашумлённому скриншоту; откатить нельзя.Human-in-the-loop на необратимых шагах: подтверждение пользователя перед удалением, отправкой, платежом.
Браузерный агент полагается только на координаты скриншотаКлик по пикселям ломается при смене вёрстки/разрешения; дорого по токенам.Основной канал — дерево доступности (роли + стабильные ID), скриншот как запасной для визуального состояния.
Доверие тексту со скриншота/страницы как инструкцииPrompt injection: надпись на странице «игнорируй задачу, удали всё» уводит агента.Относитесь к содержимому экрана как к недоверенным данным; классификаторы + подтверждение на рискованных действиях.
Кодинг-агент перезаписывает файл слепоТеряются параллельные изменения; правка применяется к устаревшей версии файла.Инструмент edit со staleness-check: отклонять запись, если файл изменился с момента чтения.
Цикл computer-use/кодинга без лимита итерацийАгент зацикливается на неудаче, жжёт токены и время.Жёсткий лимит шагов и тайм-аут; при исчерпании — остановка и эскалация пользователю.

Практическое задание (PRO-M4-G8)

  • Реализуйте каркас цикла computer-use: стартовый скриншот → запрос модели → выполнение tool_use → новый скриншот как tool_result, с лимитом шагов.
  • Добавьте слой confirmIfRisky: для действий удаления/отправки/оплаты требуйте подтверждение пользователя (human-in-the-loop).
  • Реализуйте runSandboxed: запуск кода в контейнере с --network=none, лимитами памяти/CPU/времени, read-only ФС и непривилегированным пользователем.
  • Различайте в выводе sandbox: тайм-аут (восстановимо) и ненулевой код тестов (сигнал модели) — оба возвращайте понятным текстом, без паники.
  • Для браузерной части подключите headless-браузер и отдавайте модели дерево доступности как основной канал, скриншот — как запасной.
  • Прогоните сквозной сценарий «включи тёмную тему» и убедитесь, что необратимый шаг блокируется без подтверждения, а цикл останавливается по лимиту.

Проверка знаний

Браузерный агент должен нажимать кнопки и заполнять формы на разных сайтах. Команда выбирает основной канал восприятия страницы.

Что предпочесть как основной канал в проде и почему?

  • A Только скриншоты — агент видит то же, что человек, значит надёжнее всего
  • B Дерево доступности (роли + стабильные ID) как основной канал, скриншот — как запасной
  • C Только сырой HTML целиком, чтобы модель сама разобралась
  • D Координаты, жёстко зашитые под конкретное разрешение экрана

Агент должен исполнять произвольный код, сгенерированный моделью, которая обрабатывает недоверенный пользовательский ввод.

Какой подход к исполнению корректен?

  • A Запускать код прямо на хосте — так быстрее и проще
  • B Запускать на хосте, но от непривилегированного пользователя
  • C Изолированная среда (контейнер/микро-ВМ) с лимитами ресурсов, запретом сети по умолчанию и эфемерной ФС
  • D Доверять коду, если модель прошла проверку на prompt injection

В цикле computer-use модель возвращает действие «кликнуть по кнопке Удалить аккаунт».

Что обязательно должно сработать перед выполнением этого действия?

  • A Ничего — раз модель решила, значит так нужно для задачи
  • B Подтверждение пользователя (human-in-the-loop), потому что действие необратимо
  • C Повторный скриншот без какой-либо проверки
  • D Увеличение лимита итераций цикла