# AI-RPG — Text RPG with an LLM Game Master > **Version:** 1.0.0 > **Stack:** Python 3.12 · FastAPI · SQLAlchemy 2.x async · PostgreSQL 15 · Qdrant 1.9 · React 18 · Vite 5 · TypeScript 5 · Tailwind CSS 3 · zustand 4 · react-i18next 14 AI-RPG — это веб-приложение, в котором игрок ведёт текстовую ролевую игру с ИИ-мастером (GM). Игрок создаёт мир (или выбирает готовый пресет), настраивает персонажа, и далее вступает в пошаговое взаимодействие: каждое действие игрока обрабатывается трёхфазным orchestrator-ом, который генерирует нарратив, обновляет состояние мира через tool calls, и предлагает 1-3 следующих действия. Полное ТЗ — в `docs/AI-RPG_TZ_TDD.md`. --- ## Возможности (v1.0.0) - ✅ **JWT-аутентификация** — регистрация обычных пользователей и первого админа по токену. - ✅ **Админ-панель** — настройки (LLM, embeddings, Qdrant, UI, game), логи LLM-вызовов с фильтрами, список пользователей, статистика, диагностические кнопки (test LLM / test LLM tools / test embeddings / probe dimension / recreate collections), загрузка favicon/logo/OG-image. - ✅ **Пресеты миров** — 2 встроенных (Classic Fantasy, Deep Space Outpost) + создание/редактирование своих. - ✅ **World Builder** — генерация нового мира из пресета или формы через SSE-стрим: schemas → environment → entities → intro scene. - ✅ **World Editor** — чат-инструция для LLM, `propose_changes` с diff, `ask_user` для уточнений, optimistic locking. - ✅ **Orchestrator (3 фазы)**: - **Phase 1**: planner+executor — цикл tool-calls до `submit_plan`. - **Phase 2**: writer — single LLM call с `submit_step`, стриминг `scene_chunk`. - **Phase 3**: persist + deferred triggers + summary (если история длинная) + suggest actions. - ✅ **RAG через Qdrant** — `rag_query` / `rag_add`, двухстадийный retrieval (Qdrant → PostgreSQL), изоляция миров через payload-фильтр, фоновая индексация (через `embedding_status`). - ✅ **Embeddings** — `HashEmbedder` (offline, для dev) и `OpenAIEmbedder` (OpenAI-compatible API), авто-fallback `embeddings.api_url` → `llm.api_url`. - ✅ **Игровые инструменты** (17 шт.): `entity_create/get/list/update/delete`, `env_update/get`, `update_plot_rails`, `advance_time`, `schedule_trigger`, `calc` (с кубиками), `random_choice` (детерминированный), `rag_query/add`, `run_subagent`, `submit_plan/step`, `suggest_actions`. - ✅ **Schema tools** — `schema_add_type/add_field/remove_field/modify_field` для world_editor. - ✅ **Контекстный менеджер** — последние N сообщений + summary при превышении порога, деградация recent → rag → summary. - ✅ **SSE-стриминг** — все долгие операции (world_builder, world_editor, orchestrator) отдают прогресс через SSE с `event:`/`data:`/`id:`, heartbeat, reconnect через `Last-Event-ID`. - ✅ **Фронтенд** — React+TS+Vite+Tailwind, тёмная тема, i18n (en/ru), мобильный responsive, SSE-клиент с автопереподключением. --- ## Архитектура ``` ┌──────────────────────────────────────────────────────────────┐ │ Браузер (React 18 + Vite + TS + Tailwind + zustand) │ └──────────────────────────┬───────────────────────────────────┘ │ HTTP / SSE ┌──────────────────────────▼───────────────────────────────────┐ │ FastAPI Backend (uvicorn) │ │ ├─ app/api/ — роутеры (auth, worlds, sessions, ...) │ │ ├─ app/engine/ — game_master, world_builder, editor │ │ │ └─ tools/ — ToolRegistry + 17 game tools │ │ ├─ app/core/ — llm, rag, embeddings, security, ... │ │ ├─ app/models/ — SQLAlchemy ORM │ │ ├─ app/prompts/ — системные промпты (en) │ │ └─ app/schemas/ — Pydantic request/response │ └──────┬─────────────────────────────────┬─────────────────────┘ │ async SQLAlchemy │ httpx + qdrant-client ┌──────▼──────────────┐ ┌───────▼──────────────────────┐ │ PostgreSQL 15 │ │ Qdrant 1.9 (векторный индекс)│ │ (users, worlds, │ │ collections: entities, │ │ entities, steps, │ │ story_entries │ │ logs, ...) │ └──────────────────────────────┘ └─────────────────────┘ ▲ │ httpx (embeddings API) ┌────────┴─────────────┐ │ LLM Provider (any │ │ OpenAI-compatible) │ └──────────────────────┘ ``` --- ## Быстрый старт ### Опция 1: docker-compose (рекомендуется) ```bash # 1. Скопировать .env.example в .env и отредактировать cp .env.example .env # Отредактируйте SECRET_KEY, ADMIN_SETUP_TOKEN, LLM_API_URL, LLM_API_KEY # 2. Поднять всё docker compose up -d # 3. Зайти на http://localhost (frontend через nginx) # Или http://localhost:5173 (frontend dev) / http://localhost:8000/api/docs (backend) ``` При первом старте сервер напечатает в лог: ``` === AI-RPG Admin Setup === No admin user yet. Open this URL in your browser: /register/admin?token= =========================== ``` Откройте `http://localhost/register/admin?token=<...>` и создайте первого админа. ### Опция 2: локальный dev (backend + frontend раздельно) ```bash # Backend cd /path/to/ai-rpg python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt # Запустить PostgreSQL и Qdrant (через docker compose up db qdrant) docker compose up -d db qdrant # Применить миграции (создаёт все таблицы) python -c "import asyncio; from app.db import get_engine; from app.migrations.versions.001_initial_schema import create_all_tables; asyncio.run(create_all_tables(get_engine()))" # Запустить backend uvicorn app.main:app --reload --port 8000 # В другом терминале — frontend cd frontend npm install npm run dev # Откроется http://localhost:5173 ``` --- ## Конфигурация LLM AI-RPG работает с **любым OpenAI-compatible API**: | Provider | `LLM_API_URL` | Пример `LLM_MODEL` | |-----------------|-------------------------------------|------------------------------| | OpenAI | `https://api.openai.com/v1` | `gpt-4o-mini` | | Ollama (local) | `http://localhost:11434/v1` | `qwen2.5:7b-instruct` | | LM Studio | `http://localhost:1234/v1` | `local-model` | | vLLM | `http://localhost:8000/v1` | `Qwen/Qwen2.5-7B-Instruct` | | OpenRouter | `https://openrouter.ai/api/v1` | `qwen/qwen-2.5-7b-instruct` | Если `LLM_API_URL` пуст — backend использует `MockLlmClient` (возвращает записанные replay-ответы). Это удобно для dev и тестов. ### Embeddings Два провайдера: - `offline_hash` (по умолчанию) — `HashEmbedder`, детерминированный bag-of-words + hash projection. Не делает HTTP-запросов, работает offline. Размерность 256. - `openai` — OpenAI-compatible embeddings API. URL/key fallback на `llm.api_url`/`llm.api_key`, если `embeddings.api_url`/`embeddings.api_key` пустые. Кнопка «Авто-проба размерности» в админке (`POST /api/admin/test/embeddings/probe-dimension`) определяет реальную размерность модели и предлагает сохранить её в `embeddings.dimension`. --- ## Структура проекта ``` ai-rpg/ ├── app/ # Backend (Python 3.12) │ ├── api/ # FastAPI роутеры │ │ ├── auth.py # /api/register, /api/auth/* │ │ ├── worlds.py # /api/worlds │ │ ├── sessions.py # /api/sessions/* (SSE streams) │ │ ├── presets.py # /api/presets │ │ ├── admin.py # /api/admin/* (settings, logs, test, upload) │ │ └── misc.py # /api/health, /api/i18n │ ├── core/ # Сквозные сервисы │ │ ├── llm.py # LlmClient + MockLlmClient │ │ ├── rag.py # RAG через Qdrant + PostgreSQL │ │ ├── embeddings.py # HashEmbedder, OpenAIEmbedder │ │ ├── qdrant_client.py # singleton AsyncQdrantClient │ │ ├── security.py # JWT + bcrypt │ │ ├── settings_service.py # settings table CRUD + seed │ │ ├── state_validator.py # validate_state, apply_patch │ │ ├── time_utils.py # GameTime, parse_delta, advance_time │ │ └── logging.py # structlog setup │ ├── engine/ # Игровой движок │ │ ├── game_master.py # orchestrator (3 фазы) │ │ ├── world_builder.py # flow создания мира │ │ ├── world_editor.py # flow редактирования мира │ │ ├── context.py # контекстный менеджер │ │ ├── sse.py # SseEmitter │ │ └── tools/ │ │ ├── base.py # Tool, ToolRegistry, ToolContext, ToolResult │ │ ├── game.py # 17 игровых инструментов │ │ ├── schema_tools.py # 4 schema-инструмента │ │ └── register_all.py # build_default_registry() │ ├── models/ # SQLAlchemy ORM (10 таблиц) │ ├── prompts/ │ │ ├── registry.py # get_prompt(stage, language) │ │ └── stages/ # 11 stage-промптов (en) │ ├── schemas/ # Pydantic request/response │ ├── migrations/ │ │ ├── init_db.py # init_db(session) │ │ ├── init_qdrant.py # re-export init_qdrant_collections │ │ ├── seed.py # builtin presets (fantasy, sci-fi) │ │ └── versions/001_initial_schema.py │ ├── config.py # Settings (pydantic-settings) │ ├── db.py # async engine + session factory │ └── main.py # FastAPI app + lifespan ├── frontend/ # Frontend (React 18 + Vite + TS) │ ├── src/ │ │ ├── pages/ # 8 pages │ │ ├── components/ │ │ │ ├── ui/ # 9 primitives (Button, Card, Modal, ...) │ │ │ ├── sessions/ # Chat, ToolCallBubble, ActionInput, ... │ │ │ ├── worlds/ # WorldCard, WorldBuilder, WorldEditor │ │ │ └── admin/ # SettingsPanel, LlmLogsTable, ... │ │ ├── stores/ # zustand (auth, ui, worlds, session, toast) │ │ ├── lib/ # api.ts, sse.ts, cn.ts │ │ ├── i18n/ # en.json, ru.json │ │ └── types/index.ts │ ├── package.json │ ├── vite.config.ts │ ├── tsconfig.json │ └── tailwind.config.js ├── tests/ # pytest │ ├── unit/ # 65+ unit-тестов │ └── integration/ # (placeholder) ├── deploy/ │ ├── Dockerfile.backend │ ├── Dockerfile.frontend │ └── nginx.conf ├── docs/ │ └── AI-RPG_TZ_TDD.md # оригинальное ТЗ ├── docker-compose.yml ├── requirements.txt ├── pytest.ini ├── .env.example └── README.md ``` --- ## API обзор Полная OpenAPI-схема — на `http://localhost:8000/api/docs` (Swagger UI). ### Ключевые эндпоинты | Метод | Путь | Назначение | |---------|---------------------------------------------------|-----------------------------------------| | `POST` | `/api/register` | Регистрация пользователя | | `POST` | `/api/register/admin?token=` | Регистрация первого админа | | `POST` | `/api/auth/login` | Логин по email/username → JWT | | `GET` | `/api/auth/me` | Текущий профиль | | `GET` | `/api/worlds` | Список миров пользователя | | `POST` | `/api/worlds` | Создать мир → SSE URL для world_builder | | `GET` | `/api/sessions/worlds/{id}/state` | Текущее состояние для play-страницы | | `POST` | `/api/sessions/worlds/{id}/iterate` | Запустить orchestrator → SSE URL | | `GET` | `/api/sessions/worlds/{id}/iterate/stream` | SSE-стрим итерации | | `POST` | `/api/sessions/worlds/{id}/retry` | Повторить последний шаг | | `POST` | `/api/sessions/worlds/{id}/rollback` | Откатить последний шаг | | `GET` | `/api/admin/settings` | Все настройки (секреты замаскированы) | | `PATCH` | `/api/admin/settings` | Обновить настройки | | `GET` | `/api/admin/llm-logs?stage=&status_filter=&...` | Логи LLM-вызовов с фильтрами | | `POST` | `/api/admin/test/llm?api_url=&api_key=&model=` | Проверка связности LLM | | `POST` | `/api/admin/test/embeddings/probe-dimension` | Авто-проба размерности эмбеддингов | | `POST` | `/api/admin/upload-icon` | Загрузить favicon/logo/og_image | | `GET` | `/api/health` | Health-check (db, qdrant, llm, emb) | ### SSE-события (orchestrator) | Event | Когда | |----------------------|--------------------------------------------------| | `phase_start` | Начало Phase 1/2/3 | | `phase_end` | Конец фазы | | `tool_call` | LLM вызвала инструмент | | `llm_call_start/end` | Начало/конец LLM-вызова | | `scene_chunk` | Streaming-чанк текста из Phase 2 | | `scene_complete` | Полный текст сцены + delta_time | | `suggested_actions` | 1-3 следующих действия | | `trigger_fired` | Сработал отложенный триггер | | `summary_generated` | Сгенерирован summary | | `iteration_complete` | Полное завершение итерации | | `done` | Успешное завершение стрима | | `error` | Фатальная ошибка, стрим закрывается | --- ## Тестирование ```bash # Backend unit-тесты pytest tests/unit/ -v # С покрытием pytest --cov=app --cov-report=term-missing tests/ # Frontend cd frontend npm run typecheck # tsc --noEmit npm run lint # ESLint npm run test # vitest npm run build # production build ``` Покрытие unit-тестами: - `app.core.time_utils` — парсинг/advance времени, дельты - `app.core.security` — JWT, bcrypt, валидация пароля - `app.core.state_validator` — валидация state/world, apply_patch (set/inc/dec/append/remove) - `app.core.embeddings.HashEmbedder` — детерминизм, нормализация, размерность - `app.core.llm.MockLlmClient` — replay, исчерпание, запись вызовов, стриминг - `app.engine.tools.base.ToolRegistry` — регистрация, диспетч, unknown tool, исключения - `app.engine.tools.game.CalcTool` — арифметика, кубики, переменные, ошибки - `app.engine.tools.game.RandomChoiceTool` — детерминизм, веса - `app.engine.sse.SseEmitter` — emit/done/error, ID, сериализация - `app.prompts.registry` — все 11 stage-промптов валидны --- ## Production-деплой См. `docs/AI-RPG_TZ_TDD.md` §13 (DevOps / Deployment) и §18.3 (Pre-deploy checklist). Ключевые моменты: 1. **SECRET_KEY** — сгенерировать через `python -c "import secrets; print(secrets.token_urlsafe(48))"`. 2. **ADMIN_SETUP_TOKEN** — задать в `.env` ИЛИ оставить пустым (тогда сгенерируется случайно при первом старте, напечатается в логах). 3. **HTTPS** — terminate TLS на nginx или на внешнем reverse-proxy. 4. **Backups** — ежедневно `pg_dump` + Qdrant snapshot в S3. 5. **Health-check** — `GET /api/health` должен вернуть 200 с `db:true, qdrant:true`. 6. **Мониторинг** — structlog пишет JSON в stdout, забирается любой log-агрегатор. --- ## Лицензия MIT — см. `LICENSE` (если отсутствует, предполагается MIT). --- ## Changelog ### v1.0.0 (2026-06-20) - Первый release. Реализованы все 8 спринтов из ТЗ: - Sprint 1: Foundation (docker-compose, БД, Qdrant, миграции, seed, health-check) - Sprint 2: Auth + Admin (JWT, регистрация, админ-панель настроек, тесты LLM/embeddings) - Sprint 3: World Builder (LLM-клиент, промпты, SSE-стрим генерации мира) - Sprint 4: World Editor (чат-редактор, propose_changes, ask_user, optimistic lock) - Sprint 5: Orchestrator (3 фазы, retry/rollback) - Sprint 6: Frontend polish (i18n en/ru, тёмная тема, responsive, SSE reconnect) - Sprint 7: RAG + Triggers + Summary + Context Manager - Sprint 8: Production (README, метрики, тесты, сборка ZIP)