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 (starter)",
  "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": {
        "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": {
        "content": "## Your task\n**Ingest** (top): after Has Text? → **Simple Vector Store** (Insert Documents, key `course09_kb`) with **Embeddings OpenAI** (text-embedding-3-small) and a **Default Data Loader** (JSON, expression `{{ $json.body.text }}`, metadata `source`) + **Recursive Character Text Splitter** (800 / 100). Respond with the chunk count.\n\n**Ask** (bottom): Chat Trigger → **AI Agent** + OpenAI model + Simple Memory + a **Simple Vector Store** in *Retrieve Documents (As Tool)* mode named `search_docs` (same key, its own Embeddings node). System message: answer only from search results and cite the source.\n\nTest: `WITH_CREDS=1 ./test/test.sh`",
        "height": 260,
        "width": 420
      },
      "id": "starter-task-note",
      "name": "Your Task",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -340
      ]
    }
  ],
  "connections": {
    "Ingest API": {
      "main": [
        [
          {
            "node": "Has Text?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Text?": {
      "main": [
        [],
        [
          {
            "node": "Respond No Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ask the Docs": {
      "main": [
        []
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}