GRPO обучение браузерных агентов: полный гайд по BrowserGym и малым LLM | AiManual
AiManual Logo Ai / Manual.
29 Дек 2025 Гайд

Как обучить маленькую языковую модель управлять браузером с помощью GRPO: полный разбор

Пошаговое руководство по обучению компактных языковых моделей управлению браузером через GRPO. Настройка BrowserGym, создание агента, тонкая настройка и оптимиз

Проблема: почему большие LLM не подходят для браузерной автоматизации?

Когда мы говорим об автоматизации браузера с помощью ИИ, первое, что приходит в голову — использовать мощные модели вроде GPT-4 или Claude. Но здесь кроется фундаментальная проблема: стоимость и латентность. Каждое действие в браузере требует вызова LLM, что при масштабировании становится неподъемно дорогим.

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

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

Решение: маленькие LLM + GRPO = эффективные браузерные агенты

GRPO (Group Relative Policy Optimization) — это метод обучения с подкреплением, специально разработанный для эффективного обучения языковых моделей. В отличие от классического PPO, GRPO работает с группами примеров, что делает его особенно подходящим для задач, где нужно научиться последовательности действий.

Почему именно маленькие модели? Потому что они:

  • Быстрые: инференс занимает миллисекунды
  • Дешевые: можно развернуть локально без GPU
  • Специализированные: обучены только на конкретной задаче
  • Предсказуемые: меньше "галлюцинаций" и неожиданного поведения

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

💡
Если вы ранее работали с тонкой настройкой компактных моделей, то принципы GRPO будут вам знакомы. Это следующий шаг — обучение через взаимодействие со средой.

Пошаговый план: от нуля до работающего агента

1Подготовка окружения и установка BrowserGym

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

# Создаем виртуальное окружение
python -m venv browser_agent_env
source browser_agent_env/bin/activate  # Linux/Mac
# или browser_agent_env\Scripts\activate  # Windows

# Устанавливаем BrowserGym и зависимости
pip install browsergym-core
pip install playwright
playwright install chromium

# Для обучения с GRPO
pip install transformers torch accelerate
pip install trl  # библиотека для RL от Hugging Face

Важно: BrowserGym требует браузер Chromium. Убедитесь, что у вас достаточно оперативной памяти (минимум 8GB) для одновременной работы браузера и модели.

2Выбор и подготовка модели

Для браузерного агента нам нужна маленькая, но эффективная модель. Хорошие кандидаты:

МодельРазмерПлюсыМинусы
Phi-2 (2.7B)2.7B параметровОтличное качество, бесплатнаяТребует GPU для обучения
TinyLlama (1.1B)1.1B параметровОчень быстрая, работает на CPUМеньшая "интеллектуальность"
Qwen2.5-0.5B0.5B параметровСверхкомпактная, хороша для простых задачОграниченный контекст

Давайте загрузим TinyLlama и подготовим ее для обучения:

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

# Загружаем модель и токенизатор
model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto"
)

# Добавляем специальные токены для действий браузера
special_tokens = [
    "[CLICK]", "[TYPE]", "[SCROLL]", "[NAVIGATE]",
    "[WAIT]", "[EXTRACT]", "[DONE]"
]
tokenizer.add_tokens(special_tokens)
model.resize_token_embeddings(len(tokenizer))

3Создание среды BrowserGym

BrowserGym предоставляет готовые среды для различных задач. Давайте создадим простую среду для навигации по сайту:

from browsergym.core import BrowserEnv
from browsergym.utils import get_default_user_data_dir

# Создаем среду
env = BrowserEnv(
    task="Navigate to the search bar on google.com and search for 'machine learning'",
    headless=False,  # True для серверного запуска
    viewport={"width": 1280, "height": 720},
    slow_mo=100,  # Замедление для отладки
)

# Сброс среды возвращает начальное состояние
observation = env.reset()
print(f"Initial observation: {observation['text'][:500]}...")
💡
Для сложных задач вы можете создать собственную среду, определив класс, наследующий от BrowserEnv. Это позволяет настроить систему вознаграждений и доступные действия под конкретные нужды.

4Реализация GRPO обучения

Теперь самое интересное — реализация алгоритма GRPO. Вот упрощенная версия:

import torch.nn.functional as F
from collections import deque
import numpy as np

