Skip to content

In n8n, copy this file and paste it onto the canvas, or use Import from File.

{
  "name": "09 - RAG Q&A over Your Docs",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "kb/ingest",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "m09-ingest-webhook",
      "name": "Ingest API",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "course-09-ingest"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "i1",
              "leftValue": "={{ String($json.body.text ?? '').trim().length }}",
              "rightValue": 20,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "m09-has-text",
      "name": "Has Text?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { error: 'send {\"text\": \"...\", \"source\": \"name\"} with at least 20 characters of text' } }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "m09-400",
      "name": "Respond No Text",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        260,
        220
      ]
    },
    {
      "parameters": {
        "mode": "insert",
        "memoryKey": {
          "__rl": true,
          "mode": "id",
          "value": "course09_kb"
        }
      },
      "id": "m09-insert",
      "name": "Add to Knowledge Base",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1.3,
      "position": [
        480,
        -100
      ]
    },
    {
      "parameters": {
        "model": "text-embedding-3-small",
        "options": {}
      },
      "id": "m09-embed-insert",
      "name": "Embeddings (ingest)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "typeVersion": 1.2,
      "position": [
        400,
        120
      ]
    },
    {
      "parameters": {
        "jsonMode": "expressionData",
        "jsonData": "={{ $json.body.text }}",
        "textSplittingMode": "custom",
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "source",
                "value": "={{ $json.body.source ?? 'unnamed' }}"
              }
            ]
          }
        }
      },
      "id": "m09-loader",
      "name": "Document Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "typeVersion": 1.1,
      "position": [
        560,
        120
      ]
    },
    {
      "parameters": {
        "chunkSize": 800,
        "chunkOverlap": 100,
        "options": {}
      },
      "id": "m09-splitter",
      "name": "Text Splitter",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "typeVersion": 1,
      "position": [
        640,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { status: 'ingested', source: $('Ingest API').item.json.body.source ?? 'unnamed', chunks: $input.all().length } }}",
        "options": {}
      },
      "id": "m09-ingested",
      "name": "Respond Ingested",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        840,
        -100
      ]
    },
    {
      "parameters": {
        "public": true,
        "mode": "hostedChat",
        "initialMessages": "Hi! Ask me anything about the Brewlab team handbook: time off, remote work, expenses, equipment…",
        "options": {
          "title": "Handbook Assistant",
          "showWelcomeScreen": false
        }
      },
      "id": "m09-chat",
      "name": "Ask the Docs",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.4,
      "position": [
        0,
        520
      ],
      "webhookId": "course-09-docs-chat"
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "You answer questions about the company's internal documents.\n\nRULES:\n- ALWAYS search the knowledge base with the search_docs tool before answering a question about the company.\n- Answer ONLY with information found in the search results. If the answer is not there, say: \"I couldn't find that in the documents.\" and suggest who to ask if the documents mention it.\n- Quote exact numbers, dates and names from the documents.\n- End every answer with the source in brackets, e.g. [source: handbook].\n- Keep answers under 80 words.",
          "maxIterations": 5
        }
      },
      "id": "m09-agent",
      "name": "Docs Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        260,
        520
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini",
          "cachedResultName": "gpt-5-mini"
        },
        "options": {}
      },
      "id": "m09-model",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        100,
        760
      ]
    },
    {
      "parameters": {
        "contextWindowLength": 6
      },
      "id": "m09-memory",
      "name": "Conversation Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        260,
        760
      ]
    },
    {
      "parameters": {
        "mode": "retrieve-as-tool",
        "toolDescription": "Search the company documents (handbook, policies). Input: a short search query in English. Returns the most relevant passages with their source.",
        "memoryKey": {
          "__rl": true,
          "mode": "id",
          "value": "course09_kb"
        },
        "topK": 4
      },
      "id": "m09-search",
      "name": "search_docs",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1.3,
      "position": [
        440,
        760
      ]
    },
    {
      "parameters": {
        "model": "text-embedding-3-small",
        "options": {}
      },
      "id": "m09-embed-search",
      "name": "Embeddings (search)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "typeVersion": 1.2,
      "position": [
        440,
        960
      ]
    }
  ],
  "connections": {
    "Ingest API": {
      "main": [
        [
          {
            "node": "Has Text?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Text?": {
      "main": [
        [
          {
            "node": "Add to Knowledge Base",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond No Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Knowledge Base": {
      "main": [
        [
          {
            "node": "Respond Ingested",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings (ingest)": {
      "ai_embedding": [
        [
          {
            "node": "Add to Knowledge Base",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Document Loader": {
      "ai_document": [
        [
          {
            "node": "Add to Knowledge Base",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Document Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "Ask the Docs": {
      "main": [
        [
          {
            "node": "Docs Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Docs Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Memory": {
      "ai_memory": [
        [
          {
            "node": "Docs Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "search_docs": {
      "ai_tool": [
        [
          {
            "node": "Docs Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings (search)": {
      "ai_embedding": [
        [
          {
            "node": "search_docs",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}