Files
DBBot-telegram/DBBot Unified n8n Qdrant.json
bilal b2874ff580 Reduce ingestion buffering and improve author metadata fallback.
Flush context blocks faster to avoid delayed indexing and use sender_id-based labels when Telegram sender objects are unavailable in historical messages.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 12:46:24 +03:00

425 lines
13 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"name": "DBBot Unified (Ingest + RAG)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "tg-inbox",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-640,
-120
],
"id": "31d39bff-4f9a-4645-94b2-b38d4ebf0bf0",
"name": "Webhook TG Inbox",
"webhookId": "dbbot-tg-inbox-webhook"
},
{
"parameters": {
"jsCode": "const body = $input.item.json.body || {};\nconst meta = body.metadata || {};\nconst rawText = (body.text || '').trim();\n\nif (!rawText) {\n return { json: { skip: true, reason: 'empty_text' } };\n}\n\nconst authorLabel = meta.author_label || meta.sender_name || 'Unknown';\nconst authorId = meta.sender_id || meta.author?.id || '0';\nconst repliedTo = meta.replied_to_author_label ? ` (в ответ ${meta.replied_to_author_label})` : '';\n\nreturn {\n json: {\n skip: false,\n text: `Автор ${authorLabel} (ID: ${authorId})${repliedTo}: ${rawText}`,\n metadata: {\n ...meta,\n original_text: rawText,\n source: meta.source || 'telegram_group',\n processed_at: new Date().toISOString()\n }\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-416,
-120
],
"id": "a9347d4b-fad0-4fb8-a23c-bcad6d2f3186",
"name": "Normalize Telegram Payload"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.skip }}",
"value2": true
}
]
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-192,
-120
],
"id": "de52e6d1-98be-4c24-bc03-d90d7fa80b93",
"name": "Skip Empty Text"
},
{
"parameters": {
"mode": "insert",
"qdrantCollection": {
"__rl": true,
"value": "telegram_kb",
"mode": "list",
"cachedResultName": "telegram_kb"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1.3,
"position": [
288,
-120
],
"id": "2444f3f3-d57d-45fa-b80e-e40f73f0c70f",
"name": "Qdrant Insert",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"jsCode": "const WINDOW_MS = 5 * 60 * 1000;\nconst MAX_MESSAGES = 3;\nconst MIN_MESSAGES_TO_FLUSH = 1;\n\nfunction aggregateBuffer(buffer) {\n const lines = buffer.messages.map((m) => {\n const repliedTo = m.replyTo ? ` -> ${m.replyTo}` : '';\n return `[${m.date}] ${m.author}${repliedTo}: ${m.text}`;\n });\n\n return {\n skip: false,\n text: [\n `Контекст диалога (${buffer.messages.length} сообщений, участники: ${buffer.participants.join(', ')})`,\n ...lines,\n ].join('\\n'),\n metadata: {\n ...buffer.lastMeta,\n context_id: buffer.contextId,\n context_message_count: buffer.messages.length,\n context_start_date: new Date(buffer.startTs).toISOString(),\n context_end_date: new Date(buffer.lastTs).toISOString(),\n context_participants: buffer.participants,\n context_mode: 'thread_time_window_5m_fast_flush'\n }\n };\n}\n\nconst staticData = $getWorkflowStaticData('global');\nstaticData.contextBuffers = staticData.contextBuffers || {};\n\nconst meta = $json.metadata || {};\nconst rawText = (meta.original_text || '').trim();\nif (!rawText) {\n return { json: { skip: true, reason: 'empty_original_text' } };\n}\n\nconst nowTs = meta.date ? new Date(meta.date).getTime() : Date.now();\nconst safeTs = Number.isNaN(nowTs) ? Date.now() : nowTs;\nconst chatId = String(meta.chat_id || meta.group_id || 'unknown_chat');\nconst threadId = String(meta.thread_id || meta.reply_to_message_id || meta.message_id || 'single');\nconst contextId = `${chatId}:${threadId}`;\n\nlet buffer = staticData.contextBuffers[contextId];\n\nif (buffer && safeTs - buffer.lastTs > WINDOW_MS) {\n const staleBuffer = buffer;\n buffer = null;\n\n const author = meta.author_label || meta.sender_name || 'Unknown';\n staticData.contextBuffers[contextId] = {\n contextId,\n startTs: safeTs,\n lastTs: safeTs,\n participants: [author],\n lastMeta: meta,\n messages: [\n {\n date: meta.date || new Date(safeTs).toISOString(),\n author,\n replyTo: meta.replied_to_author_label || '',\n text: rawText\n }\n ]\n };\n\n if (staleBuffer.messages.length >= MIN_MESSAGES_TO_FLUSH) {\n return { json: aggregateBuffer(staleBuffer) };\n }\n\n return { json: { skip: true, reason: 'buffer_reset_on_timeout', context_id: contextId } };\n}\n\nif (!buffer) {\n buffer = {\n contextId,\n startTs: safeTs,\n lastTs: safeTs,\n participants: [],\n lastMeta: meta,\n messages: []\n };\n}\n\nconst author = meta.author_label || meta.sender_name || 'Unknown';\nif (!buffer.participants.includes(author)) {\n buffer.participants.push(author);\n}\n\nbuffer.messages.push({\n date: meta.date || new Date(safeTs).toISOString(),\n author,\n replyTo: meta.replied_to_author_label || '',\n text: rawText\n});\nbuffer.lastMeta = meta;\nbuffer.lastTs = safeTs;\n\nconst shouldFlush =\n buffer.messages.length >= MAX_MESSAGES ||\n buffer.lastTs - buffer.startTs >= WINDOW_MS;\n\nif (!shouldFlush) {\n staticData.contextBuffers[contextId] = buffer;\n return {\n json: {\n skip: true,\n reason: 'buffering',\n context_id: contextId,\n buffered_messages: buffer.messages.length\n }\n };\n}\n\ndelete staticData.contextBuffers[contextId];\nreturn { json: aggregateBuffer(buffer) };"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
32,
-120
],
"id": "b3866944-b7ca-4505-b89e-a5f3d331f43d",
"name": "Build Context Block"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.skip }}",
"value2": true
}
]
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
160,
-120
],
"id": "a343f1ca-2423-43e7-9b8d-ce4f48f1e5ff",
"name": "Skip Buffered Context"
},
{
"parameters": {
"jsonMode": "expressionData",
"jsonData": "={{ $json.text }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
288,
56
],
"id": "3de4bfe4-bd8a-4f36-a9ec-8955f2f26f83",
"name": "Document Loader"
},
{
"parameters": {
"model": "qwen3-embedding:8b"
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"typeVersion": 1,
"position": [
96,
56
],
"id": "5f48d9eb-ec81-4a00-b2fa-ec6a1fc2f009",
"name": "Embeddings For Insert",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.2,
"position": [
-640,
288
],
"id": "163fbd89-644c-4858-a2ce-8c572a2d3593",
"name": "Telegram Trigger",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"promptType": "define",
"text": "=Ответь на вопрос пользователя, используя данные из Qdrant Vector Store: {{ $json.message.text }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
-384,
288
],
"id": "e67a8860-3c52-407e-8fd7-1f481e3c5019",
"name": "AI Agent"
},
{
"parameters": {
"model": "qwen3:30b-a3b",
"options": {
"temperature": 0
}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"typeVersion": 1,
"position": [
-352,
496
],
"id": "3c7db13e-1be9-4f2f-ab26-a613db89ea71",
"name": "Ollama Chat Model",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "Всегда используй этот инструмент для ответа: в нем релевантные документы из базы знаний.",
"qdrantCollection": {
"__rl": true,
"value": "telegram_kb",
"mode": "list",
"cachedResultName": "telegram_kb"
},
"includeDocumentMetadata": true,
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1.3,
"position": [
-128,
528
],
"id": "2f204e2e-58e7-4514-9f3c-3e23949f8520",
"name": "Qdrant Retrieve Tool",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"model": "qwen3-embedding:8b"
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"typeVersion": 1,
"position": [
-128,
704
],
"id": "e5d5f376-bb95-4a16-ab11-e5e3e7d74524",
"name": "Embeddings For Retrieve",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
},
{
"parameters": {
"jsCode": "let rawText = $input.item.json.text || $input.item.json.response || $input.item.json.output || '';\nlet cleanText = rawText.replace(/<think>[\\s\\S]*?<\\/think>/g, '').trim();\n\nif (!cleanText) {\n cleanText = 'Не смог сформировать ответ. Попробуйте переформулировать вопрос.';\n}\n\nreturn {\n json: {\n clean_response: cleanText\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-128,
288
],
"id": "81f5d5e9-3f13-4d43-b8e2-7112e2ca63e4",
"name": "Clean Agent Output"
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
"text": "={{ $json.clean_response }}",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
96,
288
],
"id": "e8476b38-8344-4aaf-828e-5da8344b79d2",
"name": "Send Telegram Reply",
"retryOnFail": true,
"maxTries": 5,
"waitBetweenTries": 5000
}
],
"pinData": {},
"connections": {
"Webhook TG Inbox": {
"main": [
[
{
"node": "Normalize Telegram Payload",
"type": "main",
"index": 0
}
]
]
},
"Normalize Telegram Payload": {
"main": [
[
{
"node": "Skip Empty Text",
"type": "main",
"index": 0
}
]
]
},
"Skip Empty Text": {
"main": [
[],
[
{
"node": "Build Context Block",
"type": "main",
"index": 0
}
]
]
},
"Build Context Block": {
"main": [
[
{
"node": "Skip Buffered Context",
"type": "main",
"index": 0
}
]
]
},
"Skip Buffered Context": {
"main": [
[],
[
{
"node": "Qdrant Insert",
"type": "main",
"index": 0
}
]
]
},
"Embeddings For Insert": {
"ai_embedding": [
[
{
"node": "Qdrant Insert",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Document Loader": {
"ai_document": [
[
{
"node": "Qdrant Insert",
"type": "ai_document",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Qdrant Retrieve Tool": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Embeddings For Retrieve": {
"ai_embedding": [
[
{
"node": "Qdrant Retrieve Tool",
"type": "ai_embedding",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Clean Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Clean Agent Output": {
"main": [
[
{
"node": "Send Telegram Reply",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"availableInMCP": false
},
"tags": []
}