{
  "name": "Chat with a Webpage (RAG)",
  "nodes": [
    {
      "id": "ae5f8056-c1c1-4c1c-9c1c-000000000001",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "parameters": {
        "options": {
          "title": "Ask the docs",
          "subtitle": "Ask anything about the URL configured in the 'Set KB URL' node.",
          "allowFileUploads": false,
          "allowedOrigins": "*"
        },
        "public": true
      },
      "webhookId": "rag-chat"
    },
    {
      "id": "ae5f8056-c2c2-4c2c-9c2c-000000000002",
      "name": "Set KB URL",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        232,
        0
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "kbUrl",
              "value": "https://docs.n8n.io/",
              "type": "string"
            },
            {
              "name": "userQuery",
              "value": "={{ $json.chatInput || $json.message || '' }}",
              "type": "string"
            },
            {
              "name": "sessionId",
              "value": "={{ $json.sessionId || 'default' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "ae5f8056-c3c3-4c3c-9c3c-000000000003",
      "name": "Fetch KB Page",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        472,
        0
      ],
      "parameters": {
        "url": "={{ $json.kbUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text",
              "neverError": true
            }
          },
          "timeout": 15000
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (compatible; n8n-rag-chat/1.0)"
            }
          ]
        }
      }
    },
    {
      "id": "ae5f8056-c4c4-4c4c-9c4c-000000000004",
      "name": "Extract & Chunk",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        712,
        0
      ],
      "parameters": {
        "jsCode": "const html = $input.first().json.data || $input.first().json.body || '';\nconst kbUrl = $('Set KB URL').item.json.kbUrl;\nconst userQuery = $('Set KB URL').item.json.userQuery;\nconst sessionId = $('Set KB URL').item.json.sessionId;\n\n// Strip HTML\nconst text = html\n  .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, ' ')\n  .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, ' ')\n  .replace(/<nav[^>]*>[\\s\\S]*?<\\/nav>/gi, ' ')\n  .replace(/<footer[^>]*>[\\s\\S]*?<\\/footer>/gi, ' ')\n  .replace(/<[^>]+>/g, ' ')\n  .replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '\"')\n  .replace(/\\s+/g, ' ').trim();\n\n// Simple keyword-based chunk ranking against the user query.\n// For a small page this is enough; for larger pages you would switch to a real vector store.\nconst CHUNK = 500; // chars\nconst chunks = [];\nfor (let i = 0; i < text.length; i += CHUNK) chunks.push(text.slice(i, i + CHUNK));\n\nconst qTerms = userQuery.toLowerCase().match(/[a-z0-9]+/g) || [];\nconst scored = chunks.map((c, idx) => {\n  const lower = c.toLowerCase();\n  let score = 0;\n  for (const t of qTerms) if (t.length > 2 && lower.includes(t)) score += 1;\n  return { idx, score, chunk: c };\n}).sort((a, b) => b.score - a.score).slice(0, 6);\n\n// Rebuild a context window of top chunks (dedupe by position to keep order)\nconst top = scored.sort((a, b) => a.idx - b.idx).map(s => s.chunk).join('\\n---\\n');\n\nreturn [{ json: { kbUrl, userQuery, sessionId, context: top, chunkCount: chunks.length, retrievedCount: scored.length } }];"
      }
    },
    {
      "id": "ae5f8056-c5c5-4c5c-9c5c-000000000005",
      "name": "RAG Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        952,
        0
      ],
      "parameters": {
        "promptType": "define",
        "text": "=You are a careful documentation assistant. Answer the user's question using ONLY the context below. If the context does not contain the answer, say so directly \u2014 do not invent.\n\nKB SOURCE: {{ $json.kbUrl }}\n\nRETRIEVED CONTEXT:\n\"\"\"\n{{ $json.context }}\n\"\"\"\n\nUSER QUESTION: {{ $json.userQuery }}\n\nRULES\n- Cite the source inline as [source]({{ $json.kbUrl }}) once near the top of your answer.\n- Prefer short, structured answers (use bullets or a small table when listing).\n- Do not add disclaimers about being an AI.\n- If the user's question is outside the docs, say so and suggest what they could ask instead.",
        "options": {}
      }
    },
    {
      "id": "ae5f8056-c6c6-4c6c-9c6c-000000000006",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        888,
        224
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini"
        },
        "options": {}
      }
    },
    {
      "id": "ae5f8056-c7c7-4c7c-9c7c-000000000007",
      "name": "Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        1040,
        224
      ],
      "parameters": {
        "sessionIdType": "fromInput",
        "sessionKey": "={{ $('Set KB URL').item.json.sessionId }}",
        "contextWindowLength": 6
      }
    }
  ],
  "connections": {
    "When chat message received": {
      "main": [
        [
          {
            "node": "Set KB URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set KB URL": {
      "main": [
        [
          {
            "node": "Fetch KB Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch KB Page": {
      "main": [
        [
          {
            "node": "Extract & Chunk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract & Chunk": {
      "main": [
        [
          {
            "node": "RAG Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "RAG Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "RAG Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null
}