Всем привет! Вероятно, у каждого бывало: ты открываешь Телеграм-чат, а там тысячи новых сообщений за день. И где-то внутри этой «солянки» важный ответ на твой вопрос или обсуждение нужной темы. Или вам нужно отслеживать определённые сообщения для бизнес-целей.
Можно, конечно, потратить кучу времени на ручной поиск, но намного интереснее научить юзербота самостоятельно парсить историю чата и составлять из неё удобную базу для поиска по смыслу.
Здесь в дело вступает связка из векторной базы Qdrant и LLM:
-
Юзербот собирает сообщения и превращает их в эмбеддинги.
-
Qdrant хранит эти векторы и по запросу вытаскивает только самые близкие фрагменты.
-
LLM получает именно эти куски и формулирует итоговый ответ человеческим языком.
Если какой-то из терминов для вас непонятен, рекомендуем ознакомиться с нашими прошлыми статьями про работу с Qdrant. Ссылки на них будут предоставлены в конце статьи.
В этой статье разберёмся, как создать такое приложение: от простейшего парсинга чата до поиска по смыслу с помощью LLM.
В чём суть работы по парсингу Telegam через бота
Когда мы говорим «поиск по смыслу», важно понимать, что это не магия, а вполне конкретная цепочка шагов.
Давайте разберёмся со всем по порядку:
1. Сбор сообщений
Первый этап — это юзербот (например, на Telethon). Он подключается к вашему аккаунту Telegram и «слушает» выбранные чаты. Как только приходит новое сообщение, бот его сохраняет. При желании можно догрузить и старую историю, но чаще всего достаточно обрабатывать новые входящие.
Почему именно юзербот? Обычный бот не имеет доступа к истории чата (он видит только то, что пишут при нём). А юзербот = вы сами, только в виде программы, значит, у него полный доступ ко всем сообщениям.
2. Превращение текста в векторы (эмбеддинги)
Чтобы компьютер умел «понимать смысл» сообщений, нужно превратить текст в числовое представление.
Это и есть эмбеддинги — набор чисел (вектор), который описывает не форму слов, а их значение.
Пример:
-
«ошибка при оплате» и «не проходит платеж» — разные наборы слов, но их векторы окажутся близки друг к другу, потому что смысл одинаковый.
-
А вот «сегодня была хорошая погода» будет где-то далеко, потому что тематика другая.
Сейчас есть куча моделей для эмбеддингов: от OpenAI (text-embedding-3-small) до полностью локальных (e5-base, mxbai-embed-large).
3. Хранение в Qdrant
Все полученные векторы мы складываем в векторную базу Qdrant. Она заточена именно под такие данные: быстро ищет ближайшие векторы среди миллионов записей.
Каждое сообщение в базе хранится с нашими настраиваемыми метаданными:
-
idсообщения; -
сам текст;
-
автор (можно анонимизировать);
-
дата и время.
Это значит, что в любой момент мы можем спросить: «Дай мне самые похожие на это сообщение куски чата» и Qdrant найдёт их за миллисекунды.
4. Запрос пользователя
Теперь представим, что вы хотите узнать: «Какие цвета обсуждали вчера?»
Ваш вопрос точно так же превращается в вектор и отправляется в Qdrant.
База ищет самые близкие по смыслу фрагменты чата и возвращает их (например, 5–10 штук).
5. Обработка в LLM
И уже наступает время большой языковой модели (LLM).
Мы берём найденные фрагменты и подкладываем их в промпт:
Вопрос: "Какие цвета обсуждали вчера?" Контекст из чата: 1) [12:30] user1: Мой любимый цвет — черный 2) [12:35] user2: Думаю перекрасить сайт в синий, будет посвежее 3) [12:40] user3: Я за зелёный, он спокойнее смотрится 4) [12:50] user4: Красный лучше привлекает внимание ... Сформулируй ответ кратко и по делу.
LLM видит только этот маленький кусок контекста, а не весь чат целиком, и формулирует итог:
«Вчера обсуждали несколько цветов: чёрный, синий, зелёный и красный.»
6. Что мы получаем в итоге
-
Никаких километров скроллинга.
-
Ответ приходит в человеческом виде.
-
При желании можно прикладывать ссылки на оригинальные сообщения, чтобы проверить контекст, но пока будет достаточно времени.
Таким образом, LLM никогда не падает под весом тысячи сообщений: она работает только с выборкой, которую заранее подготовил Qdrant.
Дополнительно: могут ли за это забанить?
Технически Telegram не одобряет использование юзерботов, так как это доступ к аккаунту через неофициальные клиенты. Но на практике за чтение истории и хранение сообщений риска почти нет. Миллионы людей используют Telethon и Pyrogram. Бан чаще прилетает за спам или агрессивную активность. Для спокойствия можно завести отдельный аккаунт под юзербота, чтобы не рисковать основным.
Где развернем парсер
На помощь в развертывании нам приходит Amvera — сервис для простого и быстрого деплоя IT-приложений.
-
Сервис при регистрации предоставляет бонусные 111 рублей без дополнительных условий. Это дает нам возможность без страха тестировать приложение, дорабатывать его и даже некоторое время покрутить бесплатно.
-
Сервис также предоставляет бесплатное проксирование до OpenAI, Gemini, Grok AI и множества других сервисов, имеющих региональные ограничения.
-
Недавно в Amvera появилась возможность подключить инференс LLM LLaMA, что освобождает нас от нужны покупать через иностранные карты токены от того же OpenAI. Как раз его мы используем для обработки сообщений.
-
Приложение достаточно легко развернуть: достаточно лишь настроить несколько параметров в конфигурации и выполнить 4 команды в термиле для загрузки файлов через git push (и то можно обойтись без них — есть возможность загружать файлы через интерфейс).
Qdrant (как преднастроенный сервис) и юзербота мы развернем именно здесь. Также подключим инференс LLaMA от Amvera.
Почему это важно: удобно, когда доступ ко всем сервисам вашего приложения (в нашем случае — LLM, Qdrant и сам юзербот) лежит на одном аккаунте, а не на множестве разных.
Практическая часть: собираем MVP бота-парсера Telegram
Чтобы не углубляться в тонкие настройки, сделаем минимальный рабочий пример:
-
юзербот читает сообщения из чата;
-
превращает их в эмбеддинги;
-
складывает в Qdrant;
-
а потом по запросу достаёт релевантные куски и отдаёт их в LLM.
Само приложение будет запускаться как два отдельно живущих модуля, выполняющие свои функции. Первый — bot.py будет собирать все новые входящие сообщения. Второй (search.py) — выполнять поиск по ним.
1. Зависимости
В requirements.txt добавим:
telethon==1.36.0 qdrant-client==1.12.1 httpx==0.27.2 python-dotenv==1.0.1 sentence-transformers==5.1.0
2. Переменные окружения
Рекомендуется все важные и секретные данные, такие как токен от ботов и пароль к Qdrant хранить в переменных окружения. В Amvera, где мы развернем наш сервис, это вкладка «Переменные».
Для данного проекта у нас используются следующие переменные:
API_ID = 123456 API_HASH = your_api_hash SESSION_PATH = /data/tg.session # Сохраняем QDRANT_URL = http://localhost:6333 QDRANT_COLLECTION = chat_messages LLM_BASE_URL = https://kong-proxy.yc.amvera.ru/api/v1/models/llama LLM_API_KEY = ключ LLM_MODEL = llama70b PYTHONUNBUFFERED = 1
Следить за актуальными методами можно в свагере
2.1. Как получить API_ID и API_HASH для юзербота
Чтобы юзербот мог подключаться к вашему аккаунту Telegram, нужны специальные ключи:
-
API_ID
-
API_HASH
Их можно получить на официальном сайте Telegram для разработчиков:
-
Зайдите на my.telegram.org под своим аккаунтом Telegram.
-
Выберите пункт API development tools.
-
Введите название приложения (можно любое, например
chat-search), короткое имя и заполните форму. -
После сохранения появятся ваши
API_IDиAPI_HASH.
3. Юзербот на Telethon
import os, asyncio from dotenv import load_dotenv from telethon import TelegramClient, events from qdrant_client import QdrantClient from qdrant_client.models import VectorParams, Distance, PointStruct from sentence_transformers import SentenceTransformer import httpx load_dotenv() API_ID = int(os.getenv("API_ID")) API_HASH = os.getenv("API_HASH") SESSION_PATH = os.getenv("SESSION_PATH", "session") QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333") COLLECTION = os.getenv("QDRANT_COLLECTION", "chat_messages") LLM_BASE_URL = os.getenv("LLM_BASE_URL") LLM_API_KEY = os.getenv("LLM_API_KEY") LLM_MODEL = os.getenv("LLM_MODEL") client = TelegramClient(SESSION_PATH, API_ID, API_HASH) qdrant = QdrantClient(url=QDRANT_URL) embedder = SentenceTransformer("intfloat/e5-small") # создаём коллекцию (один раз) try: qdrant.get_collection(COLLECTION) except: qdrant.create_collection( collection_name=COLLECTION, vectors_config=VectorParams(size=384, distance=Distance.COSINE) ) async def embed_text(text: str): return embedder.encode(text).tolist() @client.on(events.NewMessage) async def handler(event): # Рекомендуем добавить условие для проверки чата. В данной реализации сохраняются все сообщения text = event.message.message if not text: return emb = await embed_text(text) point = PointStruct( id=event.message.id, vector=emb, payload={"text": text, "chat_id": event.chat_id} ) qdrant.upsert(COLLECTION, points=[point]) print(f"Сохранили сообщение: {text[:50]}...") async def main(): await client.start() print("Юзербот запущен") await client.run_until_disconnected() if __name__ == "__main__": asyncio.run(main())
4. Поиск по смыслу + LLM ответ
В отдельном файле search.py:
import os import asyncio import httpx from dotenv import load_dotenv from qdrant_client import QdrantClient from sentence_transformers import SentenceTransformer load_dotenv() QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333") QDRANT_COLLECTION = os.getenv("QDRANT_COLLECTION", "chat_messages") LLM_BASE_URL = os.getenv("LLM_BASE_URL") # например: https://kong-proxy.yc.amvera.ru/api/v1/models/llama LLM_API_KEY = os.getenv("LLM_API_KEY") LLM_MODEL = os.getenv("LLM_MODEL", "llama8b") qdrant = QdrantClient(url=QDRANT_URL) embedder = SentenceTransformer("intfloat/e5-small") def embed_text(text: str): return embedder.encode([text], normalize_embeddings=True).tolist()[0] async def llm_answer(question, context): headers = { "X-Auth-Token": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json" } payload = { "model": LLM_MODEL, "messages": [ {"role": "system", "text": "Ты помогаешь находить важное в чате. Отвечай коротко, по делу. На русском языке"}, {"role": "user", "text": f"Вопрос: {question}\nКонтекст:\n{context}"} ] } async with httpx.AsyncClient(timeout=120.0) as http: r = await http.post(LLM_BASE_URL, headers=headers, json=payload) r.raise_for_status() data = r.json() return data["choices"][0]["message"]["text"] async def search(question): vec = embed_text(question) results = qdrant.search(collection_name=QDRANT_COLLECTION, query_vector=vec, limit=5) context = "\n".join([p.payload["text"] for p in results]) answer = await llm_answer(question, context) print("Вопрос:", question) print("Ответ:", answer) if __name__ == "__main__": q = input("Введите вопрос: ") asyncio.run(search(q))
Пример входных выходных данных.
Вопрос: "Какие цвета обсуждали вчера?" Контекст из чата: 1) [12:30] user1: Мой любимый цвет — черный 2) [12:35] user2: Думаю перекрасить сайт в синий, будет посвежее 3) [12:40] user3: Я за зелёный, он спокойнее смотрится 4) [12:50] user4: Красный лучше привлекает внимание ... Сформулируй ответ кратко и по делу.
5. Как это работает вместе
-
Запускаем
bot.py— юзербот слушает чат и сохраняет все новые сообщения в Qdrant. -
Запускаем
pythonsearch.py— вводим вопрос, получаем ответ от LLM. -
Дополнительно потребуется развернуть базу данных (Qdrant в нашем случае) и подключить LLM по API.
Пример:
Вопрос: Какие цвета обсуждали вчера? Ответ: Вчера обсуждали несколько цветов: чёрный, синий, зелёный и красный.
Деплой парсера Telegram
Итак, когда у нас уже будет готова база юзербота и всего необходимого функционала, нам нужно будет где-то развернуть все сервисы.
Разберем всё по очереди.
Регистрация
Первое, что потребуется — завести аккаунт в Amvera.
После быстрой регистрации (почта) вы сразу получаете бонусные 111 рублей на баланс. Этого достаточно, чтобы:
-
протестировать юзербота;
-
погонять Qdrant;
-
приобрести тестовый пакет токенов для LLM.
Создание проекта с Qdrant
Прежде чем запускать юзербота, стоит подготовить хранилище — базу Qdrant. В Amvera это можно сделать буквально в несколько кликов.
-
Откройте панель проектов.
-
Нажмите «Создать проект».
-
В поле Тип сервиса выберите «Преднастроенное приложение из маркетплейса».
-
В разделе Задайте параметры сервиса откройте категорию Базы данных.
-
В списке выберите Qdrant.
-
Жмем «Далее», в новом окне выбираем название проекта и тариф.
-
Снова «Далее», задаем версию Qdrant. Рекомендуется оставить ту, что будет указана по умолчанию. Завершаем создание
После этого сервис автоматически развернется, и у вас будет готовая векторная база, к которой можно подключать бота.
Как адрес для подключения используем внутреннее доменное имя, доступное во вкладке «Инфо» проекта. Сам же проект будет доступен в блоке «Преднастроенные сервисы».
Получение токена Amvera LLM Inference API
Осталась последняя зависимость нашего проекта — LLM. Мы будем использовать модель llama70b LLaMA.
Список доступных моделей доступен в документации;
Доступные методы описаны в Swagger
Для подключения прежде всего необходимо приобрести пакет токенов. Для этого переходим в блок «LLM (Preview)», выбираем нужную модель и жмём соответствующую кнопку.
Выберем любой тариф, на первый раз можно использовать «20000 токенов бесплатно на месяц (однократно)».
После приобретения пакета, открываем страницу модели и во вкладке «Инфо» перевыпускаем и копируем токен.
Деплой юзербота
Теперь, когда все зависимости доступны, мы можем развернуть наше приложение как отдельный проект. Для этого также открываем панель проектов и жмём «Создать проект».
-
В поле Тип сервиса выберите «Приложение». Жмём «Далее».
-
Задаем название проекта и выбираем тариф. Рекомендуется использовать «Начальный» или «Начальный плюс».
-
На следующем этапе будет доступна загрузка данных. Вы можете как использовать Git, так и загрузку через интерфейс. На данный момент пропустим загрузку.
-
На этапе создания «Переменные» рекомендуется прописать все переменные окружения, которые мы задавали ранее.
-
Следующий этап пропускаем — конфигурацию создадим после загрузки кода. Завершаем создание проекта и открываем его.
Если вы планируете вдальнейшем переодически обновлять проект, мы рекомендуем использовать Git как способ загрузки данных. Если же обновлений не планируется — можно загрузить через интерфейс.
Загружаем данные проекта (вместе с requirements.txt) любым удобным способом, убеждаемся в том, что переменные окружения созданы и переходим во вкладку «Конфигурация».
Во вкладке конфигурации мы можем сгенерировать amvera.yml — файл с инструкциями для сборки проекта. Подробнее можно прочитать в документации.
Для Python-проекта выбираем окружение Python и инструмент Pip. В version задаем версию Python, указываем в scriptName название запускаемого .py файла.
В моем случае конфигурация выглядит так:
Сохраняем конфигурацию и жмём кнопку сборки.
После выполненных действий ожидаем сборку и если все пройдет без ошибок — проект запустится и будет работать.
Нюанс запуска
Важно, что доступа к консоли проекта нет. Поэтому возможности указать номер телефона и код от Telegram для запуска сессии нет.
Однако, это все достаточно легко обойти, загрузив файл сессии в репозиторий проекта по пути, который мы указывали ранее в переменной SESSION_PATH переменных окружения.
Тут /data — пусть к постоянному хранилищу Amvera. В нашем случае его использовать не обязательно, т.к. файл сессии неизменяемый. Но все изменяемые в процессе работы приложения файлы необходимо сохранять в постоянное хранилище Data, иначе после каждой пересборки/перезапуска файлы обнулятся. Это относится к любым файлам, будь то .txt или файл базы SQLite.
Подробнее здесь: https://docs.amvera.ru/applications/storage.html#data
Ни в коем случае не публикуйте файл сессии в GitHub или любые другие Git-сервисы, если будете использовать их для деплоя!
Это только пример парсинга каналов, групп и чатов Telegram через бота
Мы показали самый простой pipeline, который можно собрать на Python, Qdrant и LLaMA. Такой код работает, но это больше учебный каркас, чем готовое приложение на прод.
Почему так:
-
у каждого проекта разный масштаб (от маленькой группы до корпоративного чата на сотни тысяч сообщений);
-
разные требования к приватности (где-то можно хранить историю целиком, где-то нужно анонимизировать авторов или чистить персональные данные);
-
разные модели эмбеддингов и LLM дают разные результаты (одни лучше подходят для коротких сообщений, другие для длинных диалогов).
Поэтому не следует воспринимать пример как последнее слово техники. Гораздо полезнее взять этот шаблон и попробовать самим.
Релевантные статьи
ссылка на оригинал статьи https://habr.com/ru/articles/942490/
Добавить комментарий