class GRPOTrainer:
    def __init__(self, model, tokenizer, env, learning_rate=1e-5):
        self.model = model
        self.tokenizer = tokenizer
        self.env = env
        self.optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
        self.memory = deque(maxlen=1000)  # Буфер опыта
        
    def get_action(self, observation):
        """Генерация действия на основе наблюдения"""
        prompt = self._create_prompt(observation)
        inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
        
        with torch.no_grad():
            outputs = self.model.generate(
                **inputs,
                max_new_tokens=50,
                temperature=0.7,
                do_sample=True
            )
        
        action_text = self.tokenizer.decode(outputs[0], skip_special_tokens=False)
        return self._parse_action(action_text)
    
    def train_step(self, batch_size=32):
        """Один шаг обучения GRPO"""
        if len(self.memory) < batch_size:
            return
        
        # Выбираем batch из памяти
        batch = np.random.choice(self.memory, batch_size, replace=False)
        
        losses = []
        for state, action, reward, next_state in batch:
            # Кодируем state-action пару
            text = f"{state}\nAction: {action}"
            inputs = self.tokenizer(text, return_tensors="pt").to(self.model.device)
            
            # Прямой проход
            outputs = self.model(**inputs, labels=inputs["input_ids"])
            loss = outputs.loss
            
            # Масштабируем loss по reward
            scaled_loss = loss * reward
            losses.append(scaled_loss)
        
        # Оптимизация
        total_loss = torch.stack(losses).mean()
        self.optimizer.zero_grad()
        total_loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
        self.optimizer.step()
        
        return total_loss.item()
    
    def _create_prompt(self, observation):
        """Создание промпта из наблюдения"""
        return f"""Ты — браузерный агент. Текущая страница:
{observation['text'][:1000]}

Доступные действия:
1. [CLICK] [селектор] - кликнуть по элементу
2. [TYPE] [текст] - ввести текст
3. [NAVIGATE] [url] - перейти по URL
4. [DONE] - задача выполнена

Выбери действие:"""
    
    def _parse_action(self, action_text):
        """Парсинг текста действия в команду BrowserGym"""
        # Упрощенный парсинг
        if "[CLICK]" in action_text:
            selector = action_text.split("[CLICK]")[1].strip()
            return {"action_type": "click", "selector": selector}
        elif "[TYPE]" in action_text:
            text = action_text.split("[TYPE]")[1].strip()
            return {"action_type": "type", "text": text}
        # ... остальные действия
        return {"action_type": "wait"}

# Инициализация тренера
trainer = GRPOTrainer(model, tokenizer, env)

5Цикл обучения и оценка

Теперь запустим основной цикл обучения:

num_episodes = 100
max_steps_per_episode = 20

for episode in range(num_episodes):
    observation = env.reset()
    episode_reward = 0
    
    for step in range(max_steps_per_episode):
        # Генерация действия
        action = trainer.get_action(observation)
        
        # Выполнение действия в среде
        next_observation, reward, done, info = env.step(action)
        
        # Сохранение в память
        trainer.memory.append((
            observation['text'],
            str(action),
            reward,
            next_observation['text']
        ))
        
        episode_reward += reward
        observation = next_observation
        
        # Обучение каждые 10 шагов
        if step % 10 == 0:
            loss = trainer.train_step()
            print(f"Episode {episode}, Step {step}, Loss: {loss:.4f}")
        
        if done:
            break
    
    print(f"Episode {episode} completed. Total reward: {episode_reward:.2f}")
    
    # Сохранение модели каждые 10 эпизодов
    if episode % 10 == 0:
        model.save_pretrained(f"browser_agent_episode_{episode}")
        tokenizer.save_pretrained(f"browser_agent_episode_{episode}")

Нюансы и распространенные ошибки

1. Проблема с пространством действий

Браузер имеет практически бесконечное пространство возможных действий (любой клик, любой ввод текста). Решение:

  • Ограничьте действия: разрешите только клики по элементам с определенными классами или ID
  • Используйте иерархию: сначала макродействия ("найти поисковую строку"), потом микродействия ("кликнуть по input#search")
  • Добавьте обратную связь: если агент пытается кликнуть по несуществующему элементу, дайте отрицательное вознаграждение

2. Нестабильность обучения

GRPO, как и другие RL алгоритмы, может быть нестабильным. Стратегии стабилизации:

# 1. Используйте опыт воспроизведения (experience replay)
replay_buffer = []
def store_experience(state, action, reward, next_state, done):
    replay_buffer.append((state, action, reward, next_state, done))
    if len(replay_buffer) > 10000:
        replay_buffer.pop(0)

# 2. Добавьте энтропийную регуляризацию
entropy_bonus = 0.01  # Поощряет исследование
loss = policy_loss - entropy_bonus * entropy

# 3. Используйте целевые сети (target networks)
target_model = copy.deepcopy(model)
# Обновляйте target_model раз в N шагов
target_update_freq = 100

