Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Telegram Agent

FreeNot checked

AI assistant for appointments in Telegram (cousework)

GitHubEmbed

About

AI assistant for appointments in Telegram (cousework)

README

Телеграм-бот, который принимает записи к специалистам на естественном языке. Пользователь пишет «Хочу к стоматологу в Казани на завтра утром», а LLM-агент сам находит врача, проверяет свободные слоты в расписании и создаёт запись — без кнопок, меню и пошаговых форм.

Ключевая идея проекта: бизнес-логика живёт в обычном REST API, а языковая модель получает к ней доступ через MCP (Model Context Protocol) — как к набору инструментов. Модель не знает про базу данных и не пишет SQL; она вызывает те же операции, которые доступны любому другому клиенту API.

Стек: Python 3.11 · aiogram 3 · LangChain + Ollama (qwen3:8b, локальная LLM) · MCP (mcp, mcp-use) · Django 4.2 + Django REST Framework · SQLite · Docker Compose


Что бот умеет

  • Найти специалиста по специализации, городу или категории услуг
  • Показать свободные слоты врача на конкретную дату (сетка по 30 минут, с учётом графика работы и уже занятых часов)
  • Записать на приём — время окончания рассчитывается автоматически из длительности услуги
  • Подтвердить, отменить или закрыть запись (pending → confirmed → completed / cancelled)
  • Ответить на вопросы об услугах, ценах, расписании и категориях
  • Держится в рамках роли: системный промпт ограничивает агента темой медцентра и записей

Каталог, расписания и записи параллельно доступны через админку Django и Swagger UI.


Как это устроено

flowchart LR
    subgraph bot["telegram_bot/"]
        BOT["bot.py<br/>aiogram, polling"]
        AGENT["MCPAgent<br/>до 30 шагов"]
        MCP["mcp_server.py<br/>6 tools + 12 resources"]
        BOT <--> AGENT
        AGENT <-->|"MCP / stdio"| MCP
    end

    subgraph api["api/"]
        DRF["Django REST API<br/>6 ViewSet'ов, бизнес-правила"]
        DB[("SQLite")]
        DRF <--> DB
    end

    TG(["Telegram"]) <--> BOT
    AGENT <-->|"промпт / ответ"| OLLAMA["Ollama<br/>qwen3:8b"]
    MCP <-->|"REST / JSON"| DRF

Полный цикл одного запроса:

sequenceDiagram
    actor U as Пользователь
    participant B as bot.py
    participant A as MCPAgent
    participant M as MCP-сервер
    participant D as Django API

    U->>B: «Запиши к стоматологу на завтра в 11:00»
    B->>A: текст + Telegram ID отправителя
    A->>M: search_specialists(specialization, city)
    M->>D: GET /api/specialists/?search=...
    D-->>M: список специалистов
    M-->>A: результат
    A->>M: get_available_slots(specialist_id, date)
    M->>D: GET /api/specialists/{id}/available_slots/
    D-->>M: свободные слоты
    M-->>A: результат
    A->>M: create_appointment(...)
    M->>D: GET /api/services/{id}/ — длительность, расчёт end_time
    M->>D: POST /api/appointments/
    Note over D: validate(): пересечения<br/>и график работы
    D-->>M: созданная запись
    M-->>A: результат
    A-->>B: текст ответа
    B->>U: «Записал: завтра 11:00–11:30»

1. Бот (telegram_bot/bot.py) — тонкий слой. Принимает сообщение, добавляет к нему Telegram ID отправителя (чтобы агент мог связать собеседника с клиентом в базе), отдаёт MCPAgent. Ответ модели чистится от блока рассуждений <think>...</think>, которые генерирует qwen3, и уходит пользователю.

2. АгентMCPAgent из mcp-use поверх ChatOllama. До 30 шагов цикла «рассуждение → вызов инструмента → анализ результата». Модель работает локально в Ollama: данные пациентов не уходят во внешние API.

3. MCP-сервер (telegram_bot/mcp_server.py) — мост между моделью и API, запускается через stdio.

  • 6 tools (действия): search_specialists, get_available_slots, create_appointment, confirm_appointment, cancel_appointment, complete_appointment
  • 12 resources (чтение): специалисты, их услуги и расписания, услуги, категории, записи, клиенты
  • Docstring каждого инструмента — это и есть его спецификация для модели: по ним LLM понимает, что принимает аргумент date в формате YYYY-MM-DD, а start_timeHH:MM.
  • Самая содержательная логика здесь — create_appointment: инструмент сам подтягивает длительность услуги и вычисляет end_time, чтобы модели не приходилось считать время.

4. Django REST API (api/) — источник правды и место, где живут инварианты.

  • Модели: ServiceCategory, Specialist, SpecialistSchedule, Service, Client, Appointment
  • GET /api/specialists/{id}/available_slots/?date=... — генерация слотов: берётся график на нужный день недели, режется на интервалы по 30 минут, из них вычитаются пересечения с активными записями
  • AppointmentSerializer.validate() — двойная защита: запись не создастся, если время пересекается с существующей (pending/confirmed) или выходит за пределы графика работы специалиста. Даже если модель ошибётся, API её остановит.
  • Swagger/ReDoc через drf-yasg

