Документация

AI Telegram bot

Что делает: пользователь пишет Telegram-боту → SUMMING gets update → LLM отвечает на основе вашей knowledge-базы (через RAG) → бот отправляет ответ. Помнит контек

Что делает: пользователь пишет Telegram-боту → SUMMING gets update → LLM отвечает на основе вашей knowledge-базы (через RAG) → бот отправляет ответ. Помнит контекст разговора per-чат.

Типовой use-case: FAQ-бот магазина, бот технической поддержки, «спроси нашу документацию» ассистент.

Prerequisites

  • Telegram-бот + webhook setup (getting-started/telegram.md)
  • SUMMING knowledge-база — это data-table с текстовыми чанками, которые ai.embed нода предварительно проиндексировала
  • LLM API key настроен

Secrets

telegram-bot-token
telegram-webhook-secret

Подготовка knowledge-базы (один раз)

1. Создайте data-table kb-chunks

Data Tables+ New → name: kb-chunks → schema:

ColumnTypeDescription
chunk_texttextRaw текст чанка
sourcetextОткуда (URL / имя файла)
embeddingvector(1536)Вектор (заполняется при загрузке)

2. Загрузите документы

Через CSV-импорт или через ad-hoc workflow. Пример workflow'а для bulk-import'а:

cron.trigger (once, manual)
    ↓
external.http
  url: "https://your-cms.example/api/articles"
  (или load'ите из файла через data.fetch)
    ↓
data.array.foreach (on: articles)
  ai.embed
    input: "{{item.title}}\n\n{{item.body}}"
    ↓
  data_table.rows.insert
    table: kb-chunks
    data:
      chunk_text: "{{item.title}}\n\n{{item.body}}"
      source: "{{item.url}}"
      embedding: "{{embed.output}}"

Это одноразовый workflow — запускаете его, когда обновляете knowledge-базу.

Bot workflow

┌──────────────────┐
│ telegram.webhook │
│ .trigger         │
└────────┬─────────┘
         │ {message: {text, chat.id, from.id}}
         ↓
┌──────────────────┐
│ ai.rag.answer    │  semantic search + LLM answer
│                  │  memory_key: "tg:{{chat.id}}"
└────────┬─────────┘
         │ {answer: "...", sources: [...]}
         ↓
┌──────────────────┐
│ telegram.message │  reply_to original message
│ .send            │
└──────────────────┘

Node-by-node

1. telegram.webhook.trigger

secretTokenRef: telegram-webhook-secret
filter:
  # Игнорируем все updates кроме text-messages в приватном чате.
  # edited_message / callback_query / group-chats пусть идут мимо.
  expression: |
    trigger.message && trigger.message.text && !trigger.edited_message

2. ai.rag.answer

query: "{{trigger.message.text}}"
knowledgeBase:
  table: kb-chunks
  embeddingColumn: embedding
  textColumn: chunk_text
  topK: 5
  minSimilarity: 0.7  # чтобы не отвечать на вопросы, где KB пуста
systemPrompt: |
  Ты помощник магазина Acme. Отвечай только на основе приведённого
  контекста. Если контекста недостаточно — скажи «Не нашёл в
  документации, переключаю на менеджера» и ничего не выдумывай.
memory:
  key: "tg:{{trigger.message.chat.id}}"
  ttlSeconds: 3600  # история чата — 1 час
model: gpt-4o-mini
maxTokens: 500

ai.rag.answer объединяет три операции: embedding запроса (ai. embed внутри), vector-search по kb-chunks (ai.vector.search) + LLM-call с context'ом (ai.generate). Это shortcut-нода — если вам нужен custom-поведение, разбейте на три отдельные.

3. telegram.message.send

chat_id: "{{trigger.message.chat.id}}"
text: "{{rag.answer.answer}}"
reply_to_message_id: "{{trigger.message.message_id}}"
parse_mode: Markdown

Runtime behavior

Первое сообщение:

  • /runs — новый run
  • Timeline: webhook.trigger (5ms) → ai.rag.answer (800-1500ms, embedding + search + LLM) → telegram.message.send (200ms)
  • Ответ в Telegram приходит в течение 1-2 секунд

Второе сообщение в том же чате:

  • ai.rag.answer видит memory[tg:<chat_id>] содержащий prev-turn, feed'ит в prompt → ответ контекстный

Memory очищается через 1 час inactivity (TTL).

Cost

  • ai.embed per-query — ~0.001 копейка (1 запрос, ~20 токенов)
  • ai.vector.search — 0 (local Postgres vector search)
  • ai.generate с gpt-4o-mini — ~1-2 копейки per ответ (context + output)
  • Итого: ~2 коп / вопрос. 1000 вопросов / день — 20 руб / день.

Variations

Двухуровневый fallback: сначала KB, потом general chat

ai.rag.answer
  minSimilarity: 0.7
    ↓
control.switch on rag.answer.sources.length
  > 0 → telegram.message.send (text = rag.answer.answer)
  == 0 → ai.chat (general, без RAG)
            ↓
         telegram.message.send (text = chat.output)

Escalate к менеджеру на «не знаю»

Если LLM отвечает «переключаю на менеджера» — отправьте параллельно уведомление в чат-команды:

ai.rag.answer
    ↓
control.switch on rag.answer.answer.includes('переключаю на менеджера')
  yes → telegram.message.send (chat_id = support_team, ...)
  no  → terminal

Inline-кнопки «Подробнее» на sources

telegram.message.send
  reply_markup:
    inline_keyboard:
      - - text: "📄 Источник 1"
          url: "{{rag.answer.sources.0.url}}"

Голосовые сообщения

Добавьте ai.transcribe перед rag.answer:

telegram.webhook.trigger
    ↓
control.switch on trigger.message.voice
  yes → telegram.get_file (file_id = voice.file_id)
            ↓
        ai.transcribe (audio = get_file.output)
            ↓  trigger_text = transcribe.output
  no  → trigger_text = trigger.message.text
    ↓
ai.rag.answer (query = trigger_text)
    ↓
...

ai.transcribe + telegram.get_file — в backlog'е, ETA Q2-2026.

Troubleshooting

Бот отвечает «ничего не нашёл» на очевидные вопросы

KB пустая или embedding-column пустой. Data Tables → kb-chunks → Rows проверьте что embedding'и заполнены (не null). Если пусто — ре-запустите bulk-import workflow.

Memory не помнит контекст

ai.rag.answer не получает memory.key. Проверьте что template resolve'ится правильно (в /runs/<id>/traces видно resolved-value в step-input'е).

LLM выдумывает ответы не из KB

  • Прописанный system prompt не достаточно жёсткий. Добавьте явное «Если в контексте нет ответа — не отвечай, не выдумывай».
  • Model — gpt-4o-mini иногда халатничает. Попробуйте gpt-4o для critical-вопросов.

Дубли ответов

Webhook retry от Telegram → SUMMING дедуп'ит по update_id, должно работать из коробки. Если всё равно дубли — проверьте что trigger- нода настроена с idempotency (default включена).