3. Качество наблюдений (observations)

BrowserGym по умолчанию предоставляет текстовое представление страницы. Но для сложных интерфейсов этого может быть недостаточно. Решения:

  • Добавьте скриншоты: используйте компьютерное зрение для анализа интерфейса
  • Извлекайте семантическую информацию: какие элементы кликабельны, какие формы можно заполнять
  • Используйте DOM-дерево: структурное представление страницы может быть более информативным

Предупреждение: Не пытайтесь обучать агента на слишком сложных задачах сразу. Начните с простого ("войти в аккаунт"), затем переходите к сложному ("оформить заказ"). Это принцип curriculum learning.

Оптимизация для продакшена

Когда ваш агент работает, пришло время оптимизировать его для реального использования:

1. Ускорение инференса

# Квантование модели для ускорения
from transformers import BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(
    "your_trained_model",
    quantization_config=quantization_config,
    device_map="auto"
)

# Кэширование часто используемых промптов
from functools import lru_cache

@lru_cache(maxsize=100)
def get_cached_action(observation_hash):
    return trainer.get_action(observation)

2. Добавление человеческого контроля

В продакшене агент не должен быть полностью автономным:

class HumanInTheLoopAgent:
    def __init__(self, ai_agent, confidence_threshold=0.8):
        self.ai_agent = ai_agent
        self.confidence_threshold = confidence_threshold
    
    def act(self, observation):
        action, confidence = self.ai_agent.get_action_with_confidence(observation)
        
        if confidence < self.confidence_threshold:
            # Запрашиваем решение у человека
            human_action = self.request_human_input(observation, action)
            return human_action
        
        return action
    
    def request_human_input(self, observation, suggested_action):
        # Реализация интерфейса для человека
        print(f"AI suggests: {suggested_action}")
        print(f"Observation: {observation['text'][:200]}...")
        human_decision = input("Approve? (y/n/edit): ")
        # ... обработка решения

3. Мониторинг и логирование

Создайте систему мониторинга для отслеживания производительности агента:

import logging
from datetime import datetime

class AgentMonitor:
    def __init__(self):
        self.logger = logging.getLogger("browser_agent")
        self.metrics = {
            "success_rate": [],
            "avg_steps": [],
            "avg_reward": []
        }
    
    def log_episode(self, episode_num, success, steps, reward):
        self.metrics["success_rate"].append(1 if success else 0)
        self.metrics["avg_steps"].append(steps)
        self.metrics["avg_reward"].append(reward)
        
        # Экспорт в Prometheus/Grafana
        self._export_metrics()
        
        # Алертинг при деградации
        if len(self.metrics["success_rate"]) > 10:
            recent_success = np.mean(self.metrics["success_rate"][-10:])
            if recent_success < 0.5:  # Порог 50%
                self.send_alert(f"Success rate dropped to {recent_success:.2%}")

FAQ: Часто задаваемые вопросы

ВопросОтвет
Сколько данных нужно для обучения?Для простых задач достаточно 100-200 успешных эпизодов. Для сложных — 1000+.
Можно ли использовать уже обученные модели?Да, начните с модели, обученной на общих инструкциях, затем дообучите на браузерных задачах.
Как обрабатывать динамические элементы?Используйте относительные селекторы (по тексту, по отношению к другим элементам) вместо абсолютных XPath.
GRPO vs PPO — что лучше?GRPO стабильнее для языковых моделей, требует меньше гиперпараметров. PPO может дать лучшие результаты при тонкой настройке.
Как интегрировать с RAG?Используйте RAG для поиска инструкций или примеров похожих задач. Подробнее в нашем гайде по RAG.

Заключение

Обучение маленьких языковых моделей управлению браузером через GRPO — это мощный подход, который сочетает эффективность, скорость и низкую стоимость. Ключевые моменты для успеха:

  1. Начните с простого: не пытайтесь сразу решать сложные многошаговые задачи
  2. Инвестируйте в качество среды: хорошие наблюдения и вознаграждения важнее сложных алгоритмов
  3. Итеративно улучшайте: обучение RL — это процесс постоянных экспериментов
  4. Добавляйте человеческий контроль: особенно на начальных этапах развертывания

Этот подход открывает возможности для создания специализированных агентов для автоматизации рутинных задач: заполнение форм, сбор данных, мониторинг сайтов. И все это — без зависимости от дорогих облачных API и с полным контролем над поведением агента.

💡
Для более глубокого понимания архитектуры агентов рекомендую ознакомиться с нашей статьей «Как спроектировать современного AI-агента», где мы разбираем продвинутые паттерны и архитектуры.

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