Files
truenas-stacks/n8n-workflows/02-rag-ingest-and-query.json
2026-05-16 20:11:29 +00:00

394 lines
12 KiB
JSON

{
"updatedAt": "2026-05-06T23:25:33.293Z",
"createdAt": "2026-05-06T23:25:29.005Z",
"id": "gYyOggC4J98keLbj",
"name": "RAG Pipeline — Ingest & Query",
"description": null,
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "rag/ingest",
"responseMode": "lastNode",
"responseData": "allEntries",
"options": {}
},
"id": "31ce8413-d15c-490f-92d1-956ef214e9a3",
"name": "Ingest Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"webhookId": "411ad4e3-eb9f-49e2-97c4-2f30e01dd609"
},
{
"parameters": {
"jsCode": "// Split incoming text into chunks for embedding\nconst input = $input.first().json.body || $input.first().json;\nconst text = input.text || input.content || '';\nconst source = input.source || 'manual';\nconst metadata = input.metadata || {};\n\nconst CHUNK_SIZE = 500;\nconst OVERLAP = 50;\n\nconst chunks = [];\nconst sentences = text.split(/(?<=[.!?])\\s+/);\nlet current = '';\n\nfor (const sentence of sentences) {\n if ((current + ' ' + sentence).length > CHUNK_SIZE && current.length > 0) {\n chunks.push(current.trim());\n // Keep overlap\n const words = current.split(' ');\n current = words.slice(-OVERLAP).join(' ') + ' ' + sentence;\n } else {\n current = current ? current + ' ' + sentence : sentence;\n }\n}\nif (current.trim()) chunks.push(current.trim());\n\nreturn chunks.map((chunk, i) => ({\n json: {\n chunk,\n chunkIndex: i,\n totalChunks: chunks.length,\n source,\n metadata\n }\n}));"
},
"id": "5d5aa0b1-c84b-48f6-9b5e-618cb05e5e61",
"name": "Chunk Text",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
]
},
{
"parameters": {
"method": "POST",
"url": "http://10.5.1.6:11434/v1/embeddings",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"nomic-embed-text\",\n \"input\": \"{{ $json.chunk }}\"\n}",
"options": {
"batching": {
"batch": {
"batchSize": 5,
"batchInterval": 500
}
}
}
},
"id": "89c692c6-b987-409c-b5c1-3533097d06e3",
"name": "Get Embedding",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
448,
0
]
},
{
"parameters": {
"jsCode": "const embeddingResponse = $input.first().json;\nconst chunkData = $('Chunk Text').item;\nconst embedding = embeddingResponse.data?.[0]?.embedding || [];\n\nconst pointId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); });\n\nreturn [{\n json: {\n id: pointId,\n vector: embedding,\n payload: {\n text: chunkData.json.chunk,\n source: chunkData.json.source,\n chunkIndex: chunkData.json.chunkIndex,\n totalChunks: chunkData.json.totalChunks,\n metadata: chunkData.json.metadata,\n ingested_at: new Date().toISOString()\n }\n }\n}];"
},
"id": "fec59066-958f-4b96-8224-6f4e48ad5abb",
"name": "Prepare Qdrant Point",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
0
]
},
{
"parameters": {
"method": "PUT",
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"points\": [\n {\n \"id\": \"{{ $json.id }}\",\n \"vector\": {{ JSON.stringify($json.vector) }},\n \"payload\": {{ JSON.stringify($json.payload) }}\n }\n ]\n}",
"options": {}
},
"id": "9404c8fe-46ae-4d8a-bf35-35ecba4939c0",
"name": "Upsert to Qdrant",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
880,
0
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "rag/query",
"responseMode": "lastNode",
"responseData": "allEntries",
"options": {}
},
"id": "1a5c921c-0351-4a5e-a3e4-0aab71313af1",
"name": "Query Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
304
],
"webhookId": "aad4fe04-74b0-4136-80e9-380a7e4db1c2"
},
{
"parameters": {
"method": "POST",
"url": "http://10.5.1.6:11434/v1/embeddings",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"nomic-embed-text\",\n \"input\": \"{{ $json.body.query || $json.query }}\"\n}",
"options": {}
},
"id": "0a294ba5-a890-4dfa-8ae7-ba25cd0a4534",
"name": "Embed Query",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
224,
304
]
},
{
"parameters": {
"method": "POST",
"url": "http://10.5.1.6:6333/collections/knowledge_base/points/search",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"vector\": {{ JSON.stringify($json.data[0].embedding) }},\n \"limit\": 5,\n \"with_payload\": true\n}",
"options": {}
},
"id": "2cb68891-c57e-4006-b3ac-c290aec28220",
"name": "Search Qdrant",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
448,
304
]
},
{
"parameters": {
"jsCode": "const searchResults = $input.first().json.result || [];\nconst query = $('Query Webhook').first().json.body?.query || $('Query Webhook').first().json.query || '';\n\nconst context = searchResults.map((r, i) => \n `[${i+1}] (score: ${r.score.toFixed(3)}) ${r.payload.text}`\n).join('\\n\\n');\n\nreturn [{\n json: {\n query,\n context,\n sources: searchResults.map(r => ({\n source: r.payload.source,\n score: r.score,\n text_preview: r.payload.text.substring(0, 100) + '...'\n }))\n }\n}];"
},
"id": "d32ee814-81d0-4ea6-8f28-59632d09b1b9",
"name": "Build Context",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
304
]
},
{
"parameters": {
"method": "POST",
"url": "http://10.5.1.6:4000/v1/chat/completions",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Authorization",
"value": "={{'Bearer ' + $env.LITELLM_API_KEY}}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"medium\",\n \"messages\": [\n {\n \"role\": \"system\",\n \"content\": \"You are a helpful knowledge base assistant. Answer the user's question using ONLY the provided context. If the context doesn't contain enough information, say so. Cite which source numbers you used.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Context:\\n{{ $json.context }}\\n\\nQuestion: {{ $json.query }}\"\n }\n ],\n \"max_tokens\": 500,\n \"temperature\": 0.2\n}",
"options": {}
},
"id": "e1360454-8457-40ce-b179-670c182da59e",
"name": "LiteLLM Answer",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
880,
304
]
},
{
"parameters": {
"jsCode": "const response = $input.first().json;\nconst contextData = $('Build Context').first().json;\n\nreturn [{\n json: {\n answer: response.choices?.[0]?.message?.content || 'No answer generated',\n sources: contextData.sources,\n query: contextData.query\n }\n}];"
},
"id": "fe16d3a1-0d11-442f-b941-56fb74804601",
"name": "Format Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1104,
304
]
}
],
"connections": {
"Ingest Webhook": {
"main": [
[
{
"node": "Chunk Text",
"type": "main",
"index": 0
}
]
]
},
"Chunk Text": {
"main": [
[
{
"node": "Get Embedding",
"type": "main",
"index": 0
}
]
]
},
"Get Embedding": {
"main": [
[
{
"node": "Prepare Qdrant Point",
"type": "main",
"index": 0
}
]
]
},
"Prepare Qdrant Point": {
"main": [
[
{
"node": "Upsert to Qdrant",
"type": "main",
"index": 0
}
]
]
},
"Query Webhook": {
"main": [
[
{
"node": "Embed Query",
"type": "main",
"index": 0
}
]
]
},
"Embed Query": {
"main": [
[
{
"node": "Search Qdrant",
"type": "main",
"index": 0
}
]
]
},
"Search Qdrant": {
"main": [
[
{
"node": "Build Context",
"type": "main",
"index": 0
}
]
]
},
"Build Context": {
"main": [
[
{
"node": "LiteLLM Answer",
"type": "main",
"index": 0
}
]
]
},
"LiteLLM Answer": {
"main": [
[
{
"node": "Format Response",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"staticData": null,
"meta": null,
"pinData": {},
"versionId": "832248e5-106e-4fc4-8ebd-a5d0034e2b14",
"activeVersionId": "832248e5-106e-4fc4-8ebd-a5d0034e2b14",
"versionCounter": 14,
"triggerCount": 2,
"tags": [
{
"updatedAt": "2026-05-06T22:38:51.678Z",
"createdAt": "2026-05-06T22:38:51.678Z",
"id": "S9FxzVfHLsHmyzyt",
"name": "ai"
},
{
"updatedAt": "2026-05-06T22:39:33.284Z",
"createdAt": "2026-05-06T22:39:33.284Z",
"id": "7uuEyQrIlcuruZBk",
"name": "rag"
}
],
"shared": [
{
"updatedAt": "2026-05-06T23:25:29.005Z",
"createdAt": "2026-05-06T23:25:29.005Z",
"role": "workflow:owner",
"workflowId": "gYyOggC4J98keLbj",
"projectId": "dxCRnBdX5uJizCGa",
"project": {
"updatedAt": "2026-05-06T01:10:37.484Z",
"createdAt": "2026-05-06T01:08:07.721Z",
"id": "dxCRnBdX5uJizCGa",
"name": "Wilfred Fizzlepoof <admin@paccoco.com>",
"type": "personal",
"icon": null,
"description": null,
"creatorId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d"
}
}
],
"versionMetadata": {
"name": "Version 832248e5",
"description": ""
}
}