Как собрать Telegram-бота для чата с помощью нейросети
Мы делали не просто Telegram-бота с ответами по API. Мы собирали бота для чата, который общается в заданной манере, помнит недавний контекст и включается только в нужных ситуациях.
Нам был нужен бот с характером. Конкретно - 40-летний скуф-эксперт по нейросетям. Язвительный. Колкий. Без мата. Без прямой грубости. Прям как маскот телеграм-канала Нейроскуф.
Для этого мы использовали:
- Python.
- aiogram.
- Cerebras API.
- python-dotenv.
- Gemini как инструмент разработки и сборки логики.
Gemini помогал нам писать и допиливать код. Сам бот потом работал уже на подключенной модели через API.
С чего мы начали
Сначала мы не писали код. Сначала мы сформулировали задачу.
Нужно было определить:
- где бот отвечает всегда,
- где бот молчит,
- как он должен говорить,
- сколько контекста он должен помнить,
- как его звать в группе,
- как не превратить его в спам-машину.
Без этого дальше идти бессмысленно. Бот без правил быстро становится либо скучным, либо раздражающим.
Почему мы делали это через Gemini
Мы собирали бота через Gemini поэтапно.
Сначала дали общее ТЗ. Потом получили каркас. Потом отдельно уточнили поведение в группах. Потом память. Потом триггерное слово. Потом характер. Потом правили формулировки и чистили лишнее.
Такой подход всегда работает на отлично, когда у задачи основная сложность не в объеме кода, а в поведении.
Отдельно аккаунт не покупали сделали все на базе удобной платформы SYNTX (а для моих читателей по промокоду NEIROSKUF - вы получите еще и скидку в 15% на любой тариф).
Вот базовые промпты, с которых можно начать самостоятельно.
Шаг 1. Попросили собрать базовый каркас
Сначала мы дали Gemini самый простой запрос: собрать Telegram-бота на Python с aiogram и подключением внешней модели через API.
На этом этапе нам был нужен только каркас, чтобы проверить - насколько нейрость поняла нашу простую задачу.
Шаг 2. Добавили правила ответа в личке и группе
Когда каркас уже был, мы отдельно уточнили, где бот должен отвечать, а где молчать.
Шаг 3. Добавили кодовое слово
Потом отдельно ввели скрытый триггер для чата.
Шаг 4. Добавили память диалога
Следующим шагом мы сделали короткую память, чтобы бот не отвечал на каждое сообщение как с чистого листа.
Шаг 5. Отдельно задали характер
Только после этого мы начали настраивать личность бота.
Почему мы не сделали это не одним промптом? Тут важно понимать, что самостоятельно и безконтрольно доверить нейросети доведение проекта полностью сразу - может грозить утратой контекста или части логики. Поэтому формула "сделал-проверил-продолжил" - работает намного лучше, чем "сделай сразу".
Какой стек мы выбрали
Python взяли как базу. Aiogram - для работы с Telegram. Cerebras - для генерации ответов через внешнюю модель. Dotenv - для хранения настроек и ключей.
Для первой версии этого хватило.
Как выглядел проект
На старте не нужна сложная архитектура. Сначала нужно получить работающего бота. Потом уже дробить код на модули.
Потом активировали окружение.
На Windows:
После этого установили зависимости:
Можно сразу зафиксировать их в requirements.txt:
Что мы вынесли в .env
Мы не хранили токены в коде. Все основные настройки вынесли в .env.
Пример:
В main.py собрали базовые объекты:
Как мы сделали память диалога
Боту нужен контекст. Иначе он отвечает отдельно на каждую реплику и не держит разговор.
Для первой версии мы не подключали базу. Мы сделали обычный словарь в памяти процесса.
Вот функция:
Что это дает:
- бот помнит последние сообщения,
- у каждого чата своя история,
- контекст не разрастается бесконечно.
Задаем правила ответа
Это один из ключевых блоков. Бот не должен отвечать в группе на все подряд.
Мы сделали триггеры.
В личке он отвечает всегда.
В группе - только в трех случаях:
- если его упомянули по username
- если пользователь ответил на его сообщение
- если в тексте есть кодовое слово @skuf
Функция получилась такой:
Собрали сообщения для модели
Когда фильтр сработал, нужно было собрать данные для запроса к модели.
Мы передавали три части:
- system prompt,
- недавнюю историю чата,
- текущее сообщение пользователя.
Функция выглядела так:
Этого хватало, чтобы бот не терял нить разговора.
Как мы подключили генерацию ответа
Дальше мы сделали отдельную функцию для вызова модели:
Здесь у нас три параметра:
- model - выбранная модель,
- temperature - степень вариативности,
- max_completion_tokens - длина ответа.
Для чата длинные ответы обычно не нужны. Поэтому мы сразу ограничили генерацию.
Как выглядел обработчик сообщений
После этого мы связали все вместе в одном обработчике:
Этот блок делает весь основной цикл:
- получает сообщение,
- проверяет триггеры,
- сохраняет реплику пользователя,
- отправляет запрос в модель,
- сохраняет ответ,
- отправляет ответ в чат.
Запустили бота
И... ничего.
Почему бот сначала не видел @skuf в группе
Это отдельный момент. Кодовое слово не заработало сразу. Причина была не в коде.
Проблема сидела в настройках Telegram-бота. По умолчанию бот в группе может не видеть часть сообщений. Из-за этого он просто не получает текст с вашим триггером.
Мы решили это так:
- зашли в @BotFather,
- выбрали бота,
- открыли Bot Settings,
- открыли Group Privacy,
- отключили privacy mode,
- удалили бота из группы,
- добавили его заново.
После этого бот начал видеть обычные сообщения в группе и реагировать на @skuf.
Почему мы выбрали Cerebras
Мы выбрали Cerebras, потому что у него есть бесплатный тариф с лимитами, которых хватает для запуска и тестирования Telegram-бота. Нам не нужно было сразу платить за инференс, поднимать свою модель или собирать отдельную инфраструктуру. Мы хотели быстро проверить механику: ответы в личке, работу в группе, триггерное слово, память диалога и характер. Для этого бесплатных лимитов Cerebras оказалось достаточно.