8.3 KiB
Qwen Telegram Bot
Telegram-бот на Python с четырьмя функциями:
- 📷 описание изображений — присылаешь фото, бот возвращает текстовое описание через vision-модель Qwen
- 📊 генерация графиков — по текстовому запросу бот просит LLM написать код на matplotlib, выполняет его и отправляет PNG
- 🏛 архитектурные схемы — по описанию системы LLM генерирует DOT, Graphviz рендерит это в PNG
- ♻️ воспроизведение с фото — кидаешь фото графика или архитектурной схемы, бот распознаёт тип, извлекает структуру и отрисовывает заново в своём стиле
Стек
- python-telegram-bot — асинхронный клиент Telegram Bot API
- openai SDK — работа с любым OpenAI-совместимым провайдером (OpenRouter, DashScope, vLLM и др.)
- matplotlib + numpy — рендеринг графиков
- Graphviz + python-graphviz — рендеринг архитектурных схем из DOT
- uv — менеджер пакетов и виртуальных окружений
Требования
- Python 3.11+
- установленный
uv(инструкция) - бинарь Graphviz в
$PATH(нужен для команды/arch):- Linux:
sudo apt install graphviz - macOS:
brew install graphviz - Windows: скачать с graphviz.org
- Linux:
- токен Telegram-бота от @BotFather
- API-ключ провайдера, поддерживающего мультимодальную модель Qwen
Установка
git clone <https://git.lambda.coredump.ru/BtoB_team/image_recognition.git>
cd image_recognition
# создаёт .venv и ставит зависимости из pyproject.toml + uv.lock
uv sync
Конфигурация
Скопируй шаблон и впиши свои ключи:
cp .env.example .env
⚠️ Для функции описания изображений модель должна быть мультимодальной (VL). Текстовая модель упадёт при попытке передать ей картинку.
Запуск
uv run bot.py
Бот запускается в режиме long-polling и пишет лог в stdout.
Использование
| Действие | Что делает бот |
|---|---|
/start или /help |
Показать справку |
| Отправить фото (без подписи-команды) | Вернуть описание изображения |
/plot <описание> |
Сгенерировать и прислать matplotlib-график |
/arch <описание> |
Нарисовать архитектурную схему через Graphviz |
/repro + фото |
Распознать график/схему и отрисовать её заново |
Примеры запросов к /plot:
/plot sin(x) и cos(x) на отрезке [0, 2π]
/plot затухающая синусоида на [0, 10]
/plot столбчатая диаграмма продаж по месяцам с рандомными значениями
/plot гистограмма нормального распределения, 1000 сэмплов
Примеры запросов к /arch:
/arch React-фронт, FastAPI-бэкенд, Postgres и Redis за nginx
/arch микросервисы: auth, orders, payments. Общаются через RabbitMQ, у каждого своя БД Postgres, Prometheus собирает метрики
/arch телеграм-бот, принимает вебхуки, складывает задачи в очередь Celery с Redis-брокером, воркеры пишут результаты в S3
Как пользоваться /repro
Два варианта:
- Прикрепить фото с подписью
/repro— бот сразу обработает его. - Ответить
/reproна сообщение с фото (reply-to-message).
Бот классифицирует изображение как график (plot) либо архитектурную схему (architecture), извлекает структуру через vision-модель, генерирует для неё matplotlib-код или DOT и отрисовывает заново. Это не попиксельная копия — это реконструкция: бот старается передать тот же тип графика/те же компоненты и связи, но стиль будет его собственный.
Структура проекта
.
├── bot.py # основной скрипт: конфиг, LLM-клиент, хэндлеры Telegram
├── pyproject.toml # зависимости и метаданные проекта для uv
├── uv.lock # фиксация версий (создаётся после uv sync)
├── .env.example # шаблон переменных окружения
└── .env # реальные ключи (в git не коммитить!)
Работа с зависимостями
uv add <пакет> # добавить зависимость
uv remove <пакет> # убрать зависимость
uv sync # синхронизировать .venv с lock-файлом
uv run <команда> # выполнить команду в окружении проекта
⚠️ Безопасность
Код для построения графиков генерируется LLM и исполняется через exec в урезанном неймспейсе (без import, без open, с ограниченным набором builtins). Этого достаточно для локального использования и dev-стенда, но это не песочница.
Архитектурные схемы безопаснее: Graphviz парсит DOT как данные, а не исполняет его как код, поэтому инъекция через промпт там в худшем случае даст кривую или огромную картинку.
Для публичного бота стоит изолировать исполнение /plot, например:
- отдельный воркер-процесс с
resource.setrlimit(CPU, память, время) - Docker-контейнер без сети и с read-only FS
- nsjail / firejail
- вместо
exec— разобрать запрос в ограниченный DSL, а уже его превратить в matplotlib-вызовы детерминированным кодом
Возможные улучшения
- webhook вместо polling для продакшена
- очередь задач (arq / Celery) для тяжёлого рендеринга
- кэш описаний по хэшу изображения
- вывод сгенерированного кода графика в подписи к картинке (дебаг-режим)
- rate-limiting на пользователя
- тесты для
render_plotс фиксированными промптами