VLM агент для игр без API: Strategy/Action/HITL архитектура | 2026 | AiManual
AiManual Logo Ai / Manual.
31 Мар 2026 Гайд

Архитектура civStation: строим VLM-агента для игр без единого API

Подробный гайд по созданию игрового бота на VLM с трехслойной архитектурой. Скриншот-анализ, планирование действий, human-in-the-loop контроль. Без доступа к AP

Проблема, которая всех бесит: игры без API

Хочешь сделать бота для Civilization 6 или любой другой игры. Открываешь документацию — нет API. Вообще. Нулевой доступ к внутреннему состоянию. Только экран. Только пиксели.

Классические боты работают через инъекции памяти, патчинг исполняемых файлов или перехват сетевых пакетов. Все это нарушает лицензионные соглашения, требует обратной разработки и ломается с каждым патчем. В 2026 году есть другой путь — Vision-Language Models. Они смотрят на экран как человек и понимают, что там происходит.

Важно: Этот метод работает с любой игрой, которая запускается в окне. Не нужен доступ к исходному коду, не нужны инжекты. Но нужна видеокарта с 8+ ГБ VRAM для современных VLM.

Архитектура civStation: три слоя вместо одного монолита

Большинство пытается запихнуть всю логику в одну модель: "посмотри на скриншот и нажми куда надо". Результат предсказуем — агент тупит, путает интерфейсы, не помнит контекст прошлых действий.

civStation делит задачу на три независимых слоя, каждый со своей ответственностью:

Слой Что делает Модель (актуально на март 2026) Частота вызовов
Strategy Анализирует общую ситуацию, ставит долгосрочные цели Llama 3.2 11B Vision или Qwen2.5-VL-7B Каждые 30-60 секунд
Action Преобразует цель в конкретные клики и нажатия LLaVA-NeXT-2025 (специально обучена на UI элементах) Каждые 2-5 секунд
HITL (Human-in-the-Loop) Перехватывает контроль при низкой уверенности модели Человек + простая эвристика Только при ошибках

Разделение стратегии и действий — ключевая идея. Strategy слой работает с низкой частотой, но использует более мощную модель, которая видит "общую картину". Action слой — быстрый, легкий, но предельно конкретный.

1 Strategy слой: мозг, который строит планы

Strategy получает скриншот всего экрана и историю последних 10 действий. Его задача — сформулировать цель на следующие 30 секунд. Не "нажать кнопку X", а "увеличить производство в городе Санкт-Петербург".

Промпт для стратегического слоя выглядит так:

Ты — стратегический планировщик для игры Civilization VI. 
Анализируй текущий скриншот и историю действий. 

Контекст игры:
- Текущий раунд: {{turn}}
- Наши ресурсы: {{resources}}
- Последние 5 действий: {{last_actions}}

Задача: определи ОДНУ главную цель на следующие 30 секунд игры.
Формат ответа: 
ЦЕЛЬ: [одно предложение]
ОБОСНОВАНИЕ: [2-3 предложения]
ПРИОРИТЕТ: [HIGH/MEDIUM/LOW]

Strategy слой не знает ничего про координаты экрана. Только семантика. Если тебе интересно, как управлять таким контекстом без деградации, у меня есть отдельная статья про вайб-кодинг.

2 Action слой: руки, которые кликают

Action слой получает от Strategy цель и текущий скриншот. Его работа — преобразовать "увеличить производство" в последовательность: 1) найти город Санкт-Петербург на карте, 2) кликнуть по нему, 3) найти кнопку управления производством, 4) выбрать юнит "Рабочий".

Здесь нужна VLM, которая умеет локализовывать объекты на изображении. LLaVA-NeXT-2025 (выпущена в январе 2026) поддерживает bounding boxes в ответах. Промпт:

На скриншоте интерфейс Civilization VI. 
Текущая цель: {{goal_from_strategy}}

Инструкции:
1. Найди все интерактивные элементы, относящиеся к цели
2. Для каждого элемента укажи координаты bounding box и действие
3. Действия могут быть: CLICK, RIGHT_CLICK, DRAG, PRESS_KEY

