2026-06-20 19:13:05 +03:00
# AI-RPG — Text RPG with an LLM Game Master
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
> **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
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
AI-RPG — это веб-приложение, в котором игрок ведёт текстовую ролевую игру с ИИ-мастером (GM). Игрок создаёт мир (или выбирает готовый пресет), настраивает персонажа, и далее вступает в пошаговое взаимодействие: каждое действие игрока обрабатывается трёхфазным orchestrator-ом, который генерирует нарратив, обновляет состояние мира через tool calls, и предлагает 1-3 следующих действия.
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
Полное Т З — в `docs/AI-RPG_TZ_TDD.md` .
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
---
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
## Возможности (v1.0.0)
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
- ✅ **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-клиент с автопереподключением.
---
2026-06-19 11:28:04 +03:00
## Архитектура
```
2026-06-20 19:13:05 +03:00
┌──────────────────────────────────────────────────────────────┐
│ Браузер (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) │
└──────────────────────┘
2026-06-19 11:28:04 +03:00
```
2026-06-20 19:13:05 +03:00
---
## Быстрый старт
### Опция 1: docker-compose (рекомендуется)
2026-06-19 11:28:04 +03:00
```bash
2026-06-20 19:13:05 +03:00
# 1. Скопировать .env.example в .env и отредактировать
cp .env.example .env
# Отредактируйте SECRET_KEY, ADMIN_SETUP_TOKEN, LLM_API_URL, LLM_API_KEY
2026-06-19 11:28:04 +03:00
2026-06-20 19:13:05 +03:00
# 2. Поднять всё
2026-06-20 22:21:47 +03:00
docker compose up -d --build
2026-06-20 19:13:05 +03:00
2026-06-20 22:21:47 +03:00
# 3. Открыть в браузере:
# - Frontend (nginx + React build): http://localhost:8080
# - Backend Swagger docs: http://localhost:8000/api/docs
# - Health-check: http://localhost:8000/api/health
2026-06-20 19:13:05 +03:00
```
2026-06-19 11:28:04 +03:00
2026-06-20 22:21:47 +03:00
При первом старте backend напечатает в лог:
2026-06-19 11:28:04 +03:00
```
2026-06-20 19:13:05 +03:00
=== AI-RPG Admin Setup ===
No admin user yet. Open this URL in your browser:
/register/admin?token=<ADMIN_SETUP_TOKEN>
===========================
```
2026-06-20 22:21:47 +03:00
Посмотреть лог: `docker compose logs backend | head -20` .
Откройте `http://localhost:8080/register/admin?token=<...>` и создайте первого админа.
> **Примечание про LLM:** если у вас Ollama на хосте, используйте `LLM_API_URL=http://host.docker.internal:11434/v1` — backend-контейнер автоматически резолвит `host.docker.internal` через `extra_hosts: host-gateway` (работает на Linux/macOS/Windows).
2026-06-20 19:13:05 +03:00
### Опция 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` .
---
2026-06-19 11:28:04 +03:00
## Структура проекта
```
ai-rpg/
2026-06-20 19:13:05 +03:00
├── 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 # оригинальное Т З
2026-06-19 11:28:04 +03:00
├── docker-compose.yml
2026-06-20 19:13:05 +03:00
├── requirements.txt
├── pytest.ini
2026-06-19 11:28:04 +03:00
├── .env.example
2026-06-20 19:13:05 +03:00
└── 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
2026-06-19 11:28:04 +03:00
```
2026-06-20 19:13:05 +03:00
Покрытие 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-агрегатор.
---
2026-06-19 11:28:04 +03:00
## Лицензия
2026-06-20 19:13:05 +03:00
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)