Files
ai-rpg/README.md
2026-06-21 01:02:25 +03:00

337 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 --build
# 3. Открыть в браузере:
# - Frontend (nginx + React build): http://localhost:8080
# - Backend Swagger docs: http://localhost:8000/api/docs
# - Health-check: http://localhost:8000/api/health
```
При первом старте backend напечатает в лог:
```
=== AI-RPG Admin Setup ===
No admin user yet. Open this URL in your browser:
/register/admin?token=<ADMIN_SETUP_TOKEN>
===========================
```
Посмотреть лог: `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).
### Опция 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)