Создаём Telegram бота с aiogram3 и llama3-7B
Пара слов
Итак, будем писать простого чат бота для telegram с локальной llama3 8B (даешь опенсорс!). Я всё буду делать на linux, какие-то не очевидные моменты для windows буду подсвечивать, в остальном - всё примерно тоже самое, я буду писать что конкретно делаю, а вы сможете загуглить, как оно там на винде работает - в 90% случаев команды те же самые. Предполагается, что вы можете создать и активировать виртуальное окружение самостоятельно, всё остальное я буду показывать поэтапно.
Создание проекта, выбор модели
- Создаём директорию проекта
- Создаём виртуальное окружение и активируем его
- Создаём папку models, кидаем туда файлы моделей
Модельки ламы можно скачать отсюда, тут лежат сжатые версии, кратко по сжатию:
Quantinized версии моделей позволяют снизить требования по железу, при этом у них снижается качество комплитов.
Изначально точность несжатой модели - 16bit, однако, 8bit модельки почти такие же хорошие, как и их несжатые версии, при этом требуют значительно меньше ресурсов, вы скорее всего сможете запустить llama3-Q8 даже на CPU ноутбука.
Ещё одно правило - количество параметров превалирует над силой сжатия, например сжатая llama3-70B всё равно будет работать лучше чем несжатая 8B.
Исходя из этого, 8Q даст нам лучшее соотношение скорость/качество, её и скачаю, заодно и ещё одну, 2Q - посмотреть, поиграться :)
В итоге, получилось как-то так (я ещё дополнительно создал git репозиторий для удобства)
Подготовка к созданию Telegram бота
Установим aiogram3
в наше окружение
pip install aiogram pip install python-dotenv
Добавим сразу это всё в файл requirements.txt, я для удобства это делаю одной командой, она создаст файл, если он ещё не существует и добавит туда строчку из pip freeze
, по совпадению "aiogram"
pip freeze | grep aiogram >> requirements.txt pip freeze | grep python-dotenv >> requirements.txt
Пишем BotFather, создаём там бота и получаем токен, кладём его в .env файл, переменную назовём BOT_TOKEN
Пишем hello world
на aiogram3
Создадим два файла, message_router.py
и bot.py
.
Вот что будет в message_router.py
:
from aiogram import Router from aiogram.filters import Command from aiogram.types import Message router = Router() @router.message(Command("start")) async def cmd_start(message: Message): await message.answer(f"hello world!")
Очень кратко по функционалу - @router.message
будет отслеживать любое сообщение боту, Command
это фильтр команд, то есть следующая функция будет срабатывать на любое сообщение боту, которое содержит команду /start
. Этот router
мы сейчас прицепим на главный роутер - диспетчер.
import asyncio import logging import sys import os from aiogram import Bot, Dispatcher from aiogram.client.default import DefaultBotProperties from dotenv import load_dotenv, find_dotenv import message_router # тот самый роутер load_dotenv(find_dotenv()) async def main() -> None: bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode="HTML")) await bot.delete_webhook(drop_pending_updates=True) dp = Dispatcher() dp.include_routers(message_router.router) # тут добавляем наши роутеры await dp.start_polling(bot) if __name__ == "__main__": BOT_TOKEN = os.getenv("BOT_TOKEN") # получаем токен бота из переменной окружения if BOT_TOKEN is None: raise ValueError("BOT_TOKEN is not set") logging.basicConfig(level=logging.INFO, stream=sys.stdout) asyncio.run(main())
Тут всё просто, сначала получаем токен бота, потом в главной функции создаём объект Bot
, создаём Dispatcher
, крепим к нему наш роутер и запускаем polling.
Запустим бота, можно пойти и убедиться что он отвечает на /start
.
python3 main.py
Запуск сервера llama.cpp
Тут предлагаю два варианта - с использованием зелёной карточки, или на процессоре. Для красных карточек процесс не сложнее, вам нужно будет поставить драйверы OpenCL и тут найти нужную опцию установки.
Если позже захотите переустановить сервер с другими настройками, то добавьте эти флаги --no-cache-dir --force-reinstall --upgrade
Для CPU
pip install 'llama-cpp-python[server]'
А запускать сервер находясь в папке с проектом вот так
python3 -m llama_cpp.server --model models/<Имя модели>.gguf
Для Nvidia
Если у вас стоит GPU, то советую выбрать этот вариант, так как скорость генерации на видеокарте гораздо выше.
Cначала нужно установить CUDA Toolkit, сделать это можно тут. Просто выбираете параметры своей системы и получаете готовую инструкцию. Если вы на Windows, всё делается через один installer, если на Linux, не забудьте поставить ещё и драйверы, они сразу за основной инструкцией.
После установки CUDA, установим сам llama.cpp сервер
CUDACXX=/usr/local/cuda-12/bin/nvcc CMAKE_ARGS="-DLLAMA_CUBLAS=on -DCMAKE_CUDA_ARCHITECTURES=native" FORCE_CMAKE=1 pip install 'llama-cpp-python[server]'
И чтобы запустить его, команда такая же как и для CPU варианта, только дополнительно определяем сколько задач мы хотим отдать на саму карточку
python3 -m llama_cpp.server --model models/<Имя модели>.gguf --n_gpu_layers 35
Чтобы определить оптимальное количество слоёв, на linux можно воспользоваться утилитой nvidia-msi, пока-что можете выставить совсем немного 5-10, позже сможем протестировать, во время работы бота
watch -n 0.5 nvidia-smi
Соединяем всё вместе
давайте немного отредактируем наш код, почти полностью перепишем наш message_router
:
from aiogram import Router from aiogram.types import Message import openai router = Router() history = [ { "role": "system", "content": "You are an intelligent assistant." } ] @router.message() async def any_message(message: Message, client: openai.Client): history.append({"role": "user", "content": message.text}) completion = client.chat.completions.create( model="local-model", messages=history, temperature=0.7, ) content = completion.choices[0].message.content new_message = {"role": "assistant", "content": content} history.append(new_message) await message.answer(content)
Тут всё просто, мы добавляем пришедшее сообщение в историю, после чего модель генерирует комплит, его мы тоже добавляем в историю, а затем - отправляем юзеру, откуда взялся клиент - сейчас покажу.
Теперь дополним пару строк в bot.py
, функция main
теперь выглядит так:
async def main() -> None: bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode="HTML")) await bot.delete_webhook(drop_pending_updates=True) client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed") dp = Dispatcher(client=client) dp.include_routers(message_router.router) await dp.start_polling(bot)
Создали клиента, закинули его в диспетчер, теперь он доступен из любой функции под декораторами роутеров
и не забудем про импорт OpenAI, теперь заголовок файла такой
import asyncio import logging import sys import os from openai import OpenAI from aiogram import Bot, Dispatcher from aiogram.client.default import DefaultBotProperties import message_router
Всё готово, запускаем сервер, ждём пока загрузится моделька, потом запускаем бота и идём проверять в чат
Итоги
Мы сделали простенького телеграм бота с локальной llama3, задачи по улучшению и доработке функционала на вас, например сейчас все сообщения хранятся в оперативной памяти :)
👉🏻Подписывайтесь на PythonTalk в Telegram 👈🏻
Автор: @thisizmyusername