Тестовое задание по разработке аналитического сервиса для работы с видео и замерами статистики.
Проект реализует Telegram-бота, который принимает вопросы на естественном языке (RU), преобразует их в формализованный план запроса и выполняет агрегации по данным в PostgreSQL.
Архитектура построена по принципу LLM → QueryPlan → SQLAlchemy:
- Пользователь задаёт вопрос на русском языке
- LLM (OpenAI) преобразует текст в строго ограниченный JSON-план запроса (
QueryPlan) - План валидируется через Pydantic
- На основе плана динамически строится SQL-запрос
- Результат возвращается пользователю
LLM не генерирует SQL напрямую, а лишь описывает что нужно посчитать, а как — решает серверная логика.
- Python 3.12
- aiogram (Telegram Bot API)
- SQLAlchemy (async)
- PostgreSQL
- Pydantic v2
- OpenAI API (через HTTP/SOCKS proxy)
- Alembic (миграции)
video_bot/
├── bot/
│ ├── main.py # точка входа бота
│ └── handlers.py # обработчики сообщений
├── db/
│ ├── models.py # SQLAlchemy модели
│ ├── session.py # 2 сессии. 1 async для бота 2 sync для load_json и миграций
│ ├── queries.py # выполнение QueryPlan
│ └── base.py # экземпляр DeclarativeBase
├── llm/
│ ├── client.py # OpenAI client
│ ├── prompt.py # system prompt
│ └── schemas.py # Pydantic схемы (QueryPlan)
├── services/
│ ├── dispatcher.py # диспетчер выполнения запросов
│ └── clean_response # очистка ответа от llm от мусора
├── config.py # настройки проекта
├── alembic/ # миграции БД
└── README.md
LLM возвращает JSON строго следующего вида:
{
"source": "videos | snapshots",
"select": {
"type": "aggregate",
"func": "count | sum",
"field": "id | creator_id | views_count | delta_views_count | video_created_at",
"distinct": true
},
"filters": [
{
"field": "created_at | video_created_at | creator_id | views_count | delta_views_count",
"op": "= | > | < | >= | <= | between",
"value": "..."
}
]
}- ❌ нет вложенных запросов
- ❌ нет JOIN'ов от LLM
- ❌ нет подзапросов в фильтрах
- ❌ нет raw SQL от LLM
Это гарантирует безопасность и предсказуемость.
Примеры вопросов, которые корректно обрабатываются:
- Сколько видео опубликовано за период
- Сколько разных креаторов имеют видео с > N просмотров
- Суммарный прирост просмотров за период
- Суммарные просмотры видео, опубликованных в определённый месяц
- Подсчёт уникальных календарных дней публикаций
- Анализ замеров (snapshots) с учётом времени
Все проверки выполняются через SQLAlchemy.
OpenAI API вызывается через HTTP / SOCKS5 proxy:
http_client = httpx.Client(proxy=settings.PROXY_URL)
client = OpenAI(api_key=..., http_client=http_client)Это позволяет изолировать трафик OpenAI от остального приложения (Telegram работает напрямую).
git clone https://github.com/artem-sitd/video_bot.git2.cd video_bot- Создаем вирт. окружение
python3 -m venv venv2. Активируем егоsource venv/bin/activate - Устанавливаем зависимости
pip install -r requirements.txt - Копируем .env
cp .env.example .env - Создаем БД в postgres любым вашим способом
CREATE DATABASE videos_db;
CREATE USER videos_user WITH PASSWORD '123';
ALTER DATABASE videos_db OWNER TO videos_user;
-
Заполняем .env:
- DB_HOST=localhost
- DB_PORT=5432 # стандартный порт
- DB_NAME=videos_db # название вашей бд
- DB_USER=videos_user # имя пользователя с правами на эту базу
- DB_PASSWORD=123 # пароль БД
- BOT_TOKEN= # токен телеграмм бота
- OPENAI_API_KEY= # ключ от АПИ openai
- LOGIN= # логин вашего прокси
- PASS= # пароль вашего прокси
- HOST= # ip адресс вашего прокси
- PORT= # порт вашего прокси
-
Применить миграции:
alembic revision --autogenerate -m "init tables"
alembic upgrade head
- Запустить скрипт на заполнение тестовыми данными videos.json
python3 app/loader/load_json.py
- Запустить бота:
python3 bot/main.py
Проект реализован в рамках тестового задания.
Основной фокус:
- корректная интерпретация естественного языка
- строгая валидация входных данных
- отсутствие SQL-инъекций
- поддержка нетривиальных аналитических кейсов
Все автотесты проверяющей системы успешно пройдены.
- Архитектура сознательно ограничивает возможности LLM
- Расширение логики происходит на стороне сервера, а не через промпт
- Решение ориентировано на читаемость и предсказуемость, а не на "магический" SQL
Artem Sitd
Python Backend Developer