This workflow follows the Agent → Chat Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "WooriFisa \ucd5c\uc885",
"nodes": [
{
"parameters": {
"collectionName": "questions",
"databaseName": "fisa_db"
},
"type": "@n8n/n8n-nodes-langchain.memoryMongoDbChat",
"typeVersion": 1,
"position": [
-992,
1552
],
"id": "1d034679-e702-4318-8eb9-ef5a5da94e8f",
"name": "MongoDB Chat Memory",
"credentials": {
"mongoDb": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"hasOutputParser": true,
"options": {
"systemMessage": "You are an intelligent assistant acting as a teaching assistant (TA)\nfor the \"\uc6b0\ub9acFISA \ud074\ub77c\uc6b0\ub4dc \uc5d4\uc9c0\ub2c8\uc5b4\ub9c1\" course.\n\nYour role:\n- Support learners by answering questions clearly and practically.\n- Explain concepts in a calm, professional, but approachable teaching-assistant tone.\n- Guide learners using course-aligned explanations rather than overly academic or vague answers.\n\nYou answer user questions using three data sources in this priority order:\n1) Google Sheets (daily schedule)\n2) Blog vector store\n3) Lecture vector store\n\nData sources:\n\n1) Google Sheets (via \u201cGet row(s) in sheet\u201d)\n - Contains date-based daily schedules and learning plans\n - Each row represents a specific date and its detailed tasks\n - This is the highest priority source when the question involves:\n \uc77c\uc815, \uc624\ub298 \ud560 \uac83, \uacfc\uc81c, \uc9c4\ub3c4, \uacc4\ud68d, \ub9c8\uac10, \uc2a4\ucf00\uc904, \uc8fc\ucc28, n\uc8fc\ucc28\n\n2) Blog vector store\n - Contains embedded study journal blog posts\n - metadata.type = \"blog\"\n - URLs may come from Velog or Tistory\n\n3) Lecture vector store\n - Contains official lecture materials\n - metadata.type = \"lecture\"\n - URLs must come from Notion\n\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nQuestion Classification (MANDATORY)\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nFor every user question, you MUST classify it into exactly one of the following types:\n\n- \"\uc77c\uc815 \uc9c8\ubb38\"\n \u2192 The user asks about dates, weeks, what to do today, progress, deadlines, schedules, or n\uc8fc\ucc28\n\n- \"\uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38\"\n \u2192 The user asks about technologies, concepts, tools, or what was learned\n\n- \"\uc77c\ubc18 \uc9c8\ubb38\"\n \u2192 The user asks about the program, process, or anything not directly tied to schedule or learning content\n\n\nExamples:\n- \"\uc624\ub298 \ubb50 \ud574\uc57c \ud574?\" \u2192 \uc77c\uc815 \uc9c8\ubb38\n- \"3\uc8fc\ucc28 \ubb50 \ubc30\uc6e0\uc5b4?\" \u2192 \uc77c\uc815 \uc9c8\ubb38 + \uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38 (treat as BOTH)\n- \"Spring\uc774 \ubb50\uc57c?\" \u2192 \uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38\n- \"\uc6b0\ub9acFISA \uc5b4\ub5a4 \uacfc\uc815\uc774\uc57c?\" \u2192 \uc77c\ubc18 \uc9c8\ubb38\n\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nRetrieval Strategy by Question Type\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n1) If the question is \"\uc77c\uc815 \uc9c8\ubb38\":\n \u2192 Query Google Sheets first\n \u2192 This is the main factual source\n\n2) If the question is \"\uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38\":\n \u2192 Search Blog vector store first\n \u2192 Then search Lecture vector store\n\n3) If the question is \"\uc77c\ubc18 \uc9c8\ubb38\":\n \u2192 Do NOT retrieve from any vector store unless clearly needed\n\n\nIf the user asks about a specific week (e.g. \"3\uc8fc\ucc28\", \"\uc774\ubc88 \uc8fc\ucc28\", \"n\uc8fc\ucc28\"):\nThis must be treated as BOTH:\n- \uc77c\uc815 \uc9c8\ubb38\n- \uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38\n\nIn that case:\n1. Retrieve Google Sheets for that week\n2. Use the schedule content as the semantic query\n3. Retrieve Blog vector store\n4. Retrieve Lecture vector store\n\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nLearning Category (ONLY for \uc218\uc5c5 \ub0b4\uc6a9 \uc9c8\ubb38)\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nIf and only if the question is related to learning content, assign exactly one category:\n\n- \uc790\ubc14\n- \ub3c4\ucee4\n- \ub9ac\ub205\uc2a4\n- \ud074\ub77c\uc6b0\ub4dc\n- AWS\n\nIf the question is \"\uc77c\uc815 \uc9c8\ubb38\" or \"\uc77c\ubc18 \uc9c8\ubb38\":\n\u2192 Category must be \"\uc5c6\uc74c\"\n\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nOutput Format (TEXT, not JSON)\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nYour response must be in plain readable text with the following sections:\n\n1. \uc9c8\ubb38 \uc720\ud615\n2. \uc77c\ubc18 \ub2f5\ubcc0\n3. \uae30\ubcf8 \uc9c0\uc2dd \uc694\uc57d\n4. \ud559\uc2b5 \uce74\ud14c\uace0\ub9ac\n5. \uc77c\uc815 (Google Sheets \uae30\ubc18, \ud574\ub2f9\ub420 \ub54c\ub9cc)\n6. \uc218\uc5c5 \uc790\ub8cc (Lectures)\n7. \ube14\ub85c\uadf8 \uc790\ub8cc (Blogs)\n\n\nFor Lectures and Blogs:\n- Show title, summary, and original URL\n- Never invent URLs\n- Lecture URLs must be from Notion\n- Blog URLs must be from Velog or Tistory\n- If no data exists, clearly state that the section is empty\n\n\nAlways remember:\n- Schedules come from Google Sheets\n- Blogs are searched before lectures\n- Lectures are used to reinforce blogs when available"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
-864,
1328
],
"id": "7aa36697-b66e-4fd8-af88-475956b70376",
"name": "Chatbot"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "edcd64db-8ece-412f-84ee-fdce959b7f39",
"leftValue": "",
"rightValue": "",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-896,
1024
],
"id": "c9d8b1f2-4951-4ad2-b26d-ff3dbaff1f7e",
"name": "\ud06c\ub864\ub9c1 \uacb0\uacfc \ud310\ub2e8(Notion \uad50\uc548)"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 1
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-1344,
160
],
"id": "4cc3f134-badb-499f-8990-2bed62848259",
"name": "\uc8fc\uac04 \ud06c\ub864\ub7ec"
},
{
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-1344,
1024
],
"id": "de323e23-d43f-49c9-aae8-a23f8f6e6aed",
"name": "\uc77c\uac04 \ud06c\ub864\ub7ec"
},
{
"parameters": {
"fieldToSplitOut": "data.searchPosts.posts",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
-896,
144
],
"id": "7df8af56-d283-490a-9f40-1e4e23affe23",
"name": "Split Out"
},
{
"parameters": {
"url": "=https://velog.io/@{{ $json.user.username }}/{{ $json.url_slug }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
0,
0
],
"id": "c70c86de-6554-4a65-b9eb-180276b7dd3c",
"name": "\uc0c1\uc138\uc815\ubcf4 \uac00\uc838\uc624\uae30",
"onError": "continueErrorOutput"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-224,
144
],
"id": "058ab0ab-3436-41db-91b1-2c28d4481efa",
"name": "1\uac1c\uc529"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 1
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-1344,
592
],
"id": "8a117cf1-cce7-4d6e-a5c4-8d05129e834e",
"name": "\uc8fc\uac04 \ud06c\ub864\ub7ec1"
},
{
"parameters": {
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n const text = item.json.data\n .replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, '')\n .trim();\n item.json.data = text;\n}\n\nreturn $input.all();"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
],
"id": "e80ac6b2-fd5f-4bb1-b88d-4eeb9c3ead96",
"name": "HTML \ud0dc\uadf8 \uc81c\uac70"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
1392,
368
],
"id": "50cdfe20-e6d6-4389-b7de-ac1a17d2eb05",
"name": "Default Data Loader"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.4,
"position": [
-1344,
1712
],
"id": "e80cd3e0-4538-44c0-9361-879f82c8c017",
"name": "When chat message received"
},
{
"parameters": {
"mode": "insert",
"pineconeIndex": {
"__rl": true,
"value": "woorifisa-lectures",
"mode": "list",
"cachedResultName": "woorifisa-lectures"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
"typeVersion": 1.3,
"position": [
128,
1008
],
"id": "3abbe575-849b-4eb3-9705-02021e6ee582",
"name": "\uad50\uc548 \uc2a4\ud1a0\uc5b4",
"credentials": {
"pineconeApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
256,
1232
],
"id": "cb09bfd7-4f7b-4fa7-84d1-410d8048cd0b",
"name": "Default Data Loader1"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.3,
"position": [
-1120,
1552
],
"id": "06e38cb3-f81d-48c1-acef-ecc58bbf7a41",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {
"dimensions": 1024
}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
1264,
368
],
"id": "6cf0dc8f-c183-46d1-96dd-08ec30f8e55e",
"name": "Embeddings OpenAI",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {
"dimensions": 1024
}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
128,
1232
],
"id": "2d97c548-649a-4e29-8572-3550ee65a8f0",
"name": "Embeddings OpenAI1",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-672,
1008
],
"id": "77fce335-20cd-44f7-928c-77a0f02bdd7b",
"name": "Loop Over Items2"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"responses": {
"values": [
{
"role": "system",
"content": "You are an assistant that prepares educational materials for semantic embedding.\n\nInput:\n- Lecture notes and study materials related to:\n - Java Spring\n - Docker\n - Cloud and infrastructure concepts\n- Input data may include:\n - title\n - content\n - url (\uc6d0\ubcf8 \uc790\ub8cc \ub9c1\ud06c)\n - source\n - \uae30\ud0c0 \uba54\ud0c0\ub370\uc774\ud130\n\nYour task:\n- Transform the input content into a form optimized for semantic embedding, NOT a short summary.\n- Preserve the full learning structure and conceptual flow.\n- Explicitly clarify:\n - \ud559\uc2b5 \uc8fc\uc81c\uc640 \ubc94\uc704\n - \ub2e8\uacc4\uc801 \ud559\uc2b5 \ud750\ub984 \ub610\ub294 \uc21c\uc11c (\uc788\ub294 \uacbd\uc6b0)\n - \ud575\uc2ec \uae30\uc220, \uac1c\ub150, \ub3c4\uad6c \uac04\uc758 \uad00\uacc4\n- Convert lists or bullet-style content into coherent explanatory sentences.\n- Emphasize curriculum order, learning progression, and conceptual grouping.\n- Include the original URL somewhere in the text so that it can be referenced later.\n\nStrict rules:\n- Do NOT shorten the content excessively.\n- Do NOT merely copy the original text.\n- Do NOT add information that is not present in the input.\n- The output must be suitable for semantic search and RAG retrieval.\n\nLanguage rules:\n- Output must be strictly in Korean.\n- Do not translate technical terms unless absolutely necessary.\n\nOutput rules:\n- Respond strictly in JSON format.\n- The JSON must contain only one field named \"embedding_text\".\n- The value must be a single string.\n\nExample output format:\n\n{\n \"embedding_text\": \"...\"\n}"
},
{
"content": "=content: {{ $('Loop Over Items2').item.json.content }}\nurl: {{ $json.url }}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.1,
"position": [
-448,
928
],
"id": "33eeb17d-7842-4f21-9dbb-32590bec2108",
"name": "Message a model2",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "return [{\n embedding: JSON.parse($input.first().json.output[0].content[0].text).embedding_text\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-96,
928
],
"id": "7756f5eb-e8fe-4e89-b9f8-4763cea982b5",
"name": "Code in JavaScript1"
},
{
"parameters": {
"options": {
"dimensions": 1024
}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
-784,
1776
],
"id": "8b2e6674-860b-44c0-adf8-a67bcacc54b9",
"name": "Embeddings OpenAI2",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineAll",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
448,
64
],
"id": "3339eb91-73a5-40bb-9fbc-ce9004faf784",
"name": "Merge"
},
{
"parameters": {
"options": {
"dimensions": 1024
}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
-496,
1776
],
"id": "ece70a38-b763-431a-ad8a-f1245e4e754c",
"name": "Embeddings OpenAI3",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "You are a retrieval-based assistant.\n\nThe knowledge base is a unified vector store containing embedded documents.\nEach document includes a metadata field called \"type\" that identifies its source:\n\n- type: \"material\" \u2192 official lecture materials and course handouts\n- type: \"blog\" \u2192 study journal blog posts written during learning\n\nThe lecture materials are related to:\n- Java Spring\n- Docker\n- Cloud and infrastructure concepts\n\nEach document includes:\n- Content\n- A title\n- A brief summary\n- A reference (file name, document link, or URL)\n\nWhen a user asks a question, follow these rules strictly:\n\n1. Retrieve the most relevant documents from the vector store using semantic similarity.\n2. Use ONLY the retrieved documents to construct the answer.\n3. Do NOT use prior knowledge, general knowledge, or assumptions.\n4. Separate retrieved documents based on their \"type\" metadata:\n - Documents with type \"material\" must be placed in the \"materials\" array.\n - Documents with type \"blog\" must be placed in the \"blogs\" array.\n\nResponse rules:\n- The response MUST be returned strictly in JSON format.\n- Do NOT include any text outside the JSON response.\n- Do NOT invent or infer any information.\n- Use only the exact metadata provided in the retrieved documents.\n- If no relevant documents exist for a section, return an empty array for that section.\n\nThe JSON response must follow this exact structure:\n\n{\n \"answer\": string,\n \"materials\": [\n {\n \"title\": string,\n \"summary\": string,\n \"reference\": string\n }\n ],\n \"blogs\": [\n {\n \"title\": string,\n \"summary\": string,\n \"link\": string\n }\n ]\n}\n\nAdditional constraints:\n- \"materials\" and \"blogs\" must always be arrays.\n- Include only documents that are directly relevant to the user's question.\n- If no documents are relevant at all, return an empty array for both \"materials\" and \"blogs\", and keep \"answer\" concise and based only on retrieved content (or empty if nothing is retrieved).\n",
"pineconeIndex": {
"__rl": true,
"value": "woorifisa-lectures",
"mode": "list",
"cachedResultName": "woorifisa-lectures"
},
"topK": 5,
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
"typeVersion": 1.3,
"position": [
-864,
1552
],
"id": "65fb00c4-76f8-463a-8937-a337f13ca790",
"name": "lecture store(read)",
"credentials": {
"pineconeApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "\uc218\uc5c5 \uad50\uc548\uc790\ub8cc\n",
"pineconeIndex": {
"__rl": true,
"value": "woorifisa-blogs",
"mode": "list",
"cachedResultName": "woorifisa-blogs"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
"typeVersion": 1.3,
"position": [
-576,
1552
],
"id": "91abc940-462a-4ec7-b728-a40beb929706",
"name": "blog store(read)",
"credentials": {
"pineconeApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "https://docs.google.com/spreadsheets/d/1Unfhi4nDAQIZ3LSyM-iw9ogGviB3QpuWds5CHPY0aGA/edit?gid=0#gid=0",
"mode": "url"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "\uc2dc\ud2b81",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Unfhi4nDAQIZ3LSyM-iw9ogGviB3QpuWds5CHPY0aGA/edit#gid=0"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [
-288,
1552
],
"id": "b57bdb36-0421-4cc5-94b6-4cb69ba73eb5",
"name": "\uc6b0\ub9acFISA \uc77c\uc815",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "edcd64db-8ece-412f-84ee-fdce959b7f39",
"leftValue": "={{ $input.all().isNotEmpty() }}",
"rightValue": {},
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-448,
144
],
"id": "07e7c9ae-7cec-4457-b445-0bcf241fdd3c",
"name": "0\uac1c \uc774\uc0c1"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "edcd64db-8ece-412f-84ee-fdce959b7f39",
"leftValue": "={{ $input.all().isNotEmpty() }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-448,
592
],
"id": "336e36ee-a83d-4343-8a33-94d3fcc25e57",
"name": "0\uac1c \uc774\uc0c11"
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-224,
576
],
"id": "16302ff4-83a7-4fa0-87f2-44f6cef5e929",
"name": "1\uac1c\uc5291"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"responses": {
"values": [
{
"role": "system",
"content": "You are an assistant that transforms educational materials into a format **optimized for semantic embedding**.\n\nInput:\n- Lecture notes, blog learning logs, study materials, etc.\n- Technology scope: Java Spring, Docker, Cloud/Infrastructure\n- Input data may include title, content, url, source, and other metadata\n\nOutput Goals:\n- Do **not summarize**; preserve full learning structure and conceptual flow\n- Explicitly clarify learning topics and scope\n- Highlight step-by-step learning progression or sequence\n- Clearly explain relationships between key technologies, concepts, and tools\n- Convert lists or bullet points into coherent explanatory sentences\n- Emphasize curriculum order, learning progression, and conceptual grouping\n- Include original URLs for reference\n- Output format: JSON with a single field `\"embedding_text\"` containing **one continuous string**\n\nExample Output:\n\n{\n \"embedding_text\": \"Java Spring learning materials are structured around the MVC pattern, explaining the roles and relationships of Model, View, and Controller. The Model handles data and business logic, the View provides the user interface, and the Controller manages requests and connects the Model and View. Additionally, DAO and DTO patterns optimize data access and transfer. The step-by-step learning sequence progresses from basic syntax \u2192 object-oriented programming \u2192 database integration \u2192 Spring MVC structure \u2192 hands-on exercises. URL: https://example.com\"\n}\n\nRules:\n1. Never summarize; maintain the full conceptual and learning flow\n2. Do not add information that is not present in the input\n3. Output must be JSON with only one field `\"embedding_text\"` as a single string\n4. Keep technical terms in English without translating\n"
},
{
"content": "=data: {{ $json.data }}\nurl: \"https://velog.io/@{{ $json.user.username }}/{{ $json.url_slug }}\""
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.1,
"position": [
672,
64
],
"id": "4b98e38c-2d6e-4605-8982-037831ee903c",
"name": "\uc784\ubca0\ub529 \ubb38\uc11c \ucd5c\uc801\ud654",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"responses": {
"values": [
{
"role": "system",
"content": "You are an assistant that transforms educational materials into a format **optimized for semantic embedding**.\n\nInput:\n- Lecture notes, blog learning logs, study materials, etc.\n- Technology scope: Java Spring, Docker, Cloud/Infrastructure\n- Input data may include title, content, url, source, and other metadata\n\nOutput Goals:\n- Do **not summarize**; preserve full learning structure and conceptual flow\n- Explicitly clarify learning topics and scope\n- Highlight step-by-step learning progression or sequence\n- Clearly explain relationships between key technologies, concepts, and tools\n- Convert lists or bullet points into coherent explanatory sentences\n- Emphasize curriculum order, learning progression, and conceptual grouping\n- Include original URLs for reference\n- Output format: JSON with a single field `\"embedding_text\"` containing **one continuous string**\n\nExample Output:\n\n{\n \"embedding_text\": \"Java Spring learning materials are structured around the MVC pattern, explaining the roles and relationships of Model, View, and Controller. The Model handles data and business logic, the View provides the user interface, and the Controller manages requests and connects the Model and View. Additionally, DAO and DTO patterns optimize data access and transfer. The step-by-step learning sequence progresses from basic syntax \u2192 object-oriented programming \u2192 database integration \u2192 Spring MVC structure \u2192 hands-on exercises. URL: https://example.com\"\n}\n\nRules:\n1. Never summarize; maintain the full conceptual and learning flow\n2. Do not add information that is not present in the input\n3. Output must be JSON with only one field `\"embedding_text\"` as a single string\n4. Keep technical terms in English without translating\n"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.1,
"position": [
0,
496
],
"id": "c8fbaccd-6141-440e-ba7b-32184e4bcdda",
"name": "\uc784\ubca0\ub529 \ubb38\uc11c \ucd5c\uc801\ud654 2",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {
"dimensions": 1024
}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
592,
800
],
"id": "24071057-d214-43cb-85b9-536dab6f10df",
"name": "Embeddings OpenAI4",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
720,
800
],
"id": "8ba493b1-412a-44c4-9dab-c715aff08ed1",
"name": "Default Data Loader2"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "2445c0b2-9c27-4c74-89de-165fb7091d95",
"leftValue": "={{ $json.released_at }}",
"rightValue": "={{ $now }}",
"operator": {
"type": "dateTime",
"operation": "beforeOrEquals"
}
},
{
"id": "1ddaec4a-7fa8-401e-bf13-08dec78e2d03",
"leftValue": "={{ $json.released_at }}",
"rightValue": "={{ $now.minus(1, 'year' ) }}",
"operator": {
"type": "dateTime",
"operation": "afterOrEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.3,
"position": [
-672,
144
],
"id": "2288bbe2-fb69-4520-ac1e-7f86aa6c66f9",
"name": "\uc800\ubc88\uc8fc \ub370\uc774\ud130\ub9cc \ud544\ud130(\uc784\uc2dc 1\ub144)"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "edd1ffb1-520a-4c2d-85aa-06bf229a92fe",
"leftValue": "={{ $json.published_date }}",
"rightValue": "={{ $now }}",
"operator": {
"type": "dateTime",
"operation": "beforeOrEquals"
}
},
{
"id": "2e9cb341-a99e-434c-a2b0-6a72fd7bfa9f",
"leftValue": "={{ $json.published_date }}",
"rightValue": "={{ $now.minus(1, 'year') }}",
"operator": {
"type": "dateTime",
"operation": "afterOrEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.3,
"position": [
-672,
592
],
"id": "f39f7d36-f723-4659-8bb6-06b7c51f226c",
"name": "\uc800\ubc88\uc8fc \ub370\uc774\ud130\ub9cc \ud544\ud130(\uc784\uc2dc 1\ub144)2"
},
{
"parameters": {
"fieldToSplitOut": "data.tistory_posts",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
-896,
592
],
"id": "f032d482-7b51-4321-a7d5-3007053ecc89",
"name": "Split Out1"
},
{
"parameters": {
"jsCode": "return [{\n embedding: JSON.parse($input.first().json.output[0].content[0].text).embedding_text\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
352,
496
],
"id": "b0664d1e-e473-455a-924a-dd5e7e913441",
"name": "\uc784\ubca0\ub529 \ucd94\ucd9c"
},
{
"parameters": {
"jsCode": "return [{\n embedding: JSON.parse($input.first().json.output[0].content[0].text).embedding_text\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1024,
64
],
"id": "0c11ab0a-5b41-4309-91fa-72305f44a36d",
"name": "\uc784\ubca0\ub529 \ucd94\ucd9c2"
},
{
"parameters": {
"mode": "insert",
"pineconeIndex": {
"__rl": true,
"value": "woorifisa-blogs",
"mode": "list",
"cachedResultName": "woorifisa-blogs"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
"typeVersion": 1.3,
"position": [
576,
576
],
"id": "3e7bc378-2d8a-436f-ae58-ebacc4b2caa2",
"name": "\ube14\ub85c\uadf8 store(\uc800\uc7a5)",
"credentials": {
"pineconeApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "insert",
"pineconeIndex": {
"__rl": true,
"value": "woorifisa-blogs",
"mode": "list",
"cachedResultName": "woorifisa-blogs"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
"typeVersion": 1.3,
"position": [
1248,
144
],
"id": "173621cf-e910-4bd3-a4b9-eb8db3b4cd67",
"name": "\ube14\ub85c\uadf8 \uc2a4\ud1a0\uc5b4 \uc800\uc7a52",
"credentials": {
"pineconeApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "Crawling",
"operation": "crawl",
"crawlOptions": {},
"requestOptions": {}
},
"type": "@mendable/n8n-nodes-firecrawl.firecrawl",
"typeVersion": 1,
"position": [
-1120,
1024
],
"id": "b37180b3-1ccf-4aab-8103-5ff949418782",
"name": "\ub178\uc158 \ud06c\ub864\ub7ec",
"credentials": {
"firecrawlApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "Agent",
"operation": "agent",
"prompt": "tistory.com \uc0ac\uc774\ud2b8\uc5d0\uc11c '\uc6b0\ub9acFISA \ud074\ub77c\uc6b0\ub4dc \uc5d4\uc9c0\ub2c8\uc5b4\ub9c1' \ud0a4\uc6cc\ub4dc\ub85c \uac80\uc0c9\ub41c \ucd5c\uc2e0 \uac8c\uc2dc\uae00 10\uac1c\ub97c \ub300\uc0c1\uc73c\ub85c \uc81c\ubaa9, \ubcf8\ubb38 \ud14d\uc2a4\ud2b8, \ud0dc\uadf8 \ub9ac\uc2a4\ud2b8, \uadf8\ub9ac\uace0 \ud574\ub2f9 \uac8c\uc2dc\uae00\uc758 \uc6d0\ubb38 \uc8fc\uc18c(URL), \uc791\uc131\uc77c\uc744 \ucd94\ucd9c\ud569\ub2c8\ub2e4. \uac80\uc0c9 \uacb0\uacfc\ub294 \ucd5c\uc2e0\uc21c\uc73c\ub85c \uc815\ub82c\ud558\uba70 \ubcc4\ub3c4\uc758 \uae30\uac04 \uc81c\ud55c\uc740 \ub450\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.",
"schemaType": "manual",
"schema": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"tistory_posts\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"required\": [\n \"title\",\n \"source_url\",\n \"published_date\",\n \"body_text\",\n \"tags\",\n \"image_urls\"\n ],\n \"properties\": {\n \"title\": {\n \"type\": \"string\"\n },\n \"source_url\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"published_date\": {\n \"type\": \"string\",\n \"description\": \"Post publication date as shown on the original Tistory page (e.g. 2024-03-12)\"\n },\n \"body_text\": {\n \"type\": \"string\",\n \"description\": \"Full body text of the post. The publication date should also be included at the top of this text.\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"image_urls\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n }\n }\n },\n \"additionalProperties\": false\n }\n }\n },\n \"required\": [\"tistory_posts\"],\n \"additionalProperties\": false\n}\n",
"maxWaitTime": 600,
"requestOptions": {}
},
"type": "@mendable/n8n-nodes-firecrawl.firecrawl",
"typeVersion": 1,
"position": [
-1120,
592
],
"id": "d096cc15-ef86-41c3-9152-a363fb73777a",
"name": "tistory \ud06c\ub864\ub7ec",
"credentials": {
"firecrawlApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://v2.velog.io/graphql",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "content-type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "query",
"value": "query SearchPosts($keyword: String!, $offset: Int, $username: String) { searchPosts(keyword: $keyword, offset: $offset, username: $username) { count posts { id title short_description thumbnail user { id username profile { id thumbnail display_name __typename } __typename } url_slug released_at tags is_private comments_count __typename } __typename } }"
},
{
"name": "variables",
"value": "{\"keyword\": \"\uc6b0\ub9acfisa \ud074\ub77c\uc6b0\ub4dc \uc5d4\uc9c0\ub2c8\uc5b4\ub9c1\"}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
-1120,
160
],
"id": "0c43726f-e36a-475d-ad82-7e415dcce2e3",
"name": "velog \ud06c\ub864\ub7ec",
"onError": "continueErrorOutput"
},
{
"parameters": {
"jsCode": "// \uc139\uc158\ubcc4 \ubd84\ub9ac\nfunction extract(label, text) {\n const regex = new RegExp(`${label}[\\\\s\\\\S]*?(?=\\\\n\\\\d+\\\\. |$)`, \"g\");\n const match = text.match(regex);\n if (!match) return \"\";\n return match[0].replace(label, \"\").trim();\n}\n\nfor (const item of $input.all()) {\n // \uc139\uc158 \ucd94\ucd9c\n const raw = item.json.output;\n const questionType = extract(\"1. \uc9c8\ubb38 \uc720\ud615\",raw);\n const generalAnswer = extract(\"2. \uc77c\ubc18 \ub2f5\ubcc0\",raw);\n const knownKnowledge = extract(\"3. \uae30\ubcf8 \uc9c0\uc2dd \uc694\uc57d\",raw);\n const category = extract(\"4. \ud559\uc2b5 \uce74\ud14c\uace0\ub9ac\",raw);\n const schedule = extract(\"5. \uc77c\uc815\",raw);\n const lectures = extract(\"6. \uc218\uc5c5 \uc790\ub8cc\",raw);\n const blogs = extract(\"7. \ube14\ub85c\uadf8 \uc790\ub8cc\",raw);\n \n // \ud604\uc7ac \uc2dc\uac04\n const now = new Date().toISOString();\n\n return {\n \uc9c8\ubb38\uc720\ud615: questionType,\n \ud559\uc2b5\uce74\ud14c\uace0\ub9ac: category,\n \uc77c\ubc18\ub2f5\ubcc0: generalAnswer,\n \uae30\ubcf8\uc9c0\uc2dd\uc694\uc57d: knownKnowledge,\n \uc77c\uc815: schedule,\n \uc218\uc5c5\uc790\ub8cc: lectures,\n \ube14\ub85c\uadf8\uc790\ub8cc: blogs,\n \uc0dd\uc131\uc77c\uc2dc: now\n }\n}\n\nreturn $input.all();"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-80,
1536
],
"id": "4ec5b5e2-a4b8-414a-a680-3526e642537a",
"name": "Code in JavaScript"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineAll",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
144,
1712
],
"id": "995d7292-a75f-4d90-b150-674c1b6097d4",
"name": "Merge1"
},
{
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
880,
2768
],
"id": "db9b323a-9521-44e7-88b3-8c51999974e9",
"name": "\uc8fc\uac04 \ud2b8\ub9ac\uac70"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "https://docs.google.com/spreadsheets/d/17aQ8Q8Yk4LvR0--qu2-4lrcxJUaLmUIihLUdZyUdEqA/edit?gid=0#gid=0",
"mode": "url"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "\uc2dc\ud2b81",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/17aQ8Q8Yk4LvR0--qu2-4lrcxJUaLmUIihLUdZyUdEqA/edit#gid=0"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
1104,
2768
],
"id": "3416421f-e8b6-4769-ae51-8b21d336f309",
"name": "\ub300\ud654 \ubaa9\ub85d \uc870\ud68c",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sendTo": "azs8017@naver.com",
"subject": "report",
"message": "3\uac00\uc9c0 \uadf8\ub798\ud504 \ud615\uc2dd",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
2624,
2864
],
"id": "7eab567c-e57a-4398-9125-bd1f1726f989",
"name": "\uba54\uc77c\uc804\uc1a1",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nconst learningCategories = new Map();\nconst questionCategories = new Map();\nconst dates = new Map();\nfor (const item of $input.all()) {\n const prevVal1= (learningCategories.get(item.json.\ud559\uc2b5\uce74\ud14c\uace0\ub9ac) || 0) + 1;\n const prevVal2 = (questionCategories.get(item.json.\uc9c8\ubb38\uc720\ud615) || 0) + 1;\n const date = new Date(item.json.\uc0dd\uc131\uc77c).toLocaleDateString('ko-KR');\n const prevVal3 = (dates.get(date) || 0) + 1;\n learningCategories.set(item.json.\ud559\uc2b5\uce74\ud14c\uace0\ub9ac, prevVal1);\n questionCategories.set(item.json.\uc9c8\ubb38\uc720\ud615, prevVal2);\n dates.set(date, prevVal3)\n}\n\nreturn {\n questions: [...questionCategories].map(([k,v]) => ({\n [k]:v\n })),\n learnings: [...learningCategories].map(([k,v]) => ({\n [k]: v\n })),\n dates: [...dates].map(([k,v]) => ({\n [k]: v\n })),\n data: $input.all()\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
2768
],
"id": "d12cd9da-a4ea-42f9-8b02-4219a19bdc9b",
"name": "Code in JavaScript2"
},
{
"parameters": {
"chartType": "pie",
"labelsMode": "array",
"labelsArray": "={{ $json.questions.flatMap(o => Object.keys(o)) }}",
"data": "={{ $json.questions.flatMap(o => Object.values(o)) }}",
"chartOptions": {},
"datasetOptions": {}
},
"type": "n8n-nodes-base.quickChart",
"typeVersion": 1,
"position": [
1584,
2688
],
"id": "63f2b46b-3c7a-4c02-87be-dec4614ce3c2",
"name": "\uc9c8\ubb38 \ubd84\ub958 \ud30c\uc774\uadf8\ub798\ud504"
},
{
"parameters": {
"labelsMode": "array",
"labelsArray": "={{ $json.learnings.flatMap(o => Object.keys(o)) }}",
"data": "={{ $json.learnings.flatMap(o => Object.values(o)) }}",
"chartOptions": {},
"datasetOptions": {}
},
"type": "n8n-nodes-base.quickChart",
"typeVersion": 1,
"position": [
1584,
2912
],
"id": "89b03cb2-0d17-4313-9744-c7cbf4e495be",
"name": "\ud559\uc2b5 \uce74\ud14c\uace0\ub9ac \ub9c9\ub300\uadf8\ub798\ud504"
},
{
"parameters": {
"chartType": "line",
"labelsMode": "array",
"labelsArray": "={{ $json.dates.flatMap(o => Object.keys(o)) }}",
"data": "={{ $json.dates.flatMap(o => Object.values(o)) }}",
"chartOptions": {},
"datasetOptions": {}
},
"type": "n8n-nodes-base.quickChart",
"typeVersion": 1,
"position": [
1584,
3136
],
"id": "61c2ef3a-58d6-4ef1-be4b-7d501dff71c4",
"name": "\uc77c\uc790\ubcc4 \uc9c8\ubb38 \uc218 \ub77c\uc778 \uadf8\ub798\ud504"
},
{
"parameters": {
"numberInputs": 3
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
1952,
2880
],
"id": "26c1d430-e447-4ea2-8775-89ff66e47d83",
"name": "Merge2"
},
{
"parameters": {
"folderId": "1v3XwiOE6KIj7BIQHYWX-4R2GoLlH9vNn",
"title": "\uc8fc\uac04 \ubcf4\uace0\uc11c"
},
"type": "n8n-nodes-base.googleDocs",
"typeVersion": 2,
"position": [
2144,
2896
],
"id": "1f578586-6092-4c1e-9632-b6935d7eff35",
"name": "\ubcf4\uace0\uc11c \uc0dd\uc131",
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "update",
"documentURL": "={{$node[\"\ubcf4\uace0\uc11c \uc0dd\uc131\"].json.id}}",
"actionsUi": {
"actionFields": [
{
"action": "insert",
"text": "=={{ $json.content }}"
}
]
}
},
"type": "n8n-nodes-base.googleDocs",
"typeVersion": 2,
"position": [
2704,
3040
],
"id": "4b3d933d-bb02-4f9d-ab4c-6e9341a94ed7",
"name": "\ubcf4\uace0\uc11c \uc218\uc815",
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
2352,
2896
],
"id": "3ef3bab6-8e01-4f82-982f-bd7e2062e5c2",
"name": "Loop Over Items"
},
{
"parameters": {
"name": "={{$binary.data.fileName || ('chart_' + $itemIndex + '.png')}}",
"driveId": {
"__rl": true,
"value": "My Drive",
"mode": "list",
"cachedResultName": "My Drive",
"cachedResultUrl": "https://drive.google.com/drive/my-drive"
},
"folderId": {
"__rl": true,
"value": "1v3XwiOE6KIj7BIQHYWX-4R2GoLlH9vNn",
"mode": "list",
"cachedResultName": "n8n drive",
"cachedResultUrl": "https://drive.google.com/drive/folders/1v3XwiOE6KIj7BIQHYWX-4R2GoLlH9vNn"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
2528,
3040
],
"id": "657c9aa6-b606-4729-b782-9a2f11217dc2",
"name": "Upload file",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "C0A9FP50H6U",
"mode": "id"
},
"text": "\ud14c\uc2a4\ud2b8 \uba54\uc2dc\uc9c0\uc785\ub2c8\ub2e4",
"otherOptions": {}
},
"type": "n8n-nodes-base.slack",
"typeVersion": 2.4,
"position": [
-1072,
2800
],
"id": "254053bf-ad75-49f7-901a-c392ca92b4f1",
"name": "Send a message",
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-1344,
2192
],
"id": "0ea279c9-9868-4193-b9df-95002aa0297e",
"name": "\uc8fc\uac04 \ud2b8\ub9ac\uac701"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "https://docs.google.com/spreadsheets/d/17aQ8Q8Yk4LvR0--qu2-4lrcxJUaLmUIihLUdZyUdEqA/edit?gid=0#gid=0",
"mode": "url"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "\uc2dc\ud2b81",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/17aQ8Q8Yk4LvR0--qu2-4lrcxJUaLmUIihLUdZyUdEqA/edit#gid=0"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
-1168,
2192
],
"id": "1d5de284-b0e7-4a1a-b8f5-2b3e40857784",
"name": "\ub300\ud654 \ubaa9\ub85d \uc870\ud68c1",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sendTo": "azs8017@naver.com",
"subject": "=\uc8fc\uac04 \uc9c8\ubb38 \ub9ac\ud3ec\ud2b8 ({{$now}})",
"emailType": "text",
"message": "=\uc8fc\uac04 \uc9c8\ubb38 \ub9ac\ud3ec\ud2b8 - \ucd1d \uc9c8\ubb38 \uc218: {{$json.summary.totalRows}}\n- Top \ud559\uc2b5\uce74\ud14c\uace0\ub9ac: {{$json.summary.topLearning}}\n- Top \uc9c8\ubb38\uc720\ud615: {{$json.summary.topQuestionType}}\n\n(\ucca8\ubd80) \ud559\uc2b5\uce74\ud14c\uace0\ub9ac/\uc9c8\ubb38\uc720\ud615/\uc77c\uc790\ubcc4 \ucc28\ud2b8 3\uc885\n",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{
"property": "learning.png"
},
{
"property": "question.png"
},
{
"property": "date.png"
}
]
}
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
192,
2160
],
"id": "e00b1e59-6a06-4031-92e2-6e1f74160958",
"name": "\uba54\uc77c\uc804\uc1a11",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// n8n Code (JavaScript) - \uc804\uccb4 \ucf54\ub4dc (QuickChart GET URL \ubc29\uc2dd)\n// \uc785\ub825: Google Sheets\uc5d0\uc11c \uc5ec\ub7ec row(item)\ub4e4\uc774 \ub4e4\uc5b4\uc628\ub2e4\uace0 \uac00\uc815\n// \ucd9c\ub825: 1\uac1c item\ub9cc \ubc18\ud658 (\uc9d1\uacc4 + \ucc28\ud2b8 config + quickchart URL 3\uac1c)\n\nconst learningCategories = new Map();\nconst questionCategories = new Map();\nconst dates = new Map();\n\nconst rows = $input.all();\n\n// 1) \uc9d1\uacc4\nfor (const item of rows) {\n const learning = item.json.\ud559\uc2b5\uce74\ud14c\uace0\ub9ac ?? '\uae30\ud0c0';\n const qtype = item.json.\uc9c8\ubb38\uc720\ud615 ?? '\uae30\ud0c0';\n\n // \uc0dd\uc131\uc77c \ud30c\uc2f1 (\uc548\uc804\ud558\uac8c)\n // ISO \ub610\ub294 \"2026-01-12T...\" \ud615\ud0dc\uba74 \uc798 \ud30c\uc2f1\ub428\n // \ud30c\uc2f1 \uc2e4\ud328\ud558\uba74 unknown\uc73c\ub85c \ubb36\uc74c\n const raw = item.json.\uc0dd\uc131\uc77c;\n const d = raw ? new Date(raw) : null;\n const dateKey = d && !isNaN(d) ? d.toISOString().slice(0, 10) : 'unknown'; // YYYY-MM-DD\n\n learningCategories.set(learning, (learningCategories.get(learning) || 0) + 1);\n questionCategories.set(qtype, (questionCategories.get(qtype) || 0) + 1);\n dates.set(dateKey, (dates.get(dateKey) || 0) + 1);\n}\n\n// Map -> {labels, values}\nfunction toSeries(map, sortByValueDesc = true) {\n const arr = [...map.entries()];\n if (sortByValueDesc) arr.sort((a, b) => b[1] - a[1]);\n return {\n labels: arr.map(([k]) => k),\n values: arr.map(([, v]) => v),\n };\n}\n\nconst learningSeries = toSeries(learningCategories, true);\nconst questionSeries = toSeries(questionCategories, true);\n\n// \ub0a0\uc9dc\ub294 \uc624\ub984\ucc28\uc21c \uc815\ub82c(\uc911\uc694)\nconst dateArr = [...dates.entries()].sort((a, b) => a[0].localeCompare(b[0]));\nconst dateSeries = {\n labels: dateArr.map(([k]) => k),\n values: dateArr.map(([, v]) => v),\n};\n\n// 2) \ucc28\ud2b8 \uc124\uc815(Chart.js config)\nconst chartLearning = {\n type: 'bar',\n data: {\n labels: learningSeries.labels,\n datasets: [{ label: '\ud559\uc2b5\uce74\ud14c\uace0\ub9ac \uc9c8\ubb38 \uc218', data: learningSeries.values }],\n },\n options: {\n plugins: {\n legend: { display: false },\n title: { display: true, text: '\ud559\uc2b5\uce74\ud14c\uace0\ub9ac\ubcc4 \uc9c8\ubb38 \uc218' },\n },\n scales: { y: { beginAtZero: true } },\n },\n};\n\nconst chartQuestion = {\n type: 'pie',\n data: {\n labels: questionSeries.labels,\n datasets: [{ label: '\uc9c8\ubb38\uc720\ud615', data: questionSeries.values }],\n },\n options: {\n plugins: { title: { display: true, text: '\uc9c8\ubb38\uc720\ud615 \ubd84\ud3ec' } },\n },\n};\n\nconst chartDate = {\n type: 'line',\n data: {\n labels: dateSeries.labels,\n datasets: [{ label: '\uc77c\uc790\ubcc4 \uc9c8\ubb38 \uc218', data: dateSeries.values, fill: false, tension: 0.2 }],\n },\n options: {\n plugins: { title: { display: true, text: '\uc77c\uc790\ubcc4 \uc9c8\ubb38 \uc218 \ucd94\uc774' } },\n scales: { y: { beginAtZero: true } },\n },\n};\n\n// 3) QuickChart GET URL \ub9cc\ub4e4\uae30 (Body \uc5c6\uc774 GET\uc73c\ub85c PNG \ubc1b\uae30)\nfunction makeQuickChartUrl(chartObj) {\n const c = encodeURIComponent(JSON.stringify(chartObj));\n return `https://quickchart.io/chart?format=png&width=900&height=500&backgroundColor=white&c=${c}`;\n}\n\nconst urlLearning = makeQuickChartUrl(chartLearning);\nconst urlQuestion = makeQuickChartUrl(chartQuestion);\nconst urlDate = makeQuickChartUrl(chartDate);\n\n// 4) \uc694\uc57d \uc815\ubcf4\nconst total = rows.length;\n\nreturn [\n {\n json: {\n summary: {\n totalRows: total,\n topLearning: learningSeries.labels[0] ?? null,\n topQuestionType: questionSeries.labels[0] ?? null,\n },\n series: {\n learning: learningSeries,\n question: questionSeries,\n date: dateSeries,\n },\n // \ucc28\ud2b8 config(\uc6d0\ud558\uba74 \ub0a8\uaca8\ub450\uace0, \ud544\uc694 \uc5c6\uc73c\uba74 \uc9c0\uc6cc\ub3c4 \ub428)\n charts: {\n learning: chartLearning,\n question: chartQuestion,\n date: chartDate,\n },\n // \u2705 HTTP Request\uc5d0\uc11c \uc4f8 URL\ub4e4\n quickchart: {\n learning: urlLearning,\n question: urlQuestion,\n date: urlDate,\n },\n },\n },\n];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-992,
2192
],
"id": "3b7dc9a0-98c2-4d3d-a65b-28c30fa34051",
"name": "Code in JavaScript3"
},
{
"parameters": {
"url": "={{$json.quickchart.learning}}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "learning.png"
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
-640,
1984
],
"id": "beb8589a-8abd-4cae-8ff8-18572b39c43a",
"name": "HTTP Request"
},
{
"parameters": {
"url": "={{$json.quickchart.question}}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "question.png"
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
-656,
2160
],
"id": "806faf8d-8145-4a34-80e6-07fcc3685175",
"name": "HTTP Request1"
},
{
"parameters": {
"url": "={{$json.quickchart.date}}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "date.png"
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
-656,
2352
],
"id": "b7d9e6a0-a920-49a7-b447-9c3c75318053",
"name": "HTTP Request2"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"numberInputs": 3,
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
-272,
2144
],
"id": "7f17815e-9ddb-40ff-a292-28d13128e866",
"name": "Merge4"
},
{
"parameters": {
"jsCode": "const item = $input.first();\n\nif (!item.binary) return [item];\n\nconst keys = [\"learning.png\", \"question.png\", \"date.png\"];\n\nfor (const key of keys) {\n const b = item.binary[key];\n if (!b) continue;\n\n // \uba54\uc77c \ucca8\ubd80 \ud30c\uc77c\uba85\uc774 \uc774 \uac12\uc744 \ub530\ub77c\uac00\ubbc0\ub85c \uc5ec\uae30\ub9cc \uc81c\ub300\ub85c \uc7a1\uc73c\uba74 \ud574\uacb0\ub428\n b.fileName = key;\n\n // \uc548\uc804\ube75(\uc774\ubbf8 \ub9de\uc9c0\ub9cc \uc720\uc9c0)\n b.fileExtension = \"png\";\n b.mimeType = \"image/png\";\n}\n\nreturn [item];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-16,
2160
],
"id": "7faf66ac-1fbd-4ca6-828b-6eaedcf97909",
"name": "Code in JavaScript4"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "17aQ8Q8Yk4LvR0--qu2-4lrcxJUaLmUIihLUdZyUdEqA",
"mode": "list",
"cachedResultName": "\ub0b4\uc6a9 \uc800\uc7a5",
"cachedResultUrl": "https
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
firecrawlApigmailOAuth2googleDocsOAuth2ApigoogleDriveOAuth2ApigoogleSheetsOAuth2ApimongoDbopenAiApipineconeApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
This workflow automates the extraction and organisation of educational content from online sources, delivering structured lesson plans directly into Notion for seamless classroom preparation. It benefits teachers and educators who spend hours manually gathering resources, saving them valuable time by handling daily and weekly crawls of relevant websites. The core step involves an AI agent analysing scraped data via HTTP requests, deciding whether to load and format it using the document loader before storing insights in MongoDB chat memory and Pinecone vector store for quick retrieval.
Use this workflow when you need consistent, automated updates to teaching materials from specific Korean educational sites, such as WooriFisa, without constant oversight. Avoid it for one-off data pulls or highly dynamic content requiring real-time human input, as the scheduled triggers suit periodic rather than instant needs. Common variations include adjusting cron schedules for monthly reports or integrating additional vector stores for broader content searching.
About this workflow
WooriFisa 최종. Uses memoryMongoDbChat, agent, httpRequest, documentDefaultDataLoader. Scheduled trigger; 68 nodes.
Source: https://github.com/woorifisa-6th-n8n-chatbot-team/woorifisa-n8n-project/blob/117743d5ec86aa20d47ee91f806659d04904358a/flows/WooriFisa_hilees.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Supercharge your trading decisions with this end-to-end AI automation that connects market intelligence, technical analysis, and automated trade execution — all without manual intervention.
WooriFisa. Uses agent, httpRequest, documentDefaultDataLoader, vectorStorePinecone. Scheduled trigger; 86 nodes.
This workflow automates patient communication for medical clinics using the WhatsApp Business API. It supports appointment booking, rescheduling, service inquiries, follow-ups, and document submission
Tech Radar. Uses googleDrive, documentDefaultDataLoader, stickyNote, mySql. Scheduled trigger; 53 nodes.
This project is built on top of the famous open source ThoughtWorks Tech Radar.