Почему MCP, а не function calling напрямую. Инструменты описаны один раз на стороне сервера и не привязаны ни к боту, ни к конкретной модели. Тот же mcp_server.py можно подключить к другому клиенту (Claude Desktop, IDE, веб-чат), а модель в боте заменить одной строкой — контракт не меняется.


Схема данных

Модель Назначение
ServiceCategory Категории услуг (Красота, Здоровье, Фитнес)
Specialist Врач/мастер: специализация, город, фото, привязка к User
SpecialistSchedule График по дням недели (unique_together на специалиста и день)
Service Услуга: цена, длительность в минутах, специалист, категория
Client Клиент: телефон, email, город, telegram_id
Appointment Запись: клиент, услуга, специалист, дата, интервал, статус

Запуск

Требования

  • Docker и Docker Compose
  • Ollama с загруженной моделью:
    ollama pull qwen3:8b
    
  • Бот в Telegram, созданный через @BotFather

1. Переменные окружения

Создайте .env в корне проекта:

BOT_TOKEN=<токен от BotFather>
OLLAMA_URL=http://host.docker.internal:11434

host.docker.internal позволяет контейнеру достучаться до Ollama, запущенной на хосте (macOS/Windows). На Linux используйте http://172.17.0.1:11434 или запустите Ollama в той же Docker-сети.

2. Поднять сервисы

docker compose up --build

3. Применить миграции и наполнить базу

docker compose exec api python manage.py migrate
docker compose exec api python manage.py create_test_data

Команда create_test_data создаёт категории, специалистов с расписаниями, услуги и тестовых клиентов, а также администратора admin / admin.

4. Проверить

Локальный запуск без Docker

python -m venv venv && source venv/bin/activate
pip install -r requirements.txt

# терминал 1 — API
cd api && python manage.py migrate && python manage.py create_test_data
python manage.py runserver 8000

# терминал 2 — бот (MCP-сервер поднимается им автоматически через stdio)
cd telegram_bot && python bot.py

Примеры диалога

👤 Есть стоматологи в Казани?
🤖 Да, принимает Иванов Пётр Сергеевич — стоматолог, Казань.
   Услуги: консультация (30 мин, 1500 ₽), лечение кариеса (60 мин, 4500 ₽).

👤 Какие есть окна на 15 июня?
🤖 Свободно: 09:00–09:30, 09:30–10:00, 11:00–11:30, 14:00–14:30.

👤 Запиши на консультацию в 11:00
🤖 Записал: 15 июня, 11:00–11:30, консультация у Иванова П. С.
   Статус — ожидает подтверждения.

Технические решения и компромиссы

Что сделано осознанно:

  • Локальная LLM. Медицинские данные не покидают инфраструктуру — для домена это не опция, а требование.
  • Валидация на стороне API, а не промпта. LLM недетерминирована; пересечения слотов и выход за график ловятся в сериализаторе, где ошибиться нельзя.
  • Расчёт end_time внутри MCP-инструмента. Арифметика со временем — плохая задача для модели, поэтому она отдана коду.
  • Разделение tools и resources. Действия, меняющие состояние, отделены от чтения — модель не может «случайно» что-то создать, читая справочник.

Что осталось за рамками текущей версии:

  • Аутентификация: permission_classes = [AllowAny] и placeholder-токен в MCP-сервере — рабочая конфигурация для демо, но перед продакшеном нужен реальный Token/JWT-слой.
  • mcp_server.py ходит на http://localhost:8000 — при запуске в Docker Compose адрес нужно поменять на http://api:8000 (или вынести в переменную окружения).
  • SQLite и runserver — dev-конфигурация; для боевого контура нужны PostgreSQL и ASGI-сервер.
  • telegram_id есть в модели Client, но не попал в ClientSerializer — связка «Telegram-пользователь ↔ клиент в базе» пока делается через передачу ID в промпте.
  • Нет истории диалога между сообщениями: каждый запрос обрабатывается независимо.

Структура проекта

.
├── api/                        # Django REST API
│   ├── boba/                   # settings, urls, swagger
│   └── booking/
│       ├── models.py           # 6 моделей предметной области
│       ├── serializers.py      # валидация пересечений и графика
│       ├── views.py            # ViewSet'ы, генерация слотов, экшены статусов
│       └── management/commands/create_test_data.py
├── telegram_bot/
│   ├── bot.py                  # aiogram + MCPAgent + Ollama
│   └── mcp_server.py           # 6 tools + 12 resources поверх REST API
└── docker-compose.yaml

from github.com/om1ji/telegram-agent

Installing Telegram Agent

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/om1ji/telegram-agent

FAQ

Is Telegram Agent MCP free?

Yes, Telegram Agent MCP is free — one-click install via Unyly at no cost.

Does Telegram Agent need an API key?

No, Telegram Agent runs without API keys or environment variables.

Is Telegram Agent hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Telegram Agent in Claude Desktop, Claude Code or Cursor?

Open Telegram Agent on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Telegram Agent with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All communication MCPs