Формат ответа JSON:
{
  "actions": [
    {
      "element": "название элемента",
      "bbox": [x1, y1, x2, y2],
      "action": "тип действия",
      "confidence": 0.95
    }
  ]
}
💡
Не пытайся использовать для Action слоя ту же модель, что и для Strategy. Для частых вызовов нужна легкая модель. LLaVA-NeXT-2025 в 7B параметров обрабатывает скриншот за 1.2 секунды на RTX 4070. Идеально для layer 2.

3 HITL слой: страховочная сетка

Human-in-the-Loop — это не "человек сидит и смотрит 8 часов". Это система, которая:

  • Отслеживает confidence score от Action слоя
  • При confidence ниже 0.7 делает паузу и показывает предложенное действие человеку
  • Позволяет человеку скорректировать действие одним кликом
  • Запоминает корректировки для будущих аналогичных ситуаций

На практике HITL срабатывает в 3-5% случаев, но эти 3-5% предотвращают катастрофические ошибки (вроде объявления войны вместо торгового соглашения). Для локального запуска таких агентов рекомендую Tauri + llama.cpp комбинацию — она экономит оперативку.

Пошаговая сборка: от нуля до работающего агента

Шаг 1: Установка и настройка моделей

Сначала ставим две разные модели. Для Strategy слоя — что-то с хорошим reasoning, для Action — что-то с точной визуальной локализацией.

# Устанавливаем Ollama (актуальная версия на март 2026)
curl -fsSL https://ollama.ai/install.sh | sh

# Качаем модель для Strategy слоя
ollama pull qwen2.5:7b-vision

# Для Action слоя нужна LLaVA-NeXT с поддержкой bounding boxes
# Она есть в репозитории llama.cpp, но не в Ollama по умолчанию
# Качаем вручную с Hugging Face:
git clone https://huggingface.co/llava-hf/LLaVA-NeXT-2025-7B-vision

Если хочешь сэкономить на API и сохранить приватность, читай мой гайд про локальные Claude-совместимые модели.

Шаг 2: Модуль захвата экрана

НЕ используй стандартный PIL.ImageGrab на Windows. Он медленный и не работает с GPU-ускоренным рендерингом. Вместо этого — библиотека DXcam или mss с копированием в текстуру CUDA.

import dxcam
import torch
from PIL import Image

class GameCapture:
    def __init__(self):
        self.camera = dxcam.create()
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    
    def get_screenshot(self):
        # Захват в numpy array, сразу на GPU
        frame = self.camera.grab()
        if frame is None:
            return None
        # Конвертация в тензор, нормализация для VLM
        tensor = torch.from_numpy(frame).to(self.device).float() / 255.0
        return tensor

Шаг 3: Strategy слой — реализация

Подключаем модель через llama.cpp с GGUF-квантованием до 4-bit (экономит VRAM). Для контекстного менеджмента используем технику sliding window.

from llama_cpp import Llama

class StrategyLayer:
    def __init__(self, model_path):
        self.model = Llama(
            model_path=model_path,
            n_ctx=4096,  # Хватит на историю 10 действий + скриншот
            n_gpu_layers=35,  # Все слои на GPU
            verbose=False
        )
        self.action_history = []
    
    def analyze(self, screenshot_tensor, game_state):
        # Конвертируем тензор в base64 для VLM
        screenshot_base64 = tensor_to_base64(screenshot_tensor)
        
        prompt = f"""[SYSTEM] Ты стратегический планировщик...
        Скриншот: {screenshot_base64}
        История: {self.action_history[-10:]}
        Состояние: {game_state}"""
        
        response = self.model.create_chat_completion(
            messages=[{"role": "user", "content": prompt}],
            temperature=0.1,  # Низкая температура для консистентности
            max_tokens=256
        )
        
        return self._parse_strategy(response['choices'][0]['message']['content'])

Предупреждение: Не храни полную историю действий в контексте модели. После 50-60 действий производительность падает. Вместо этого делай суммаризацию каждые 10 действий. Как это правильно делать — в статье про Agent Skills.

Шаг 4: Action слой — от плана к кликам

Для Action слоя используем LLaVA-NeXT через трансформеры напрямую, потому что нужен доступ к выходным logits для bounding boxes.

