Архитектура браузерного AI-агента: как LLM взаимодействует с веб-интерфейсами
Браузерный AI-агент для техподдержки - это программа, которая видит веб-интерфейс, анализирует его и выполняет действия вместо человека. Она открывает консоль тикетов, находит нужный запрос, читает содержимое, принимает решение и кликает по кнопкам. В основе лежит LLM, которая работает как мозг: получает описание страницы, генерирует план действий и передаёт команды браузеру. Такой агент замыкает цикл «наблюдение-решение-действие» без участия оператора.
На практике это означает, что рутинные операции - сброс пароля, проверка статуса заказа, назначение исполнителя - перестают отнимать время сотрудников. Агент обрабатывает тикет за 15-30 секунд, в то время как человек тратит 2-5 минут на переключение между окнами и ручной ввод. При потоке в 200 тикетов в день экономия достигает 10-15 человеко-часов.
Ключевой выбор при проектировании - способ получения информации о странице. Есть два принципиально разных подхода: скриншоты и DOM-дерево. Каждый определяет архитектуру агента, стоимость токенов и надёжность работы. Разберём оба.
Цикл «наблюдение-решение-действие»: основа автономной работы
Цикл состоит из трёх фаз, повторяющихся до достижения цели. На фазе наблюдения агент получает текущее состояние интерфейса - скриншот или структуру DOM с координатами элементов. На фазе решения LLM анализирует полученные данные и определяет следующее действие: какой элемент выбрать, куда кликнуть, какой текст ввести. На фазе действия команда отправляется в браузер через Playwright, и цикл повторяется.
Вот упрощённый псевдокод цикла для обработки тикета о сбросе пароля:
# Инициализация агента с доступом к браузеру
agent = BrowserAgent(llm=azure_openai_client, browser=playwright_mcp)
# Целевая задача
ticket_id = "TKT-2847"
goal = f"Обработать тикет {ticket_id}: проверить учётную запись и сбросить пароль"
# Запуск цикла
state = agent.start(goal)
while not state.is_complete:
observation = agent.observe() # скриншот или DOM
action = agent.decide(observation) # LLM выбирает действие
result = agent.act(action) # клик, ввод, скролл
state = agent.evaluate(result) # проверка прогрессаКаждый шаг логируется. При ошибке - элемент не найден, страница не загрузилась - агент может повторить попытку или запросить уточнение. В production-сценариях добавляют счётчик итераций и таймаут: если за 20 циклов цель не достигнута, задача передаётся человеку.
Скриншоты vs DOM: выбор подхода для вашего сценария
Подход со скриншотами работает через визуальное распознавание. Агент делает снимок экрана, передаёт его мультимодальной LLM и получает описание интерфейса вместе с координатами для клика. Плюс - не зависит от вёрстки, работает с Canvas, графиками и нестандартными элементами. Минус - каждый скриншот весит 100-500 КБ в base64, что при разрешении 1920x1080 даёт 500-1500 токенов на изображение. На одном прогоне из 10 шагов это 5000-15000 токенов только на скриншоты.
DOM-подход извлекает структуру страницы: теги, атрибуты, текстовое содержимое, координаты ограничивающих рамок. Агент получает компактное текстовое представление - 200-800 токенов на страницу. Он точен в выборе элементов по ID или CSS-селекторам, работает быстрее и обходится дешевле. Недостаток - ломается при изменении вёрстки. Если разработчики переименовали классы или перестроили DOM, агент теряет ориентацию.
Сравнение подходов для сценария техподдержки:
| Критерий | Скриншоты | DOM + координаты |
|---|---|---|
| Точность выбора элемента | Зависит от модели, 85-95% | 99% при стабильной вёрстке |
| Стоимость токенов на 1 шаг | 500-1500 токенов | 200-800 токенов |
| Устойчивость к смене дизайна | Высокая | Низкая |
| Работа с Canvas/графиками | Да | Нет |
| Скорость одного цикла | 3-7 секунд | 1-3 секунды |
Для консоли техподдержки с фиксированной структурой - таблицы, формы, кнопки - DOM-подход предпочтительнее. Он быстрее, дешевле и точнее. Скриншоты стоит подключать как fallback: если DOM-элемент не найден, агент делает снимок и пытается найти цель визуально.
Инструментарий: OpenAI Agents SDK и Playwright MCP
Связка OpenAI Agents SDK и Playwright MCP даёт готовый конвейер для браузерной автоматизации. OpenAI Agents SDK управляет жизненным циклом агента: принимает задачу, вызывает LLM, обрабатывает ответы и маршрутизирует действия. Playwright MCP запускает браузер как MCP-сервер, предоставляя стандартизированный интерфейс для навигации, кликов, ввода текста и извлечения содержимого страниц.
MCP (Model Context Protocol) - это открытый протокол, который определяет, как LLM взаимодействует с внешними инструментами. Вместо того чтобы писать кастомные обёртки для каждого действия браузера, вы описываете доступные инструменты в конфигурации MCP-сервера. Агент получает их список и сам решает, какой инструмент вызвать. Подробнее архитектура MCP и её место в агентных системах разбирается в материале об архитектуре AI-агентов от токенов до ReAct-цикла.
Playwright в режиме MCP-сервера отдаёт браузерные API как набор инструментов: navigate, click, type, screenshot, get_dom. Агент не управляет браузером напрямую - он отправляет запросы через MCP, а сервер выполняет их в изолированном контексте. Это упрощает отладку: каждый вызов логируется, можно воспроизвести сессию по логам.
OpenAI Agents SDK берёт на себя оркестрацию: получает задачу, формирует промпт с описанием доступных инструментов, отправляет в Azure OpenAI, парсит ответ и выполняет вызов MCP. SDK поддерживает потоковую обработку - агент может начать действие, не дожидаясь полного ответа модели. Для задач техподдержки это сокращает задержку на 1-2 секунды на каждом шаге.
Пошаговая настройка: от Azure OpenAI до первого действия агента
Настройка занимает 20-30 минут при наличии активной подписки Azure и установленного Node.js. Пройдём все шаги последовательно.
Подключение к Azure OpenAI: получение доступа к LLM
Создайте ресурс Azure OpenAI в портале Azure. Выберите регион с доступными квотами на GPT-4o - на момент написания это East US, West Europe, Sweden Central. После развёртывания перейдите в раздел «Keys and Endpoint» и скопируйте URL эндпоинта и один из ключей.
Пример конфигурации для Python-клиента:
import os
from openai import AzureOpenAI
client = AzureOpenAI(
azure_endpoint="https://your-resource.openai.azure.com/",
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-10-21"
)
# Проверка подключения
response = client.chat.completions.create(
model="gpt-4o", # имя развёртывания в Azure
messages=[{"role": "user", "content": "Проверка связи"}],
max_tokens=50
)
print(response.choices[0].message.content)Для продакшена используйте аутентификацию через Azure AD с ролями RBAC вместо ключей. Это исключает риск утечки секретов и упрощает ротацию доступа.
Запуск Playwright как MCP-сервера: мост между агентом и браузером
Установите Playwright и MCP-сервер:
npm install -g @playwright/test
npx playwright install chromium
npm install @anthropic/mcp-server-playwrightЗапустите сервер с привязкой к порту 3000:
npx @anthropic/mcp-server-playwright \
--browser chromium \
--headless \
--port 3000 \
--allowed-origins "https://your-support-console.example.com"Флаг --headless запускает браузер без GUI, что обязательно для серверного использования. Параметр --allowed-origins ограничивает домены, с которыми может работать агент - это защита от случайной навигации на внешние ресурсы.
Проверьте, что сервер отвечает:
curl -X POST http://localhost:3000/tools/list \
-H "Content-Type: application/json"Ответ вернёт JSON со списком доступных инструментов: navigate, click, type, screenshot, get_dom и других. Этот список агент будет получать при инициализации и использовать для планирования действий.
Конфигурация агента: минимальный набор инструкций для обработки тикетов
Минимальная конфигурация агента на OpenAI Agents SDK выглядит так:
{
"agent": {
"name": "support-ticket-agent",
"model": "gpt-4o",
"instructions": [
"Ты агент техподдержки. Твоя задача - обрабатывать тикеты в консоли.",
"Открывай тикет по ID, анализируй содержимое, выполняй стандартные процедуры.",
"После каждого действия проверяй результат. Если элемент не найден, сделай скриншот и проанализируй визуально.",
"Максимум 15 шагов на тикет. Если не уложился - передай оператору."
],
"tools": ["mcp_playwright"],
"tool_config": {
"mcp_playwright": {
"server_url": "http://localhost:3000",
"timeout": 30000
}
},
"max_iterations": 15,
"on_error": "escalate_to_human"
}
}Четыре инструкции покрывают базовый сценарий. Первая задаёт роль, вторая - порядок работы, третья - стратегию восстановления при ошибках, четвёртая - ограничение по ресурсам. Этого достаточно для обработки 70-80% типовых тикетов. Более сложные кейсы требуют расширения инструкций, но стартовать лучше с минимальной конфигурации и добавлять правила по мере выявления проблем.
Запуск агента:
from agents import Agent, Runner
agent = Agent.from_config("support-ticket-agent.json")
result = Runner.run(
agent,
input="Обработай тикет TKT-2847: пользователь не может войти, запрашивает сброс пароля"
)
print(result.final_output)На этом этапе агент готов к первому прогону. Он подключится к MCP-серверу, откроет браузер и начнёт выполнять инструкции.
Полный прогон: от открытия консоли до верификации кейса
Рассмотрим сквозной сценарий на реальном примере. Тикет TKT-2847: пользователь сообщает, что не может войти в личный кабинет, пароль не подходит, просит сброс.
Сценарий: обработка тикета о сбросе пароля
Агент получает задачу и выполняет последовательность действий:
- Навигация: открывает URL консоли техподдержки, дожидается загрузки страницы.
- Поиск тикета: вводит «TKT-2847» в строку поиска, нажимает Enter.
- Открытие: кликает по найденному тикету в списке результатов.
- Анализ: извлекает содержимое тикета - email пользователя, описание проблемы, историю обращений.
- Проверка учётной записи: переходит в админ-панель пользователей, ищет аккаунт по email, проверяет статус (активен, не заблокирован).
- Сброс пароля: нажимает кнопку «Сбросить пароль», подтверждает действие в модальном окне.
- Ответ пользователю: возвращается в тикет, добавляет комментарий с шаблонным ответом и инструкцией по получению нового пароля.
- Смена статуса: переводит тикет в статус «Решён».
Время выполнения - 18 секунд. Агент использовал 9 итераций цикла, 7 DOM-запросов и 2 скриншота (для подтверждения в модальном окне, которое не отдало DOM). Общий расход токенов - 4200, стоимость - около $0.06 на GPT-4o.
Лог действий агента для этого прогона:
[Step 1] navigate → https://support.example.com/console - OK (1.2s)
[Step 2] type → search input: "TKT-2847" - OK (0.3s)
[Step 3] click → search button - OK (0.5s)
[Step 4] click → ticket link TKT-2847 - OK (0.8s)
[Step 5] get_dom → ticket content - OK, 142 nodes (0.4s)
[Step 6] click → admin panel link - OK (1.1s)
[Step 7] type → user search: "user@example.com" - OK (0.3s)
[Step 8] click → reset password button - OK (0.6s)
[Step 9] screenshot → modal confirmation - analyzed, click confirm (2.1s)
[Step 10] navigate → back to ticket TKT-2847 - OK (0.9s)
[Step 11] type → comment field: template response - OK (0.4s)
[Step 12] click → status dropdown → "Resolved" - OK (0.7s)
[Step 13] get_dom → verify status - OK, status="Resolved" (0.3s)
[Step 14] task complete - successВерификация: как убедиться, что агент всё сделал правильно
Автоматическая верификация встроена в последний шаг цикла. Агент не просто меняет статус - он перечитывает DOM страницы тикета и проверяет, что статус изменился на «Решён», комментарий добавлен, а кнопка «Сбросить пароль» больше не активна. Это трёхточечная проверка, которая ловит 95% ошибок.
Для production-сценариев добавляют внешний валидатор - отдельный скрипт, который после завершения агента проверяет консистентность данных через API:
def verify_ticket_resolution(ticket_id):
# Проверка через API, а не через UI
ticket = api.get_ticket(ticket_id)
assert ticket.status == "resolved"
assert len(ticket.comments) > 0
user = api.get_user(ticket.requester_email)
assert user.password_reset_requested == True
return TrueТакая двухуровневая верификация - на уровне UI и на уровне API - исключает ситуацию, когда агент «думает», что выполнил задачу, а фактически изменения не применились. Внедрение подобных проверок - стандартная практика при построении агентов для production-сред, детально описанная в разборе архитектуры самописного AI-агента.
Ограничения и практические рекомендации
Браузерный агент на LLM - мощный, но не универсальный инструмент. Его эффективность прямо зависит от стабильности DOM-структуры целевого интерфейса. Если вёрстка консоли техподдержки меняется раз в две недели, агент на DOM-подходе начнёт давать сбои и потребует обновления селекторов. Решение - добавить в CI/CD-пайплайн тест, который после каждого деплоя консоли прогоняет агента на тестовом тикете и алертит при падении точности ниже 90%.
Стоимость токенов - второй критический фактор. На скриншотах один тикет обходится в $0.15-0.30, на DOM - $0.03-0.08. При 1000 тикетов в месяц разница составляет $120-220. Для старта выбирайте DOM-подход, а скриншоты подключайте как fallback для нестандартных элементов интерфейса.
Мониторинг обязателен. Агент должен логировать каждый шаг, время выполнения, количество итераций и результат. Метрики для отслеживания: доля успешно обработанных тикетов (цель > 85%), среднее время обработки (цель < 30 секунд), доля эскалаций на оператора (цель < 15%). При выходе за пороги - пересмотр инструкций или дообучение на проблемных кейсах.
Начинайте с трёх-пяти типовых сценариев: сброс пароля, проверка статуса заказа, назначение исполнителя. Не пытайтесь автоматизировать всё сразу. Каждый новый сценарий добавляйте только после того, как предыдущий работает стабильно неделю без вмешательства человека. Этот подход снижает риски и позволяет итеративно наращивать покрытие.
Готовые решения вроде Koda Desktop или Google Antigravity 2.0 предлагают визуальный интерфейс для настройки агентов без кода. Для прототипирования и небольших команд это быстрый старт. Собственная сборка на OpenAI Agents SDK + Playwright MCP оправдана, когда нужен полный контроль над логикой, аудит безопасности и интеграция с внутренними системами.