Однооконный Tetris, в котором фигурой управляет модель через NVIDIA NOOA. Игра не использует таймер: каждый тик длится ровно столько, сколько модели нужно на ответ.
Цикл одного тика:
- Движок рендерит PNG и точную текстовую сетку игрового поля.
- Выбранный адаптер отправляет совместимый формат, компактное состояние и пользовательский промпт в модель.
PredictStrategyвалидирует ответ по строгой Pydantic-схеме.- Ровно одна команда применяется к фигуре, затем выполняется один шаг гравитации.
NOOA здесь работает в single-shot режиме и не исполняет сгенерированный моделью Python-код.
Безопасность: приложение отправляет игровое состояние и выбранный режим представления в настроенный пользователем LLM endpoint. Не помещайте в prompt, provider-конфиг или логи секреты и персональные данные. API-ключи хранятся только локально — через environment variables или
config/providers.local.json.
Требования: macOS/Linux и uv. Для локальных Gemma нужен OpenAI-compatible
LM Studio server на http://localhost:1234.
cd ~/programming/my/tetris-agent-lab
uv sync
uv run tetris-agentМеню модели содержит:
- LM Studio ·
google/gemma-4-12b— изображение или текстовая сетка; - LM Studio ·
google/gemma-4-31b— изображение или текстовая сетка; - Настраиваемые OpenAI-compatible провайдеры из
config/providers.local.json. Для них появляется редактируемое поле поиска и кнопкаОБНОВИТЬ /MODELS. Список загружается с provider-specific/models, а режим изображения доступен только моделям сcapabilities.vision=true(или совпавшим с fallback-pattern).
Локальный конфиг намеренно находится в .gitignore и не попадает в коммиты.
Рабочий пример структуры лежит в config/providers.example.json:
{
"openai_compatible_providers": [
{
"key": "my-provider",
"label": "My Provider",
"base_url": "https://api.example.com/v1",
"api_key": "replace-me",
"models_path": "models",
"default_model": "model-id",
"default_vision": false,
"vision_model_patterns": ["vision", "-vl"]
}
]
}Вместо api_key можно указать api_key_env; путь к конфигу переопределяется
через TETRIS_AGENT_PROVIDERS_CONFIG. Локальному файлу рекомендуется mode 0600.
Текущая настройка AnyModel использует https://anymodel.org/v1; /models
на момент проверки вернул 101 модель, из них 50 с vision. Стартовая проверенная
модель — gc/gemini-2.5-flash-lite.
По умолчанию выбрана Gemma 4 12B в режиме изображения. Если выбранная локальная модель скачана, но не загружена, адаптер загрузит её через native LM Studio API с контекстом 8192 токенов.
Настройка без изменения кода:
LM_STUDIO_MODEL=google/gemma-4-31b uv run tetris-agent
LM_STUDIO_BASE_URL=http://localhost:1234/v1 uv run tetris-agent
LM_STUDIO_CONTEXT_LENGTH=16384 uv run tetris-agentВ UI нет клавиатурного или ручного управления фигурой: только Start/Stop и
редактируемый промпт. Опциональная тень места приземления выключена по умолчанию;
галочка доступна только между играми и одновременно управляет UI и PNG модели.
Соседний выбор «Цветность» задаёт визуальное кодирование поля: все блоки одним
цветом (monochrome), нейтральный стек с цветной текущей фигурой
(active_highlight) или обычные цвета тетромино (piece_colors, по умолчанию).
Он также фиксируется для replay и изображения, отправляемого vision-модели.
Галочка «Включить историю» также доступна только между играми. По умолчанию она
выключена: каждый тик получает свежий разговорный контекст без прошлых ответов,
recent_actions и previous_note. При включении один
NOOA-агент и его чат сохраняются до Stop/Game Over, поэтому модель видит прошлые
наблюдения и собственные ответы. Длинная история, особенно с PNG, увеличивает
контекст, задержку и расход памяти модели.
Solid-блоки — зафиксированные клетки и текущая фигура. Справа ведётся журнал
ответов: список тиков переключает показанный JSON. Пока выбран последний тик, список автоматически
следует за новыми ответами; выбор старого тика приостанавливает auto-follow.
Текстовая сетка всегда содержит 20 строк по 10 клеток: . — пусто, заглавные
IOTSZJL — зафиксированные блоки, @ — текущая падающая фигура, * —
опциональная тень. Все четыре координаты текущей фигуры перечисляются отдельно,
включая клетки выше видимого поля. Также observation содержит число закреплённых
клеток, высоты и дыры колонок. В интерактивном assisted-режиме также передаются
точные конечные клетки и метрики допустимых прямых приземлений. Это снимает с
text-only модели ненадёжный мысленный расчёт геометрии; benchmark использует
отдельный raw-режим без этих подсказок.
Зафиксированный черновой дизайн лежит в benchmark.yaml. Batch runner не
инициализирует Qt и использует тот же GameRunner, состояние Tetris и адаптеры,
что интерактивная игра.
uv run tetris-benchmark validate benchmark.yaml
uv run tetris-benchmark run benchmark.yaml --jobs 1
uv run tetris-benchmark analyze benchmark.yamlПовторный run пропускает игры с готовым summary.json и перезапускает только
незавершённые. --rerun явно заменяет готовые trace. Параллельный --jobs N
поддерживается для черновых прогонов, но для сопоставимой latency рекомендуется
--jobs 1.
Основная матрица: model × modality × image color mode × history × seed. Для
image выполняются отдельные игры в режимах monochrome, active_highlight и
piece_colors. Для text_grid цвет отсутствует и записывается как n/a, чтобы
не плодить три идентичных текстовых прогона. Режимы истории имеют строгую
семантику:
none— свежий разговор и пустая память на каждом тике;compact— свежий разговор плюс последние восемь структурированных результатов действий и изменений доски;full— один NOOA-чат со всеми прошлыми сообщениями, только для дополнительного дорогого эксперимента.
Benchmark всегда использует raw text-grid без BEST_SAFE_LANDING, списка
посадок и готовой команды. Интерактивный UI сохраняет assisted-представление.
Тем самым image и text-grid получают одинаковый prompt и машинное состояние, а
отличаются только представлением поля.
Сырые записи создаются в runs/<experiment>/<run_id>/: manifest.json хранит
seed, модель, prompt/hash, inference-параметры, Git и версии; events.jsonl —
snapshots до/после тика, raw output, retry, usage и метрики; summary.json —
результат партии и агрегаты.
После analyze появляются CSV, главный SVG-график и report.md в
runs/<experiment>/results/. Токены и стоимость остаются пустыми, если провайдер
их не сообщил. Любой events.jsonl можно открыть кнопкой ОТКРЫТЬ REPLAY:
проигрывание, пауза, пошаговая навигация и скорость работают локально и не
вызывают модель. Подробная методология: docs/BENCHMARK.md.
Модель обязана вернуть объект вида:
{"action":"rotate_cw","note":"Flatten the right side"}action допускает только:
left,rightrotate_cw,rotate_ccwsoft_drop,hard_dropwait
Лишние поля, неизвестные действия и note длиннее 160 символов отвергаются.
NOOA делает до трёх попыток исправить невалидный структурированный ответ.
uv run pytest
uv run ruff check .
uv run python scripts/smoke_ui.py
uv run python scripts/smoke_lmstudio.py
uv run python scripts/smoke_provider.py --profile openai-compatible:anymodel --mode image --ticks 1
uv run python scripts/smoke_provider.py --profile openai-compatible:anymodel --model am/gemma-4-31b-it --mode image --ticks 1Две последние команды выполняют реальные provider-запросы. Каждая игровая сессия
пишется в logs/session-*.jsonl; логи не коммитятся.
game.py— детерминированный Tetris state machine.render.py— синхронные PNG и text-grid представления поля.placement.py— точный анализ и ранжирование конечных приземлений text-grid.protocol.py— строгий JSON-контракт.prompts.py— полный редактируемый prompt с правилами и стратегией размещения.adapters/base.py— универсальный порт провайдера.adapters/catalog.py— подключённые модели и capability-матрица.adapters/nooa_openai.py— общий OpenAI-compatible NOOA-движок.adapters/lmstudio.py— подключение локального OpenAI-compatible сервера.adapters/openai_compatible.py—/models, capability parsing и generic adapter.provider_config.py— безопасная загрузка локального provider-конфига.runner.py— прерываемый пошаговый цикл.trace.py— воспроизводимый JSONL trace и replay loader.baselines.py— random-legal и greedy baseline.benchmark/— YAML-схема, headless executor, CSV/SVG/report.app.py— единственное окно PySide6.DEVLOG.md— журнал разработки, решений и известных ограничений.
Код этого репозитория распространяется по Apache License 2.0. Сторонние Python-пакеты и внешние LLM-провайдеры сохраняют собственные условия; подробности — в THIRD_PARTY_NOTICES.md.