AI Telegram bot
Что делает: пользователь пишет Telegram-боту → OPORA gets update → LLM отвечает на основе вашей knowledge-базы (через RAG) → бот отправляет ответ. Помнит контекст разговора per-чат.
Типовой use-case: FAQ-бот магазина, бот технической поддержки, «спроси нашу документацию» ассистент.
Prerequisites
Section titled “Prerequisites”- Telegram-бот + webhook setup (getting-started/telegram.md)
- OPORA knowledge-база — это data-table с текстовыми чанками,
которые
ai.embedнода предварительно проиндексировала - LLM API key настроен
Secrets
Section titled “Secrets”telegram-bot-tokentelegram-webhook-secretПодготовка knowledge-базы (один раз)
Section titled “Подготовка knowledge-базы (один раз)”1. Создайте data-table kb-chunks
Section titled “1. Создайте data-table kb-chunks”Data Tables → + New → name: kb-chunks → schema:
| Column | Type | Description |
|---|---|---|
| chunk_text | text | Raw текст чанка |
| source | text | Откуда (URL / имя файла) |
| embedding | vector(1536) | Вектор (заполняется при загрузке) |
2. Загрузите документы
Section titled “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
Section titled “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
Section titled “Node-by-node”1. telegram.webhook.trigger
Section titled “1. telegram.webhook.trigger”secretTokenRef: telegram-webhook-secretfilter: # Игнорируем все updates кроме text-messages в приватном чате. # edited_message / callback_query / group-chats пусть идут мимо. expression: | trigger.message && trigger.message.text && !trigger.edited_message2. ai.rag.answer
Section titled “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-minimaxTokens: 500ai.rag.answer объединяет три операции: embedding запроса (ai. embed внутри), vector-search по kb-chunks (ai.vector.search) +
LLM-call с context’ом (ai.generate). Это shortcut-нода — если
вам нужен custom-поведение, разбейте на три отдельные.
3. telegram.message.send
Section titled “3. telegram.message.send”chat_id: "{{trigger.message.chat.id}}"text: "{{rag.answer.answer}}"reply_to_message_id: "{{trigger.message.message_id}}"parse_mode: MarkdownRuntime behavior
Section titled “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).
ai.embedper-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
Section titled “Variations”Двухуровневый fallback: сначала KB, потом general chat
Section titled “Двухуровневый 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 к менеджеру на «не знаю»
Section titled “Escalate к менеджеру на «не знаю»”Если LLM отвечает «переключаю на менеджера» — отправьте параллельно уведомление в чат-команды:
ai.rag.answer ↓control.switch on rag.answer.answer.includes('переключаю на менеджера') yes → telegram.message.send (chat_id = support_team, ...) no → terminalInline-кнопки «Подробнее» на sources
Section titled “Inline-кнопки «Подробнее» на sources”telegram.message.send reply_markup: inline_keyboard: - - text: "📄 Источник 1" url: "{{rag.answer.sources.0.url}}"Голосовые сообщения
Section titled “Голосовые сообщения”Добавьте 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
Section titled “Troubleshooting”Бот отвечает «ничего не нашёл» на очевидные вопросы
Section titled “Бот отвечает «ничего не нашёл» на очевидные вопросы”KB пустая или embedding-column пустой. Data Tables → kb-chunks → Rows проверьте что embedding’и заполнены (не null). Если пусто —
ре-запустите bulk-import workflow.
Memory не помнит контекст
Section titled “Memory не помнит контекст”ai.rag.answer не получает memory.key. Проверьте что template
resolve’ится правильно (в /runs/<id>/traces видно resolved-value
в step-input’е).
LLM выдумывает ответы не из KB
Section titled “LLM выдумывает ответы не из KB”- Прописанный system prompt не достаточно жёсткий. Добавьте явное «Если в контексте нет ответа — не отвечай, не выдумывай».
- Model — gpt-4o-mini иногда халатничает. Попробуйте gpt-4o для critical-вопросов.
Дубли ответов
Section titled “Дубли ответов”Webhook retry от Telegram → OPORA дедуп’ит по update_id, должно
работать из коробки. Если всё равно дубли — проверьте что trigger-
нода настроена с idempotency (default включена).