image.png

{
  "nodes": [
    {
      "parameters": {
        "functionCode": "// 從 items[0].json 中取出 body 內的資料\\nconst payload = items[0].json.body;\\n\\n// 檢查 payload 是否存在且有 events 屬性\\nif (!payload || !payload.events || !Array.isArray(payload.events) || payload.events.length === 0) {\\n  throw new Error('未收到有效的 events 資料');\\n}\\n\\n// 提取 replyToken 與 userMessage\\nconst replyToken = payload.events[0].replyToken;\\nconst userMessage = payload.events[0].message.text;\\n\\nreturn [{ replyToken, userMessage }];\\n"
      },
      "name": "Parse LINE Message",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        140,
        -180
      ],
      "id": "c6e10252-eaef-4d38-8ab9-39aabce39dd0"
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "url": "<https://api.line.me/v2/bot/message/reply>",
        "responseFormat": "string",
        "jsonParameters": true,
        "options": {},
        "bodyParametersJson": "={\\n  \\"replyToken\\": \\"{{ $json.replyToken }}\\",\\n  \\"messages\\": [\\n    {\\n      \\"type\\": \\"text\\",\\n      \\"text\\": \\"{{ $json.answer }}\\"\\n    }\\n  ]\\n}\\n",
        "headerParametersJson": "{\\n  \\"Authorization\\": \\"Bearer j3EbNCnP+c47jLfFtd0TuEMgCVvNeonxALwtRMxhwNumaM4HARI6/GSmDMFhmXvGDbnCyy3h1/tMh6FUDDg8MewR9LSid+0kqBDK5UlcbvQecQvpb8+kZdSOQKFHhgAlHGqu9YMz+rjet2xR5bT5nQdB04t89/1O/w1cDnyilFU=\\",\\n  \\"Content-Type\\": \\"application/json\\"\\n}"
      },
      "name": "Reply to LINE",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        1560,
        -120
      ],
      "id": "590db112-209f-4e6c-8bbc-c725dcd6fd3a"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "line-webhook",
        "options": {}
      },
      "name": "Webhook1",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        -120,
        -120
      ],
      "id": "88dc7f78-90c7-4326-80e8-e2a79dcb3af0",
      "webhookId": "73099445-ab48-4c29-ae8e-ea32299d1988"
    },
    {
      "parameters": {
        "operation": "search",
        "text": "我初進宮的那一天",
        "options": {}
      },
      "name": "Get Existing Records",
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2,
      "position": [
        40,
        40
      ],
      "id": "6e4530ff-701a-413a-89b4-9188210de8ca",
      "executeOnce": true,
      "credentials": {
        "notionApi": {
          "id": "mLkW1oHj5qZYbd3M",
          "name": "Notion account"
        }
      }
    },
    {
      "parameters": {
        "functionCode": "// 取得所有輸入項目\\nconst inputItems = $input.all();\\n\\n// 建立新的結果陣列\\nconst outputItems = inputItems.map(item => {\\n  let nameField = item.json.name;\\n  \\n  // 如果 nameField 是字串,嘗試將它解析成 JSON 或直接包裝成陣列\\n  if (typeof nameField === 'string') {\\n    const trimmed = nameField.trim();\\n    // 如果看起來像 JSON 陣列,就嘗試解析\\n    if (trimmed.startsWith('[') && trimmed.endsWith(']')) {\\n      try {\\n        nameField = JSON.parse(nameField);\\n      } catch (error) {\\n        // 解析失敗則直接包裝整個字串\\n        nameField = [{ Text: nameField }];\\n      }\\n    } else {\\n      // 直接包裝成陣列\\n      nameField = [{ Text: nameField }];\\n    }\\n  }\\n  \\n  // 檢查 nameField 是否為陣列且至少有一個元素\\n  if (!Array.isArray(nameField) || nameField.length === 0) {\\n    return { json: { error: 'No text found in the \\"name\\" field' } };\\n  }\\n  \\n  // 取得第一個物件的 Text 屬性作為文本內容\\n  const text = nameField[0].Text;\\n  if (!text) {\\n    return { json: { error: 'No text found in this item' } };\\n  }\\n  \\n  // 以單一換行符號分割文本\\n  // 並移除每行前後的空白,過濾掉空行\\n  const lines = text.split('\\\\n').map(line => line.trim()).filter(line => line !== \\"\\");\\n  \\n  // 如果需要對每行進行進一步處理,例如長度超過200字時根據句號拆分:\\n  const segments = [];\\n  lines.forEach(line => {\\n    if (line.length > 200) {\\n      // 以句號拆分,並保留句號\\n      const sentences = line.split('。').map(s => s.trim()).filter(s => s !== \\"\\");\\n      let temp = \\"\\";\\n      sentences.forEach((s, index) => {\\n        temp += s + \\"。\\";\\n        // 每3個句子合併成一個片段\\n        if ((index + 1) % 3 === 0) {\\n          segments.push(temp.trim());\\n          temp = \\"\\";\\n        }\\n      });\\n      if (temp !== \\"\\") {\\n        segments.push(temp.trim());\\n      }\\n    } else {\\n      segments.push(line);\\n    }\\n  });\\n  \\n  return { json: { segments: segments } };\\n});\\n\\n// 回傳處理後的結果\\nreturn outputItems;\\n"
      },
      "name": "Clean Answer1",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        200,
        40
      ],
      "id": "3978f25a-d35d-4fac-90f8-a29039625ab1"
    },
    {
      "parameters": {
        "mode": "insert"
      },
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1,
      "position": [
        340,
        0
      ],
      "id": "49204389-fdd3-45d6-bb5b-2384a453d448",
      "name": "In-Memory Vector Store1"
    },
    {
      "parameters": {
        "model": "nomic-embed-text:latest"
      },
      "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
      "typeVersion": 1,
      "position": [
        300,
        220
      ],
      "id": "b76707f9-118a-48fb-b98c-a0374c1f17ca",
      "name": "Embeddings Ollama",
      "credentials": {
        "ollamaApi": {
          "id": "3YPITdUvF4SqZwvs",
          "name": "Ollama account 2"
        }
      }
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "typeVersion": 1,
      "position": [
        460,
        160
      ],
      "id": "738c991a-095d-40e0-8392-efa9be66b196",
      "name": "Default Data Loader"
    },
    {
      "parameters": {
        "model": "deepseek-r1:7b",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmOllama",
      "typeVersion": 1,
      "position": [
        760,
        60
      ],
      "id": "440a200b-1d0c-4cc6-8fca-f842982979c5",
      "name": "Ollama Model1",
      "credentials": {
        "ollamaApi": {
          "id": "3YPITdUvF4SqZwvs",
          "name": "Ollama account 2"
        }
      }
    },
    {
      "parameters": {
        "topK": 666
      },
      "type": "@n8n/n8n-nodes-langchain.retrieverVectorStore",
      "typeVersion": 1,
      "position": [
        940,
        60
      ],
      "id": "0892163a-2715-4c96-ad7d-d139e157f413",
      "name": "Vector Store Retriever"
    },
    {
      "parameters": {},
      "type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
      "typeVersion": 1,
      "position": [
        940,
        200
      ],
      "id": "49ef2739-e50f-4be7-ba28-cf45c587b73b",
      "name": "In-Memory Vector Store"
    },
    {
      "parameters": {
        "model": "nomic-embed-text:latest"
      },
      "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
      "typeVersion": 1,
      "position": [
        960,
        360
      ],
      "id": "b8f5c58f-d81a-4199-a685-43f07e275149",
      "name": "Embeddings Ollama1",
      "credentials": {
        "ollamaApi": {
          "id": "3YPITdUvF4SqZwvs",
          "name": "Ollama account 2"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        660,
        -160
      ],
      "id": "0b054147-16a8-4605-859b-183fab3e9ff0",
      "name": "Merge"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={\\n  \\"prompt\\": \\"使用以下背景資料回答問題:{{ $json.userMessage }}\\\\n\\\\n{context}\\"\\n}\\n",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainRetrievalQa",
      "typeVersion": 1.4,
      "position": [
        840,
        -120
      ],
      "id": "fdeb4b84-eb10-46e4-aedd-34af8971a48f",
      "name": "Question and Answer Chain",
      "executeOnce": true
    },
    {
      "parameters": {
        "chunkSize": 5000
      },
      "type": "@n8n/n8n-nodes-langchain.textSplitterTokenSplitter",
      "typeVersion": 1,
      "position": [
        480,
        320
      ],
      "id": "88560be7-cc22-470a-a6e5-13f5299ece46",
      "name": "Token Splitter"
    },
    {
      "parameters": {
        "jsCode": "/*\\n  此節點用來清洗答案,去除 \\\\n、*、- 和 <think> 標籤的內容\\n  假設輸入的答案格式為 \\"<think>\\\\n\\\\n</think>\\\\n\\\\n1 + 1 = **2**\\"\\n*/\\nconst input = $input.first().json.response.text || \\"\\";\\n\\n\\n// 使用正則表達式去除 <think> 標籤與其內容,並去除多餘空白\\nlet cleaned = input.replace(/[\\\\s\\\\S]*?\\\\s*/gi, '').trim();\\n\\n// 去除所有 \\\\n、*、- 和多餘的空格\\ncleaned = cleaned.replace(/[\\\\n\\\\*\\\\-\\\\\\"\\\\”\\\\“]/g, ' ').replace(/\\\\s+/g, ' ').trim();\\n\\n// 返回 JSON 格式的答案\\nreturn [{ answer: cleaned }];\\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        -120
      ],
      "id": "ff1e6ec4-b644-4675-b3c2-d7d21cd620cd",
      "name": "Code"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineAll",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1340,
        -260
      ],
      "id": "d5ae6790-a398-4162-8e46-d546093b1f6c",
      "name": "Merge1"
    }
  ],
  "connections": {
    "Parse LINE Message": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply to LINE": {
      "main": [
        []
      ]
    },
    "Webhook1": {
      "main": [
        [
          {
            "node": "Parse LINE Message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Existing Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Existing Records": {
      "main": [
        [
          {
            "node": "Clean Answer1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Answer1": {
      "main": [
        [
          {
            "node": "In-Memory Vector Store1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "In-Memory Vector Store1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Embeddings Ollama": {
      "ai_embedding": [
        [
          {
            "node": "In-Memory Vector Store1",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader": {
      "ai_document": [
        [
          {
            "node": "In-Memory Vector Store1",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Question and Answer Chain",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Vector Store Retriever": {
      "ai_retriever": [
        [
          {
            "node": "Question and Answer Chain",
            "type": "ai_retriever",
            "index": 0
          }
        ]
      ]
    },
    "In-Memory Vector Store": {
      "ai_vectorStore": [
        [
          {
            "node": "Vector Store Retriever",
            "type": "ai_vectorStore",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings Ollama1": {
      "ai_embedding": [
        [
          {
            "node": "In-Memory Vector Store",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Question and Answer Chain",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Question and Answer Chain": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Token Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Reply to LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "b4675dd1a7ebe0e31fae9b043df25c973d6121b6f98cd79084b3ea3b081c6fe8"
  }
}