English translation is not available yet. Showing Russian content.

Развернуть 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, классификации)

Если нет реального инструмента — симулируем:

  1. Создаём пустой Git-репозиторий prompt-registry.
  2. Используем файл docker-compose.yml для поднятия PostgreSQL (postgres:15-alpine) и Redis (redis:7-alpine).
  3. Все API-запросы выполняем через curl или Postman.

3. Технологический стек

КомпонентИнструментыНазначение
Backend-фреймворкFastAPI (Python 3.11+)REST API, валидация, документация OpenAPI
База данныхPostgreSQL 15Хранение версий промптов и метаданных
КэшRedis 7Кэширование последней версии промпта
ORMSQLAlchemy 2.0 + asyncpgАсинхронная работа с PostgreSQL
МиграцииAlembicУправление схемой БД
Клиент Redisredis-py (aioredis)Кэширование
КонтейнеризацияDocker ComposeЛокальный запуск стека
Тестированиеpytest + httpxИнтеграционные тесты

4. Этапы выполнения

Этап 1: Проектирование схемы данных и API (30–45 минут)

Действия

  1. Определить сущность Prompt: поля id (UUID), name (строковый ключ, уникальный), template (текст промпта), version (целое число, инкремент), created_at, updated_at, is_active (булево — текущая активная версия).
  2. Спроектировать таблицу 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
);
  1. Спроектировать 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 — откат к указанной версии (сделать её активной).
  2. Определить структуру ответов: { "name": "...", "version": N, "template": "...", "variables": [...] }.

Ожидаемый результат этапа Файл schema.sql или Alembic-миграция, файл api_spec.yaml (или описание в README).

Этап 2: Настройка инфраструктуры (Docker Compose) (30 минут)

Действия

  1. Создать 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:
  1. Создать Dockerfile для FastAPI-приложения (использовать python:3.11-slim).
  2. Создать файл .env с переменными для локального запуска.

Ожидаемый результат этапа Контейнеры стартуют, приложение подключается к БД и Redis (можно проверить логами).

Этап 3: Реализация CRUD API для промптов (1.5–2 часа)

Действия

  1. Инициализировать FastAPI-приложение с роутерами prompts.py.
  2. Настроить SQLAlchemy: модели, async-сессия.
  3. Реализовать create_prompt(name, template, variables):
    • Проверить, что name не существует (иначе ошибка 409).
    • Вставить запись с version = 1, is_active = TRUE.
    • Очистить кэш по ключу prompt:{name}:latest.
  4. Реализовать update_prompt(name, template, variables):
    • Найти максимальную версию.
    • Новая запись version = max+1, is_active = TRUE.
    • Старой активной версии установить is_active = FALSE.
    • Очистить кэш.
  5. Реализовать get_latest_prompt(name):
    • Сначала попробовать получить из Redis по ключу prompt:{name}:latest.
    • Если не найдено — запрос к БД, затем записать в Redis (TTL 300 секунд).
    • Вернуть данные.
  6. Реализовать get_versions(name) — список всех версий.
  7. Реализовать rollback_prompt(name, version):
    • Установить is_active = FALSE для текущей активной.
    • Установить is_active = TRUE для указанной версии.
    • Очистить кэш.

Ожидаемый результат этапа Все эндпоинты работают локально; GET /prompts/{name}/latest возвращает данные за <50 мс (с кэшем).

Этап 4: Кэширование с Redis и отказоустойчивость (45 минут)

Действия

  1. Реализовать сервис cache.py для работы с Redis (асинхронно).
  2. Для get_latest:
    • При записи в кэш использовать SETEX с TTL 5 минут.
    • При очистке — DELETE.
  3. Обработка недоступности Redis:
    • Обернуть вызовы в try-except, при ошибке падать в БД (graceful degradation).
    • Логировать предупреждение.
  4. Добавить health-check эндпоинт GET /health, проверяющий подключение к БД и Redis.

Ожидаемый результат этапа При отключённом Redis сервис продолжает работать (через БД), задержка увеличивается до ~100-200 мс.

Этап 5: Тестирование и валидация (1 час)

Действия

  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, проверить что запросы проходят через БД.
  2. Запустить тесты в CI-окружении (опционально — GitHub Actions с docker compose).
  3. Задокументировать 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 API1.5–2 ч
Этап 4: Кэширование + fallback45 мин
Этап 5: Тестирование1 ч
Итого (чистое время)4–5 часов
С учётом отладки (×1.5)6–7.5 часов

Примечание: Для первого раза заложите +2 часа на неожиданные проблемы с Docker и Alembic.

9. Связанные вопросы из базы знаний

ВопросТема
42Создание REST API на FastAPI
73Работа с SQLAlchemy async
108Redis: основные структуры, TTL
205Docker Compose для стека Python+PostgreSQL
311Интеграционное тестирование с httpx
456Управление конфигурацией через .env
521Alembic: создание миграций
634Graceful degradation и error handling
748Оптимистичная блокировка и сериализация
829OpenAPI-документация: настройка

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-запросов.