Развернуть Prompt Registry
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Развернуть Prompt Registry
1. Цель задачи
Спроектировать и развернуть Prompt Registry — централизованное хранилище промптов с поддержкой версионирования, кэшированием через Redis и REST API. Научиться управлять жизненным циклом промптов: создание, обновление, получение актуальной версии, откат. Полученный сервис станет основой для A/B-тестирования промптов и обеспечения консистентности в RAG-цепочках.
Ключевой результат Работающий HTTP-сервис, где prompts{id}GET /prompts/{id}/latest возвращает последнюю активную версию промпта за <50 мс (с кэшем) и корректно обрабатывает версионирование.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Среда выполнения (Linux/macOS/WSL) | Локальная машина разработчика |
| Установленные Docker и Docker Compose | Официальные инструкции |
| Репозиторий с кодом (шаблон FastAPI) | Создать самостоятельно по описанию ниже |
| Доступ к PostgreSQL (через Docker) | docker-compose.yml |
| Доступ к Redis (через Docker) | docker-compose.yml |
| Набор тестовых промптов (5–10 штук) | Придумать самостоятельно (например, для суммаризации, QA, классификации) |
Если нет реального инструмента — симулируем:
- Создаём пустой Git-репозиторий
prompt-registry. - Используем файл docker-compose.yml для поднятия PostgreSQL (postgres:15-alpine) и Redis (redis:7-alpine).
- Все API-запросы выполняем через curl или Postman.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Backend-фреймворк | FastAPI (Python 3.11+) | REST API, валидация, документация OpenAPI |
| База данных | PostgreSQL 15 | Хранение версий промптов и метаданных |
| Кэш | Redis 7 | Кэширование последней версии промпта |
| ORM | SQLAlchemy 2.0 + asyncpg | Асинхронная работа с PostgreSQL |
| Миграции | Alembic | Управление схемой БД |
| Клиент Redis | redis-py (aioredis) | Кэширование |
| Контейнеризация | Docker Compose | Локальный запуск стека |
| Тестирование | pytest + httpx | Интеграционные тесты |
4. Этапы выполнения
Этап 1: Проектирование схемы данных и API (30–45 минут)
Действия
- Определить сущность Prompt: поля
id(UUID),name(строковый ключ, уникальный), template (текст промпта),version(целое число, инкремент),created_at,updated_at,is_active(булево — текущая активная версия). - Спроектировать таблицу prompts:
CREATE TABLE prompts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL, -- логическое имя промпта
version INTEGER NOT NULL DEFAULT 1, -- версия для данного name
template TEXT NOT NULL, -- сам промпт с плейсхолдерами {{var}}
variables JSONB NOT NULL DEFAULT '[]', -- описание переменных
is_active BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(name, version) -- уникальность пары name+version
);
- Спроектировать API endpoints:
POST /prompts— создать новый промпт (name, template, variables). Версия = 1, is_active = TRUE.- PUT /prompts/{name} — создать новую версию (версия = max(version)+1, предыдущая is_active = FALSE).
GET /prompts/{name}/latest— вернуть последнюю активную версию (сначала из Redis, затем из БД).GET /prompts/{name}/versions— список всех версий.POST /prompts/{name}/rollback?version=N— откат к указанной версии (сделать её активной).
- Определить структуру ответов: { "name": "...", "version": N, "template": "...", "variables": [...] }.
Ожидаемый результат этапа Файл schema.sql или Alembic-миграция, файл api_spec.yaml (или описание в README).
Этап 2: Настройка инфраструктуры (Docker Compose) (30 минут)
Действия
- Создать docker-compose.yml:
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: prompt_registry
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
ports: ["5432:5432"]
volumes: [pgdata:/var/lib/postgresql/data]
redis:
image: redis:7-alpine
ports: ["6379:6379"]
app:
build: .
ports: ["8000:8000"]
depends_on: [db, redis]
environment:
DATABASE_URL: postgresql+asyncpg://user:pass@db:5432/prompt_registry
REDIS_URL: redis://redis:6379/0
volumes:
pgdata:
- Создать Dockerfile для FastAPI-приложения (использовать python:3.11-slim).
- Создать файл .env с переменными для локального запуска.
Ожидаемый результат этапа Контейнеры стартуют, приложение подключается к БД и Redis (можно проверить логами).
Этап 3: Реализация CRUD API для промптов (1.5–2 часа)
Действия
- Инициализировать FastAPI-приложение с роутерами prompts.py.
- Настроить SQLAlchemy: модели, async-сессия.
- Реализовать create_prompt(name, template, variables):
- Проверить, что
nameне существует (иначе ошибка 409). - Вставить запись с
version = 1,is_active = TRUE. - Очистить кэш по ключу prompt:{name}:latest.
- Проверить, что
- Реализовать update_prompt(name, template, variables):
- Найти максимальную версию.
- Новая запись
version = max+1,is_active = TRUE. - Старой активной версии установить
is_active = FALSE. - Очистить кэш.
- Реализовать
get_latest_prompt(name): - Реализовать
get_versions(name)— список всех версий. - Реализовать
rollback_prompt(name, version):- Установить
is_active = FALSEдля текущей активной. - Установить
is_active = TRUEдля указанной версии. - Очистить кэш.
- Установить
Ожидаемый результат этапа Все эндпоинты работают локально; GET /prompts/{name}/latest возвращает данные за <50 мс (с кэшем).
Этап 4: Кэширование с Redis и отказоустойчивость (45 минут)
Действия
- Реализовать сервис cache.py для работы с Redis (асинхронно).
- Для
get_latest: - Обработка недоступности Redis:
- Обернуть вызовы в try-except, при ошибке падать в БД (graceful degradation).
- Логировать предупреждение.
- Добавить health-check эндпоинт GET /health, проверяющий подключение к БД и Redis.
Ожидаемый результат этапа При отключённом Redis сервис продолжает работать (через БД), задержка увеличивается до ~100-200 мс.
Этап 5: Тестирование и валидация (1 час)
Действия
- Написать интеграционные тесты
test_prompts.py(pytest + httpx):test_create_prompt— создание, проверка ответа 201.test_create_duplicate_name— ошибка 409.test_update_creates_new_version— обновление, проверка инкремента версии.test_latest_returns_active— после обновления latest возвращает новую версию.test_rollback— откат к предыдущей, проверка is_active.test_cache_hit— два запроса, второй должен быть быстрее (из кэша).test_redis_down— отключить Redis, проверить что запросы проходят через БД.
- Запустить тесты в CI-окружении (опционально — GitHub Actions с docker compose).
- Задокументировать API в README (использовать OpenAPI-документацию, которая автоматически генерируется FastAPI).
Ожидаемый результат этапа Все тесты проходят, покрытие ключевых сценариев 100%.
5. Критерии приемки (Definition of Done)
- Сервис запускается одной командой docker compose up --build.
-
POST /promptsсоздаёт первую версию, возвращаетHTTP 201. -
PUT /prompts/{name}создаёт новую версию, старая становится неактивной. -
GET /prompts/{name}/latestвозвращает последнюю активную версию. -
GET /prompts/{name}/versionsвозвращает список всех версий с метаданными. -
POST /prompts/{name}/rollback?version=Nкорректно переключает активную версию. - Redis-кэш для
latestработает (TTL ≤ 5 мин, очищается при изменении). - При недоступности Redis сервис продолжает отвечать (через БД).
- OpenAPI-документация доступна по
/docs. - Все интеграционные тесты (≥6) проходят.
6. Ожидаемый результат
Основной артефакт Git-репозиторий prompt-registry со следующей структурой:
prompt-registry/
├── docker-compose.yml
├── Dockerfile
├── .env.example
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI приложение
│ ├── database.py # SQLAlchemy engine, session
│ ├── models.py # ORM-модель Prompt
│ ├── schemas.py # Pydantic-схемы
│ ├── routers/
│ │ └── prompts.py # REST endpoints
│ ├── services/
│ │ └── cache.py # Redis клиент
│ └── migrations/ # Alembic
├── tests/
│ └── test_prompts.py
└── README.md
Содержание README.md описание архитектуры, инструкция по запуску, примеры запросов (curl), ссылка на /docs.
Дополнительно (опционально): Скрипт для загрузки демо-данных (seed.py).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Конфликт версий при параллельных запросах | Использовать SELECT ... FOR UPDATE в транзакции при создании новой версии (или оптимистичную блокировку через version). |
| Redis не запущен (или ошибка подключения) | Graceful degradation: ловить исключение ConnectionError, возвращать данные из БД. |
Асинхронный код: session закрыта до выполнения запроса | Использовать зависимости FastAPI с async with session.begin(). |
| Миграции Alembic не применяются | Настроить autogenerate и команду alembic upgrade head в entrypoint. |
| Долгие запросы без кэша | Установить индекс на (name, is_active) в PostgreSQL. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Проектирование | 30–45 мин |
| Этап 2: Инфраструктура (Docker) | 30 мин |
| Этап 3: Разработка CRUD API | 1.5–2 ч |
| Этап 4: Кэширование + fallback | 45 мин |
| Этап 5: Тестирование | 1 ч |
| Итого (чистое время) | 4–5 часов |
| С учётом отладки (×1.5) | 6–7.5 часов |
Примечание: Для первого раза заложите +2 часа на неожиданные проблемы с Docker и Alembic.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | Создание REST API на FastAPI |
| 73 | Работа с SQLAlchemy async |
| 108 | Redis: основные структуры, TTL |
| 205 | Docker Compose для стека Python+PostgreSQL |
| 311 | Интеграционное тестирование с httpx |
| 456 | Управление конфигурацией через .env |
| 521 | Alembic: создание миграций |
| 634 | Graceful degradation и error handling |
| 748 | Оптимистичная блокировка и сериализация |
| 829 | OpenAPI-документация: настройка |
10. Чек-лист самопроверки
- Я запустил
docker compose upи все контейнеры стартуют без ошибок. - Я проверил
GET /prompts/my-prompt/latest— возвращает последнюю активную версию. - Я выполнил
PUT /prompts/my-promptи убедился, что версия увеличилась, а старая стала неактивной. - Я отключил Redis (
docker stop prompt-registry_redis_1) и повторил запрос — ответ пришёл из БД. - Я запустил
pytest tests/и все тесты зелёные. - Я открыл
http://localhost:8000/docsи увидел полную OpenAPI-спецификацию. - В README есть инструкция по запуску и примеры curl-запросов.