AutomationFlowsAI & RAG › Llama Chatbot with Ollama, Groq, Slack & Sheets

Llama Chatbot with Ollama, Groq, Slack & Sheets

Original n8n title: Build a Private Llama Chatbot with Ollama, Groq, Slack and Google Sheets

ByOneclick AI Squad @oneclick-ai on n8n.io

This workflow builds a fully private, self-hosted AI chatbot using Meta Llama models. Unlike cloud-based AI APIs, every conversation stays on your infrastructure — no data leaves your environment. The chatbot remembers conversation history per session, routes different query…

Webhook trigger★★★★☆ complexity13 nodesHTTP RequestGoogle Sheets
AI & RAG Trigger: Webhook Nodes: 13 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #13811 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "tuxEpNi6e4MBIlk9",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "n8n Llama AI \u2014 Private Chatbot Automation",
  "tags": [],
  "nodes": [
    {
      "id": "65ccc159-b599-4990-8de4-bcec058d7f0a",
      "name": "Main Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        0
      ],
      "parameters": {
        "width": 1520,
        "height": 1332,
        "content": "# n8n Llama AI Workflow\n## Build a Private AI Chatbot Automation\n\n---\n\n### Description\nThis workflow builds a fully private, self-hosted AI chatbot using Meta Llama models. Unlike cloud-based AI APIs, every conversation stays on your infrastructure \u2014 no data leaves your environment. The chatbot remembers conversation history per session, routes different query types to specialized Llama prompts, logs all interactions, and can escalate unresolved queries to a human agent via Slack.\n\nPowered by Ollama (local) or Groq/Together AI (cloud Llama endpoints) \u2014 configurable in one node.\n\n### How It Works\n\nStage A \u2014 Message Intake\nWebhook receives incoming chat message with session ID and user message text. Set node stores Llama endpoint config and normalizes the payload.\n\nStage B \u2014 Session Memory\nCode node loads conversation history for the session from an in-memory store. Appends the new user message to build the full context window for Llama.\n\nStage C \u2014 Intent Router\nIF node checks the message for keywords to classify intent: support issue, sales inquiry, general question, or escalation request. Routes to the matching Llama system prompt branch.\n\nStage D \u2014 Llama Inference\nHTTP Request calls the Llama API (Ollama local, Groq, or Together AI). Sends full conversation history plus the matched system prompt. Returns the assistant reply.\n\nStage E \u2014 Response Handling\nCode node parses the Llama output, updates the session memory, checks if escalation is needed, and formats the final response.\n\nStage F \u2014 Logging and Delivery\nGoogle Sheets logs every turn. Slack fires only when escalation is flagged. Webhook responds with the chatbot reply and session metadata.\n\n---\n\n### Configuration Requirements\n- LLAMA_ENDPOINT \u2014 Your Ollama URL (http://localhost:11434) or Groq/Together AI base URL\n- LLAMA_API_KEY \u2014 API key if using Groq or Together AI (leave blank for local Ollama)\n- LLAMA_MODEL \u2014 Model name e.g. llama3, llama3.1:8b, llama3.1:70b, mixtral\n- SLACK_WEBHOOK_URL \u2014 For human escalation alerts\n- GOOGLE_SHEET_ID \u2014 Conversation audit log\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c310d003-caf5-429a-b1c5-b6caabf8f795",
      "name": "Stage AB Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        384
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 418,
        "content": "### Stage A+B \u2014 Intake & Memory\n\nSession memory is built from a static store keyed by sessionId. Each call appends the new user message and returns the last 10 turns as the context window for Llama. No external DB needed."
      },
      "typeVersion": 1
    },
    {
      "id": "fdb8535a-020d-4d9c-ad06-13168bc9a975",
      "name": "Stage CD Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        336
      ],
      "parameters": {
        "color": 6,
        "width": 384,
        "height": 514,
        "content": "### Stage C+D \u2014 Route & Infer\n\nCode node selects the system prompt for the detected intent (support / sales / general / escalate). Single HTTP Request covers Ollama, Groq, and Together AI using the OpenAI-compatible chat completions endpoint format."
      },
      "typeVersion": 1
    },
    {
      "id": "feb698fe-8136-4aa7-813e-234c8c398d13",
      "name": "Stage EF Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        240
      ],
      "parameters": {
        "color": 6,
        "width": 848,
        "height": 706,
        "content": "### Stage E+F \u2014 Parse, Log & Deliver\n\nParse node updates session memory and checks for escalation signals. Sheets logs every turn for compliance and training data. Slack fires only when escalation is detected \u2014 not on every message. Webhook returns reply + session metadata."
      },
      "typeVersion": 1
    },
    {
      "id": "3c7ef5e1-4e69-426f-967c-a5649ee2f480",
      "name": "Receive Chat Message",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1072,
        632
      ],
      "parameters": {
        "path": "llama-chat",
        "options": {
          "rawBody": false
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "1dd5c1e5-8f6e-4cf7-8037-5ea638b4ce4b",
      "name": "Set Llama Config",
      "type": "n8n-nodes-base.set",
      "position": [
        1296,
        632
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "l01",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.body.sessionId || $json.sessionId || 'session-' + Date.now() }}"
            },
            {
              "id": "l02",
              "name": "userMessage",
              "type": "string",
              "value": "={{ $json.body.message || $json.message || '' }}"
            },
            {
              "id": "l03",
              "name": "userId",
              "type": "string",
              "value": "={{ $json.body.userId || $json.userId || 'anonymous' }}"
            },
            {
              "id": "l04",
              "name": "userName",
              "type": "string",
              "value": "={{ $json.body.userName || $json.userName || 'User' }}"
            },
            {
              "id": "l05",
              "name": "botPersona",
              "type": "string",
              "value": "={{ $json.body.botPersona || $json.botPersona || 'support' }}"
            },
            {
              "id": "l06",
              "name": "messageId",
              "type": "string",
              "value": "={{ 'MSG-' + Date.now() }}"
            },
            {
              "id": "l07",
              "name": "turnNumber",
              "type": "number",
              "value": "={{ $json.body.turnNumber || 1 }}"
            },
            {
              "id": "l08",
              "name": "LLAMA_ENDPOINT",
              "type": "string",
              "value": "YOUR_LLAMA_ENDPOINT"
            },
            {
              "id": "l09",
              "name": "LLAMA_API_KEY",
              "type": "string",
              "value": "YOUR_LLAMA_API_KEY"
            },
            {
              "id": "l10",
              "name": "LLAMA_MODEL",
              "type": "string",
              "value": "llama3.1:8b"
            },
            {
              "id": "l11",
              "name": "MAX_TOKENS",
              "type": "number",
              "value": 800
            },
            {
              "id": "l12",
              "name": "TEMPERATURE",
              "type": "number",
              "value": 0.7
            },
            {
              "id": "l13",
              "name": "SLACK_WEBHOOK",
              "type": "string",
              "value": "YOUR_SLACK_WEBHOOK_URL"
            },
            {
              "id": "l14",
              "name": "SHEET_ID",
              "type": "string",
              "value": "YOUR_GOOGLE_SHEET_ID"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ee18f566-5261-4687-a39f-8fbea8820338",
      "name": "Load Memory and Build Request",
      "type": "n8n-nodes-base.code",
      "position": [
        1520,
        632
      ],
      "parameters": {
        "jsCode": "\nvar d = $input.first().json;\n\nif (!d.userMessage || d.userMessage.trim() === '') {\n  throw new Error('Missing required field: message');\n}\n\n// \u2500\u2500 In-memory session store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// In production replace with Redis GET/SET or Postgres query\n// For n8n: use a static variable trick or pass history in the request body\nvar history = [];\ntry {\n  // Accept history from the client payload for stateless setups\n  var bodyHistory = $input.first().json.body && $input.first().json.body.history\n    ? $input.first().json.body.history : [];\n  if (Array.isArray(bodyHistory) && bodyHistory.length > 0) {\n    history = bodyHistory;\n  }\n} catch(e) { history = []; }\n\n// Keep last 10 turns (20 messages) to stay within context window\nhistory = history.slice(-20);\n\n// \u2500\u2500 Intent detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar msg = (d.userMessage || '').toLowerCase();\n\nvar intent = 'general';\nif (/refund|broken|damaged|not working|issue|bug|error|complaint|wrong|missing|problem/.test(msg)) {\n  intent = 'support';\n} else if (/price|pricing|plan|buy|purchase|cost|discount|trial|demo|upgrade|subscribe/.test(msg)) {\n  intent = 'sales';\n} else if (/human|agent|manager|escalate|speak to someone|real person|help me now/.test(msg)) {\n  intent = 'escalate';\n}\n\n// \u2500\u2500 System prompts per intent \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar systemPrompts = {\n  support: (\n    'You are a helpful and empathetic customer support agent. '\n    + 'Your goal is to resolve the customer\\'s issue efficiently. '\n    + 'If you cannot resolve it, acknowledge the problem clearly and offer to escalate. '\n    + 'Be concise \u2014 max 3 short paragraphs. Always end with a follow-up question or clear next step.'\n  ),\n  sales: (\n    'You are a knowledgeable and friendly sales assistant. '\n    + 'Help the customer understand the product value and guide them toward the right plan. '\n    + 'Do not pressure. Highlight benefits. Offer a free trial or demo when appropriate. '\n    + 'Keep replies under 150 words.'\n  ),\n  escalate: (\n    'You are a support agent handling an escalation request. '\n    + 'Acknowledge the customer\\'s frustration with empathy. '\n    + 'Confirm that a human agent will contact them within 2 hours. '\n    + 'Collect their preferred contact method if not already provided. '\n    + 'Be warm and reassuring.'\n  ),\n  general: (\n    'You are a helpful, friendly AI assistant. '\n    + 'Answer questions clearly and concisely. '\n    + 'If a question is outside your knowledge, say so honestly and suggest where they might find help. '\n    + 'Keep responses under 200 words.'\n  )\n};\n\nvar systemPrompt = systemPrompts[intent] || systemPrompts['general'];\n\n// \u2500\u2500 Build messages array for Llama \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar messages = [{ role: 'system', content: systemPrompt }];\n\n// Add conversation history\nhistory.forEach(function(turn) {\n  messages.push(turn);\n});\n\n// Add current user message\nmessages.push({ role: 'user', content: d.userMessage });\n\n// \u2500\u2500 Build request payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Compatible with Ollama /api/chat, Groq, and Together AI\nvar isOllama = (d.LLAMA_ENDPOINT || '').includes('localhost') || (d.LLAMA_ENDPOINT || '').includes('11434');\n\nvar requestPayload = {\n  model:       d.LLAMA_MODEL || 'llama3.1:8b',\n  messages:    messages,\n  temperature: d.TEMPERATURE || 0.7,\n  max_tokens:  d.MAX_TOKENS  || 800,\n  stream:      false\n};\n\n// Ollama uses slightly different field name\nif (isOllama) {\n  requestPayload.options = { temperature: d.TEMPERATURE || 0.7, num_predict: d.MAX_TOKENS || 800 };\n  delete requestPayload.temperature;\n  delete requestPayload.max_tokens;\n}\n\nvar apiPath = isOllama ? '/api/chat' : '/chat/completions';\n\nreturn [{ json: {\n  sessionId:      d.sessionId,\n  userId:         d.userId,\n  userName:       d.userName,\n  userMessage:    d.userMessage,\n  messageId:      d.messageId,\n  turnNumber:     d.turnNumber,\n  botPersona:     d.botPersona,\n  intent:         intent,\n  isEscalation:   intent === 'escalate',\n  history:        history,\n  messages:       messages,\n  systemPrompt:   systemPrompt,\n  requestPayload: JSON.stringify(requestPayload),\n  apiPath:        apiPath,\n  isOllama:       isOllama,\n  LLAMA_ENDPOINT: d.LLAMA_ENDPOINT,\n  LLAMA_API_KEY:  d.LLAMA_API_KEY,\n  LLAMA_MODEL:    d.LLAMA_MODEL,\n  SLACK_WEBHOOK:  d.SLACK_WEBHOOK,\n  SHEET_ID:       d.SHEET_ID,\n  startedAt:      new Date().toISOString()\n}}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b62d5dbc-e835-4a18-b2c5-89fbc27b7f7b",
      "name": "Call Llama Model",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1744,
        632
      ],
      "parameters": {
        "url": "={{ $json.LLAMA_ENDPOINT + $json.apiPath }}",
        "method": "POST",
        "options": {
          "timeout": 90000
        },
        "jsonBody": "={{ $json.requestPayload }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{ $json.LLAMA_API_KEY ? 'Bearer ' + $json.LLAMA_API_KEY : 'Bearer ollama' }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d633c9b0-fe9f-4d73-b0a7-f9ae089d24b8",
      "name": "Parse Reply and Update Memory",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        632
      ],
      "parameters": {
        "jsCode": "\nvar raw  = $('Call Llama Model').first().json;\nvar meta = $('Load Memory and Build Request').first().json;\n\n// \u2500\u2500 Extract assistant reply \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar reply = '';\ntry {\n  // Ollama format\n  if (raw.message && raw.message.content) {\n    reply = raw.message.content.trim();\n  }\n  // OpenAI-compatible format (Groq, Together)\n  else if (raw.choices && raw.choices.length > 0) {\n    reply = (raw.choices[0].message && raw.choices[0].message.content)\n      ? raw.choices[0].message.content.trim()\n      : (raw.choices[0].text || '').trim();\n  }\n  // Fallback\n  else {\n    reply = JSON.stringify(raw);\n  }\n} catch(e) {\n  reply = 'Sorry, I encountered an error. Please try again.';\n}\n\n// \u2500\u2500 Update session history \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar updatedHistory = (meta.history || []).slice();\nupdatedHistory.push({ role: 'user',      content: meta.userMessage });\nupdatedHistory.push({ role: 'assistant', content: reply });\n// Keep last 20 messages\nupdatedHistory = updatedHistory.slice(-20);\n\n// \u2500\u2500 Escalation detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar escalationPhrases = ['escalate', 'human agent', 'speak to someone', 'real person', 'supervisor'];\nvar userWantsEscalation = meta.isEscalation || false;\nvar botFlaggedEscalation = /i.ll connect you|escalating|human agent will|contact you within/i.test(reply);\nvar needsEscalation = userWantsEscalation || botFlaggedEscalation;\n\n// \u2500\u2500 Token usage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvar tokensUsed = 0;\ntry {\n  tokensUsed = raw.usage\n    ? (raw.usage.total_tokens || raw.usage.completion_tokens || 0)\n    : (raw.eval_count || 0);\n} catch(e) {}\n\nreturn [{ json: {\n  sessionId:       meta.sessionId,\n  userId:          meta.userId,\n  userName:        meta.userName,\n  messageId:       meta.messageId,\n  turnNumber:      meta.turnNumber,\n  intent:          meta.intent,\n  userMessage:     meta.userMessage,\n  assistantReply:  reply,\n  replyLength:     reply.length,\n  needsEscalation: needsEscalation,\n  updatedHistory:  updatedHistory,\n  turnCount:       updatedHistory.length / 2,\n  tokensUsed:      tokensUsed,\n  model:           meta.LLAMA_MODEL,\n  SLACK_WEBHOOK:   meta.SLACK_WEBHOOK,\n  SHEET_ID:        meta.SHEET_ID,\n  startedAt:       meta.startedAt,\n  completedAt:     new Date().toISOString()\n}}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "6ac4ebd6-a9bd-4fd9-b900-f4a4ea2991b1",
      "name": "Log Conversation Turn",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2416,
        464
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Chat Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.SHEET_ID }}"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5,
      "continueOnFail": true
    },
    {
      "id": "55941710-398b-44d4-976b-0052588192da",
      "name": "Check Escalation Flag",
      "type": "n8n-nodes-base.if",
      "position": [
        2192,
        728
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.needsEscalation }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "d84345d3-f85e-4732-ab73-b76f704f7fad",
      "name": "Alert Slack Escalation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2416,
        656
      ],
      "parameters": {
        "url": "={{ $json.SLACK_WEBHOOK }}",
        "method": "POST",
        "options": {},
        "jsonBody": "{\"text\": \"={{ ':rotating_light: Escalation from ' + $json.userName }}\", \"blocks\": [{\"type\": \"header\", \"text\": {\"type\": \"plain_text\", \"text\": \"={{ ':rotating_light: Escalation Alert \\u2014 ' + $json.userName }}\"}}, {\"type\": \"section\", \"fields\": [{\"type\": \"mrkdwn\", \"text\": \"={{ '*Session:*\\\\n' + $json.sessionId }}\"}, {\"type\": \"mrkdwn\", \"text\": \"={{ '*User:*\\\\n' + $json.userName + ' (' + $json.userId + ')' }}\"}, {\"type\": \"mrkdwn\", \"text\": \"={{ '*Intent:*\\\\n' + $json.intent }}\"}, {\"type\": \"mrkdwn\", \"text\": \"={{ '*Turn:*\\\\n' + $json.turnNumber }}\"}, {\"type\": \"mrkdwn\", \"text\": \"={{ '*Tokens Used:*\\\\n' + $json.tokensUsed }}\"}, {\"type\": \"mrkdwn\", \"text\": \"={{ '*Time:*\\\\n' + $json.completedAt }}\"}]}, {\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"={{ '*Last user message:*\\\\n> ' + $json.userMessage }}\"}}, {\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"={{ '*Bot last reply:*\\\\n_' + $json.assistantReply.substring(0,300) + '..._' }}\"}}, {\"type\": \"context\", \"elements\": [{\"type\": \"mrkdwn\", \"text\": \"={{ 'Flagged at ' + $json.completedAt + ' | n8n Llama Chatbot' }}\"}]}]}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "a0b2dadd-71c6-426c-9aa7-2048cc02c7c6",
      "name": "Send Chatbot Reply",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2640,
        656
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, messageId: $json.messageId, sessionId: $json.sessionId, reply: $json.assistantReply, intent: $json.intent, needsEscalation: $json.needsEscalation, turnCount: $json.turnCount, tokensUsed: $json.tokensUsed, model: $json.model, history: $json.updatedHistory, completedAt: $json.completedAt }) }}"
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "c8ffaedc-7d75-416e-9b02-cb949ba937c4",
  "connections": {
    "Call Llama Model": {
      "main": [
        [
          {
            "node": "Parse Reply and Update Memory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Llama Config": {
      "main": [
        [
          {
            "node": "Load Memory and Build Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Chat Message": {
      "main": [
        [
          {
            "node": "Set Llama Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Escalation Flag": {
      "main": [
        [
          {
            "node": "Alert Slack Escalation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Chatbot Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Conversation Turn": {
      "main": [
        [
          {
            "node": "Send Chatbot Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Slack Escalation": {
      "main": [
        [
          {
            "node": "Send Chatbot Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Memory and Build Request": {
      "main": [
        [
          {
            "node": "Call Llama Model",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Reply and Update Memory": {
      "main": [
        [
          {
            "node": "Log Conversation Turn",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check Escalation Flag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow builds a fully private, self-hosted AI chatbot using Meta Llama models. Unlike cloud-based AI APIs, every conversation stays on your infrastructure — no data leaves your environment. The chatbot remembers conversation history per session, routes different query…

Source: https://n8n.io/workflows/13811/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This template automates the extraction of structured data from Thai government letters received via LINE or uploaded to Google Drive. It uses Mistral AI for OCR and OpenAI for information extraction,

HTTP Request, Google Drive Trigger, Google Drive +4
AI & RAG

I made this little workflow with care for people like you who are part of busy WhatsApp groups and want a simple way to keep track of everything.

Google Drive, Google Sheets, OpenAI Chat +2
AI & RAG

RAG Assistant - Query. Uses httpRequest. Webhook trigger; 4 nodes.

HTTP Request
AI & RAG

The Ultimate Scraper for n8n uses Selenium and AI to retrieve any information displayed on a webpage. You can also use session cookies to log in to the targeted webpage for more advanced scraping need

OpenAI Chat, HTTP Request, Information Extractor +1
AI & RAG

z-Api. Uses httpRequest, openAi, redis, postgres. Webhook trigger; 61 nodes.

HTTP Request, OpenAI, Redis +4