{
  "name": "Docs site to knowledge base (n8n vector store)",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "name": "Run manually"
    },
    {
      "parameters": {
        "operation": "crawl",
        "crawl_url": "https://webcrawlerapi.com/docs",
        "items_limit": 20,
        "whitelist_regexp": "^https://webcrawlerapi\\.com/docs/.*",
        "blacklist_regexp": "",
        "respect_robots_txt": true,
        "max_depth": 2,
        "output_as_file": false
      },
      "type": "n8n-nodes-webcrawlerapi.webCrawlerApiNode",
      "typeVersion": 1,
      "position": [
        240,
        0
      ],
      "name": "Crawl docs site",
      "credentials": {
        "webCrawlerApi": {
          "id": "",
          "name": "WebCrawlerAPI account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// The WebCrawlerAPI node returns ONE item: the job plus one combined markdown string.\n// Pages are separated by a small header: ----\\nurl: <page url>\\n----\nconst { markdown, job } = $input.first().json;\nconst titles = Object.fromEntries(job.job_items.map((i) => [i.original_url, i.title]));\n\nconst parts = markdown.split(/^----\\nurl: (\\S+)\\n----\\n/m);\nconst pages = [];\nfor (let i = 1; i < parts.length; i += 2) {\n  pages.push({\n    json: {\n      url: parts[i],\n      title: titles[parts[i]] || '',\n      markdown: parts[i + 1].trim(),\n      last_crawled_at: job.finished_at,\n    },\n  });\n}\nreturn pages;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ],
      "name": "One item per page"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.markdown.length }}",
              "rightValue": 300,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        720,
        0
      ],
      "name": "Drop near-empty pages"
    },
    {
      "parameters": {
        "mode": "insert",
        "memoryKey": {
          "__rl": true,
          "value": "docs_knowledge_base",
          "mode": "list",
          "cachedResultName": "docs_knowledge_base"
        },
        "clearStore": true
      },
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1.3,
      "position": [
        960,
        0
      ],
      "name": "Simple Vector Store"
    },
    {
      "parameters": {
        "jsonMode": "expressionData",
        "jsonData": "={{ $json.markdown }}",
        "textSplittingMode": "custom",
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "url",
                "value": "={{ $json.url }}"
              },
              {
                "name": "title",
                "value": "={{ $json.title }}"
              },
              {
                "name": "last_crawled_at",
                "value": "={{ $json.last_crawled_at }}"
              }
            ]
          }
        }
      },
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "typeVersion": 1.1,
      "position": [
        1040,
        220
      ],
      "name": "Default Data Loader"
    },
    {
      "parameters": {
        "chunkSize": 1000,
        "chunkOverlap": 100,
        "options": {
          "splitCode": "markdown"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "typeVersion": 1,
      "position": [
        1100,
        420
      ],
      "name": "Recursive Character Text Splitter"
    },
    {
      "parameters": {
        "model": "openai/text-embedding-3-small",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "typeVersion": 1.2,
      "position": [
        900,
        220
      ],
      "name": "Embeddings OpenAI",
      "credentials": {
        "openAiApi": {
          "id": "",
          "name": "OpenAI / OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "public": true,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        640
      ],
      "name": "Ask the docs"
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "You answer questions about the WebCrawlerAPI documentation. Always search the knowledge base tool first. Answer only from what it returns and name the source URL. If the answer is not there, say so."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3,
      "position": [
        300,
        640
      ],
      "name": "Docs support agent"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "openai/gpt-4o-mini",
          "mode": "id"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        220,
        880
      ],
      "name": "Chat model",
      "credentials": {
        "openAiApi": {
          "id": "",
          "name": "OpenAI / OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "mode": "retrieve-as-tool",
        "toolDescription": "Search the WebCrawlerAPI documentation. Returns the most relevant doc chunks with their source URL.",
        "memoryKey": {
          "__rl": true,
          "value": "docs_knowledge_base",
          "mode": "list",
          "cachedResultName": "docs_knowledge_base"
        },
        "topK": 8,
        "includeDocumentMetadata": true
      },
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1.3,
      "position": [
        480,
        880
      ],
      "name": "Docs knowledge base"
    }
  ],
  "connections": {
    "Run manually": {
      "main": [
        [
          {
            "node": "Crawl docs site",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Crawl docs site": {
      "main": [
        [
          {
            "node": "One item per page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "One item per page": {
      "main": [
        [
          {
            "node": "Drop near-empty pages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Drop near-empty pages": {
      "main": [
        [
          {
            "node": "Simple Vector Store",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader": {
      "ai_document": [
        [
          {
            "node": "Simple Vector Store",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI": {
      "ai_embedding": [
        [
          {
            "node": "Simple Vector Store",
            "type": "ai_embedding",
            "index": 0
          },
          {
            "node": "Docs knowledge base",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Ask the docs": {
      "main": [
        [
          {
            "node": "Docs support agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat model": {
      "ai_languageModel": [
        [
          {
            "node": "Docs support agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Docs knowledge base": {
      "ai_tool": [
        [
          {
            "node": "Docs support agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "active": false
}