from transformers import LlavaNextForConditionalGeneration, LlavaNextProcessor
import torch

class ActionLayer:
    def __init__(self):
        self.model_id = "llava-hf/LLaVA-NeXT-2025-7B-vision"
        self.processor = LlavaNextProcessor.from_pretrained(self.model_id)
        self.model = LlavaNextForConditionalGeneration.from_pretrained(
            self.model_id,
            torch_dtype=torch.float16,
            device_map="auto"
        )
    
    def generate_actions(self, screenshot, strategy_goal):
        prompt = f"""На скриншоте интерфейс игры. Цель: {strategy_goal}
        Определи элементы для взаимодействия..."""
        
        inputs = self.processor(images=screenshot, text=prompt, return_tensors="pt").to("cuda")
        
        # Генерация с constrained decoding для JSON
        output = self.model.generate(
            **inputs,
            max_new_tokens=500,
            do_sample=False,  # Детерминировано для точности координат
            output_bboxes=True  # Новая фича LLaVA-NeXT-2025
        )
        
        return self.processor.decode(output[0], skip_special_tokens=True)

Шаг 5: HITL слой — минимальная реализация

HITL — это простой веб-интерфейс на FastAPI, который показывает скриншот и предложенные действия. Человек может кликнуть по экрану для корректировки.

from fastapi import FastAPI, WebSocket
from pydantic import BaseModel
import asyncio

app = FastAPI()

class ActionRequest(BaseModel):
    screenshot: str  # base64
    proposed_actions: list
    confidence: float

@app.post("/review_action")
async def review_action(request: ActionRequest):
    if request.confidence < 0.7:
        # Отправляем в очередь на человеческую проверку
        await human_review_queue.put(request)
        return {"status": "pending_human"}
    return {"status": "approved"}

Для сложных диалоговых сценариев, где нужен многошаговый диалог с агентом, изучи архитектуру целеустремленных агентов.

Где архитектура ломается (и как чинить)

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

  • Динамические интерфейсы: В том же Civilization VI панели меняют размер, появляются модальные окна. Решение — детектировать изменения интерфейса через diff скриншотов и переключать "контекстные профили".
  • Время загрузки: Между ходами игра грузится 3-5 секунд. Action слой будет пытаться кликать по черному экрану. Добавь детектор "загрузочного экрана" по цветовым паттернам.
  • Разрешения экрана: Игра в 4K, модель обучена на 1080p. Решение — даунсэмплинг областей интереса, но с сохранением пропорций UI элементов.
Проблема Симптом Фикс
VLM не видит мелкие элементы Пропускает кнопки размером меньше 32x32 пикселей Предобработка: увеличивай контрастность UI-элементов перед подачей в модель
Ложные срабатывания Кликает по декоративным элементам как по интерактивным Добавь whitelist допустимых элементов через RAG-систему
Задержки между слоями Strategy думает 3 секунды, игра уже изменилась Кэшируй результаты Strategy на похожих скриншотах

А если мне нужен агент для другого софта?

Архитектура civStation универсальна. Замени промпты и обучение — получишь агента для:

  • Автоматизации веб-приложений (вместо Selenium)
  • Ботов для мобильных игр через эмулятор
  • Ассистентов в профессиональном ПО (Photoshop, Blender)

Для настольных игр с физическими компонентами тебе пригодится мой опыт по созданию RAG-агентов — там похожие проблемы с контекстом.

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

Что будет дальше с VLM-агентами?

До конца 2026 года появится три ключевых улучшения:

  1. Мультимодальные RAG — агенты будут запрашивать документацию по игре прямо во время выполнения, как в LM Studio MCP.
  2. Самодостаточное обучение — агент сможет создавать датасеты из своих ошибок и дообучать Action слой без человека.
  3. Стандартизация интерфейсов — что-то вроде OpenAPI для VLM, где игры будут предоставлять "визуальную схему" своего UI.

Мой прогноз: к 2027 году screen-based automation вытеснит 80% традиционных ботов, работающих через инжекты. Не потому что они точнее, а потому что они легальны и переносимы между играми.

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

Подписаться на канал