Хабр, привет! Я работаю системным аналитиком более 5 лет. В последнее время все чаще стала задумываться как системному аналитику в текущих реалиях применять в работе ИИ. Так началось мое погружение в увлекательный мир LangChain, ИИ, RAG и векторные БД.

Для начального исследования я решила попробовать что-то достаточно простое и базовое. Так в мою голову пришла идея создать ТГ бота, который напоминает о запланированных делах

Первые шаги и ошибки

Первая версия была максимально простой: просто скрипт, который парсил текст грубыми регулярками. Фразу «встреча с X в 19:45» он ещё понимал, а вот «позвонить X через 2 часа» — уже нет.

Тут я вспомнила про LangChain. Честно признаюсь, до этого проекта я только краем уха слышала про этот фреймворк. Он предоставляет отличную возможность для сборки AI-приложений. 

Танцы с бубнами вокруг AI-моделей

Я перепробовала кучу вариантов:

Попытка №1: OpenAI (ChatGPT)
Самый очевидный выбор. Быстро получила ключ, написала код, запустила... и получила ошибку unsupported_country_region_territory. OpenAI не дружит с моим регионом. Можно было бы попробовать настроить VPN/прокси, но слишком сильно заморачиваться для первого проекта не хотелось.

Попытка №2: DeepSeek
Китайский конкурент. Действительно, работает быстро, цены смешные, регион не проверяет. Но после пары тестов на моём аккаунте закончился баланс (Insufficient Balance), а пополнять в тот момент не хотелось.

Попытка №3: GigaChat от Сбера
Российская модель, ко��орая сразу заработала. Никаких блокировок, отличный русский язык, понятная документация. Мой бот наконец-то заработал и начал понимать мои запросы, но бесплатные токены быстро закончились.

Попытка №4 (финальная): Gemma 3 27B
Тут я задумалась: «А что, если не зависеть ни от каких API?» И тут я вспомнила про Ollama и локальные модели.

Выбор пал на Gemma 3 27B от Google. Модель весит прилично (около 17 ГБ), но теперь есть возможность локального запуска. Никаких API-ключей, никаких ограничений, никакого интернета — только ты и твой ноут.

По итогу получился следующий тех стек:

  • Python 3.10 

  • python-telegram-bot==13.15 

  • langchain-ollama

  • Ollama 

Что умеет мой бот сейчас

После нескольких итераций и кучи исправлений родился универсальный помощник, который понимает:

  • встреча с X завтра в 15:00

  • сдать отчет через 2 часа

  • купить хлеб вечером

Он сам определяет тип напоминания, сам вычисляет время (даже если я пишу «послезавтра» или «через 3 часа»), сам запоминает и присылает уведомление ровно за минуту до события.

Пошаговая реализация

Шаг 1: Настройка окружения

Первым делом создаем виртуальное окружение (это спасёт от конфликтов зависимостей):

python -m venv langchain_env

source langchain_env/bin/activate  # для Linux/Mac

# или для Windows:

langchain_env\Scripts\activate

Устанавливаем зависимости:

pip install langchain-ollama python-telegram-bot==13.15 python-dotenv

Шаг 2: Инициализация модели

Подключение локальной Gemma 3 через Ollama:

from langchain_ollama import ChatOllama

llm = ChatOllama(
    model="gemma3:27b",       # название модели
    temperature=0,            # чем ниж��, тем точнее
    num_predict=1024,         # макс. длина ответа
    keep_alive="10m",         # держать в памяти 10 минут
)

Важно: перед запуском убедитесь, что Ollama сервер работает:

ollama serve
ollama pull gemma3:27b

Шаг 3: Промт для извлечения информации

Необходимо настроить промт так, чтобы модель гарантированно возвращала чистый JSON:

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

reminder_prompt = ChatPromptTemplate.from_messages([
    ("system", """Ты - помощник, который извлекает информацию о напоминаниях из текста.
    Текущее время: {current_time}
    
    Проанализируй сообщение и верни ТОЛЬКО JSON с полями:
    - type: тип (meeting/call/task/shopping/health/birthday/other)
    - title: название (обязательно)
    - datetime: когда напомнить (формат ГГГГ-ММ-ДД ЧЧ:ММ)
    
    Примеры:
    "встреча с Х завтра в 15:00" -> 
        {{"type": "meeting", "title": "Встреча с Х", "datetime": "2024-01-16 15:00"}}
    "позвонить Х через 2 часа" -> 
        {{"type": "call", "title": "Позвонить Х", "datetime": "2024-01-15 18:30"}}
    
    Верни ТОЛЬКО JSON, без пояснений."""),
    ("human", "{text}")
])

