{ "name": "65-03 AI request flow", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "ai-request", "responseMode": "responseNode", "options": {} }, "id": "07b51032-62c2-549b-8cfb-e3f16a851ed7", "name": "AI request webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [ 0, 0 ], "webhookId": "26aa9aad-522f-5f3d-8b58-ae0d7c18ce66" }, { "parameters": { "assignments": { "assignments": [ { "id": "cf988cf6-5aec-59ef-887c-93252d7d366b", "name": "llm_url", "value": "https://api.openai.com/v1", "type": "string" }, { "id": "6d229077-7077-5e85-8ebb-8f6046227539", "name": "model", "value": "gpt-4.1-mini", "type": "string" }, { "id": "8a827ce4-4716-5aad-ad39-9c105afcf895", "name": "user", "value": "={{ $json.body.user || 'anonymous' }}", "type": "string" }, { "id": "462379ca-3e96-599d-9a1c-64bae0b1095a", "name": "text", "value": "={{ $json.body.text }}", "type": "string" } ] }, "includeOtherFields": false, "options": {} }, "id": "6c57d7cf-acd9-5007-b34a-b741afa2c060", "name": "Settings", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 220, 0 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose", "version": 2 }, "conditions": [ { "id": "f2c175d2-9545-570a-99a6-e7cdfb17162f", "leftValue": "={{ $json.text }}", "operator": { "type": "string", "operation": "notEmpty", "singleValue": true }, "rightValue": "" } ], "combinator": "and" }, "options": {} }, "id": "d19bdfca-d55e-5760-a488-458abc7a5480", "name": "Has text?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 440, 0 ] }, { "parameters": { "respondWith": "json", "responseBody": "={{ { \"error\": \"text is required\" } }}", "options": { "responseCode": 400 } }, "id": "5adfc553-1a2c-55fd-a4bd-d6564197222a", "name": "Reject empty", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [ 680, 180 ] }, { "parameters": { "method": "POST", "url": "={{ $('Settings').first().json.llm_url }}/chat/completions", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ model: $('Settings').first().json.model, temperature: 0, messages: [ { role: 'system', content: \"Classify the IT request into exactly one of: account, network, device, software, other. Reply with the label only.\" }, { role: 'user', content: $('Settings').first().json.text } ] }) }}", "options": { "timeout": 180000 }, "authentication": "predefinedCredentialType", "nodeCredentialType": "openAiApi" }, "id": "d084b606-aad9-5341-98cd-490070f907ac", "name": "LLM classify", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 680, -40 ], "credentials": { "openAiApi": { "id": "openAiCourse0001", "name": "OpenAI (.env)" } } }, { "parameters": { "jsCode": "// 1) Clean up the LLM label 2) Search the FAQ list\nconst allowed = ['account', 'network', 'device', 'software', 'other'];\nconst raw = String($input.first().json.choices?.[0]?.message?.content ?? '').trim().toLowerCase();\nconst category = allowed.find(a => raw.includes(a)) ?? 'other';\n\nconst faqs = [\n {\n \"id\": \"FAQ-001\",\n \"title\": \"비밀번호 재설정\",\n \"keywords\": [\n \"비밀번호\",\n \"패스워드\",\n \"password\",\n \"재설정\",\n \"초기화\"\n ],\n \"answer\": \"사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다.\"\n },\n {\n \"id\": \"FAQ-002\",\n \"title\": \"계정 잠김 해제\",\n \"keywords\": [\n \"계정\",\n \"잠김\",\n \"잠금\",\n \"로그인\",\n \"실패\"\n ],\n \"answer\": \"로그인 5회 실패 시 30분 동안 잠깁니다. 30분 후 다시 시도하거나 헬프데스크에 잠금 해제를 요청합니다.\"\n },\n {\n \"id\": \"FAQ-003\",\n \"title\": \"VPN 접속 오류\",\n \"keywords\": [\n \"vpn\",\n \"원격\",\n \"재택\",\n \"접속\",\n \"연결\"\n ],\n \"answer\": \"VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다.\"\n },\n {\n \"id\": \"FAQ-004\",\n \"title\": \"프린터 출력 안 됨\",\n \"keywords\": [\n \"프린터\",\n \"출력\",\n \"인쇄\",\n \"용지\"\n ],\n \"answer\": \"프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다.\"\n },\n {\n \"id\": \"FAQ-005\",\n \"title\": \"메일함 용량 초과\",\n \"keywords\": [\n \"메일\",\n \"용량\",\n \"메일함\",\n \"첨부\"\n ],\n \"answer\": \"오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다.\"\n },\n {\n \"id\": \"FAQ-006\",\n \"title\": \"소프트웨어 설치 요청\",\n \"keywords\": [\n \"설치\",\n \"소프트웨어\",\n \"프로그램\",\n \"라이선스\"\n ],\n \"answer\": \"IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다.\"\n }\n];\nconst settings = $('Settings').first().json;\nconst words = String(settings.text).toLowerCase().split(/\\s+/).filter(w => w.length > 1);\nconst scored = faqs\n .map(f => ({ ...f, score: words.filter(w => f.keywords.some(k => w.includes(k) || k.includes(w))).length }))\n .filter(f => f.score > 0)\n .sort((a, b) => b.score - a.score);\nconst faq = scored[0] ?? null;\n\nreturn [{ json: { user: settings.user, text: settings.text, raw_label: raw, category, found: faq !== null, faq } }];" }, "id": "aa4be834-45fb-563b-95f5-c6a0dbeb2903", "name": "Normalize and search FAQ", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 920, -40 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose", "version": 2 }, "conditions": [ { "id": "6a4b578f-d9da-5899-933d-7e05d901e3eb", "leftValue": "={{ $json.found }}", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "rightValue": "" } ], "combinator": "and" }, "options": {} }, "id": "21f335b6-8a0a-5331-b8df-43d3c9f9aafc", "name": "FAQ found?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 1160, -40 ] }, { "parameters": { "method": "POST", "url": "={{ $('Settings').first().json.llm_url }}/chat/completions", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ model: $('Settings').first().json.model, temperature: 0, messages: [ { role: 'system', content: \"Write a polite 2-sentence Korean reply using only the FAQ answer. End with the FAQ id in parentheses.\" }, { role: 'user', content: '문의: ' + $('Normalize and search FAQ').first().json.text + '\\nFAQ: ' + JSON.stringify($('Normalize and search FAQ').first().json.faq) } ] }) }}", "options": { "timeout": 180000 }, "authentication": "predefinedCredentialType", "nodeCredentialType": "openAiApi" }, "id": "a1e86429-1ee7-585c-bdaa-f1aa0d10f49b", "name": "LLM draft reply", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1400, -140 ], "credentials": { "openAiApi": { "id": "openAiCourse0001", "name": "OpenAI (.env)" } } }, { "parameters": { "assignments": { "assignments": [ { "id": "1e3762b6-7dae-5e2f-9538-8a5b0f5dcae0", "name": "category", "value": "={{ $('Normalize and search FAQ').first().json.category }}", "type": "string" }, { "id": "230f5390-e5c9-5e6a-90ec-03e6965652f7", "name": "faq_id", "value": "={{ $('Normalize and search FAQ').first().json.faq.id }}", "type": "string" }, { "id": "1f78eb57-6478-5590-94f9-bacb9b2c89b9", "name": "route", "value": "자동 답변", "type": "string" }, { "id": "756cec4c-d91f-516a-9d9f-ae74098227e9", "name": "reply", "value": "={{ $json.choices[0].message.content.trim() }}", "type": "string" } ] }, "includeOtherFields": false, "options": {} }, "id": "da512682-1045-5a8e-83d1-52975eb08e97", "name": "Auto reply", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 1640, -140 ] }, { "parameters": { "assignments": { "assignments": [ { "id": "bf4ced71-6e32-5ed2-ad7d-4124d462e3d2", "name": "category", "value": "={{ $json.category }}", "type": "string" }, { "id": "79bffc37-152f-5b3e-9018-e30121faed11", "name": "faq_id", "value": "", "type": "string" }, { "id": "86fde04e-b2a1-5782-97c1-caba13a9ed10", "name": "route", "value": "담당자 확인", "type": "string" }, { "id": "d760805f-73f1-5d0a-a7cf-8eb67d03e978", "name": "reply", "value": "관련 안내를 찾지 못해 담당자가 확인 후 답변드리겠습니다.", "type": "string" } ] }, "includeOtherFields": false, "options": {} }, "id": "604eb70d-bff2-5270-b1a4-f94a3a6c681d", "name": "Hand off to staff", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 1400, 80 ] }, { "parameters": { "respondWith": "json", "responseBody": "={{ $json }}", "options": {} }, "id": "2de33965-f060-5f8b-bd91-2a7c72a1e618", "name": "Send result", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [ 1880, -40 ] } ], "connections": { "AI request webhook": { "main": [ [ { "node": "Settings", "type": "main", "index": 0 } ] ] }, "Settings": { "main": [ [ { "node": "Has text?", "type": "main", "index": 0 } ] ] }, "Has text?": { "main": [ [ { "node": "LLM classify", "type": "main", "index": 0 } ], [ { "node": "Reject empty", "type": "main", "index": 0 } ] ] }, "LLM classify": { "main": [ [ { "node": "Normalize and search FAQ", "type": "main", "index": 0 } ] ] }, "Normalize and search FAQ": { "main": [ [ { "node": "FAQ found?", "type": "main", "index": 0 } ] ] }, "FAQ found?": { "main": [ [ { "node": "LLM draft reply", "type": "main", "index": 0 } ], [ { "node": "Hand off to staff", "type": "main", "index": 0 } ] ] }, "LLM draft reply": { "main": [ [ { "node": "Auto reply", "type": "main", "index": 0 } ] ] }, "Auto reply": { "main": [ [ { "node": "Send result", "type": "main", "index": 0 } ] ] }, "Hand off to staff": { "main": [ [ { "node": "Send result", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1" }, "pinData": {}, "id": "493c961026875d" }