chain = reminder_prompt | llm | StrOutputParser()

Обратите внимание на двойные фигурные скобки {{ — это экранирование в LangChain, чтобы шаблон правильно обработал примеры JSON.

Шаг 4: Парсинг ответа модели

Модель иногда возвращает JSON внутри markdown-блоков, поэтому нужна очистка:

def parse_reminder_request(text):
    try:
        result = chain.invoke({
            "text": text,
            "current_time": datetime.now().strftime("%H:%M %d.%m.%Y")
        })
        
        # Очищаем от markdown
        result = result.strip()
        if '```json' in result:
            result = result.split('```json')[1].split('```')[0].strip()
        elif '```' in result:
            result = result.split('```')[1].split('```')[0].strip()
        
        data = json.loads(result)
        print(f"🔍 Распознано: {data}")
        return data
    except Exception as e:
        print(f"❌ Ошибка: {e}")
        return None

Шаг 5: Система напоминаний

Самая простая и надёжная реализация — без APScheduler, на обычном threading:

import threading
import time
from datetime import datetime
import telegram

def check_reminders():
    bot = telegram.Bot(token=TELEGRAM_TOKEN)
    while True:
        try:
            now = datetime.now()
            for reminder in reminders:
                if not reminder.get('notified') and now >= reminder['datetime']:
                    bot.send_message(
                        chat_id=CHAT_ID,
                        text=f"🔔 **НАПОМИНАНИЕ!**\n\n{reminder['text']}"
                    )
                    reminder['notified'] = True
                    save_reminders()
            time.sleep(5)  # проверяем каждые 5 секунд
        except Exception as e:
            print(f"❌ Ошибка: {e}")
            time.sleep(5)

# Запускаем в отдельном потоке
reminder_thread = threading.Thread(target=check_reminders, daemon=True)
reminder_thread.start()

Шаг 6: Telegram-обработчики

Базовые команды и обработка сообщений:

from telegram.ext import Updater, CommandHandler, MessageHandler, Filters

def start(update, context):
    update.message.reply_text(
        "👋 Привет! Просто напиши мне:\n"
    )

def handle_message(update, context):
    text = update.message.text
    print(f"📨 Получено: {text}")
    
    reminder, response = add_reminder_from_text(text)
    update.message.reply_text(response)

def main():
    updater = Updater(TELEGRAM_TOKEN, use_context=True)
    dp = updater.dispatcher
    
    dp.add_handler(CommandHandler("start", start))
    dp.add_handler(CommandHandler("list", list_reminders))
    dp.add_handler(CommandHandler("today", today_reminders))
    dp.add_handler(MessageHandler(Filters.text, handle_message))
    
    updater.start_polling()
    updater.idle()

Итоговая структура проекта

C:\Users\mfrus\meeting_reminder_bot\
├── langchain_env\           # виртуальное окружение
├── .env                     # TELEGRAM_TOKEN и CHAT_ID
├── simple_universal_bot.py  # главный бот
├── gemma3_bot.py            # резервная копия
├── reminders.json           # база напоминаний
└── requirements.txt         # зависимости

Файл .env:

TELEGRAM_BOT_TOKEN=ваш_токен
YOUR_CHAT_ID=ваш_id

Файл requirements.txt:

langchain-ollama>=0.1.0
python-telegram-bot==13.15
python-dotenv>=1.0.0

 Запуск и проверка

# 1. Активировать окружение
langchain_env\Scripts\activate

# 2. Убедиться, что Ollama запущен
ollama list  # должна быть gemma3:27b

# 3. Запустить бота
python simple_universal_bot.py

Что в итоге

Сейчас у меня есть:

✅ Полностью локальный Telegram-бот
✅ Работает на моём компьютере 24/7
✅ Понимает естественный язык благодаря Gemma 3
✅ Не требует API-ключей
✅ Напоминает о чём угодно: от встреч до покупки хлеба

Весь код доступен на GitHub. Буду рада вопросам и предложениям в комментариях!