257 lines
7.5 KiB
JSON
257 lines
7.5 KiB
JSON
{
|
|
"name": "Portfolio Bryan - AI Chatbot",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "portfolio-chat",
|
|
"responseMode": "responseNode",
|
|
"options": {
|
|
"allowedOrigins": "*"
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
220,
|
|
340
|
|
],
|
|
"id": "webhook-trigger",
|
|
"name": "Webhook - Incoming Chat"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Sanitización y validación de entrada para proteger cuota de API y prevenir abusos\nconst body = $input.item.json.body || {};\nconst rawMessage = typeof body.message === 'string' ? body.message.trim() : '';\nconst sessionId = typeof body.sessionId === 'string' && body.sessionId ? body.sessionId.slice(0, 64) : 'anon-' + Date.now();\n\nif (!rawMessage) {\n return [{\n json: {\n isValid: false,\n errorMessage: 'El mensaje no puede estar vacío.',\n sessionId\n }\n }];\n}\n\n// Limitar a máximo 400 caracteres para cuidar tokens gratuitos\nconst cleanMessage = rawMessage.slice(0, 400).replace(/<[^>]*>?/gm, '');\n\nreturn [{\n json: {\n isValid: true,\n cleanMessage,\n sessionId\n }\n}];"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
460,
|
|
340
|
|
],
|
|
"id": "code-sanitizer",
|
|
"name": "Sanitize & Validate"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict"
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "cond-is-valid",
|
|
"leftValue": "={{ $json.isValid }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
680,
|
|
340
|
|
],
|
|
"id": "if-valid-input",
|
|
"name": "Is Input Valid?"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"promptType": "define",
|
|
"text": "={{ $('Sanitize & Validate').item.json.cleanMessage }}",
|
|
"options": {
|
|
"systemMessage": "=Eres el asistente virtual oficial de Bryan Baño en su portafolio web personal.\n\n### CONTEXTO DE BRYAN BAÑO:\n- Ingeniero en Computación (EPN, Quito - Ecuador).\n- Especialidad: .NET 8/9 Clean Architecture (CQRS MediatR, EF Core MySQL) y Angular Signals (OnPush, Canvas 2D).\n- Filosofía de Software: Ponytail (Simplicidad extrema, YAGNI, cero sobreingeniería, Web Platform nativa, 60 FPS).\n- Proyectos Destacados: Auditoría CACES (.NET), Incident Simulator Cloud Security (Serverless), Portafolio Interactivo Cyber-Zen.\n- Certificaciones: EC-Council Network Defense Essentials (NDE), Cisco Networking Academy.\n- Ubicación: Quito, Ecuador. Disponible para proyectos y contrataciones remotas globales.\n\n### REGLAS DE RESPUESTA:\n1. Responde siempre en el MISMO idioma del usuario (Español o Inglés).\n2. Sé técnico, conciso, profesional y cálido (máximo 2 a 3 párrafos o viñetas breves).\n3. No inventes experiencia que no esté en este contexto.\n4. Si el visitante desea contactar a Bryan o contratarlo, indícale amablemente que use el formulario de contacto del portafolio o su perfil de LinkedIn."
|
|
}
|
|
},
|
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
|
"typeVersion": 1.7,
|
|
"position": [
|
|
920,
|
|
240
|
|
],
|
|
"id": "ai-agent-bryan",
|
|
"name": "AI Agent - Bryan Assistant"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"modelName": "models/gemini-2.0-flash",
|
|
"options": {
|
|
"temperature": 0.4
|
|
}
|
|
},
|
|
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
880,
|
|
480
|
|
],
|
|
"id": "gemini-model",
|
|
"name": "Google Gemini Chat Model",
|
|
"credentials": {
|
|
"googleGeminiApi": {
|
|
"id": "gemini-free-cred",
|
|
"name": "Google Gemini Free API"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sessionKey": "={{ $('Sanitize & Validate').item.json.sessionId }}",
|
|
"contextWindowLength": 8
|
|
},
|
|
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
|
|
"typeVersion": 1.3,
|
|
"position": [
|
|
1040,
|
|
480
|
|
],
|
|
"id": "window-memory",
|
|
"name": "Window Buffer Memory"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={\n \"reply\": {{ JSON.stringify($json.output) }},\n \"sessionId\": {{ JSON.stringify($('Sanitize & Validate').item.json.sessionId) }}\n}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Access-Control-Allow-Origin",
|
|
"value": "*"
|
|
},
|
|
{
|
|
"name": "Access-Control-Allow-Methods",
|
|
"value": "POST, OPTIONS"
|
|
},
|
|
{
|
|
"name": "Access-Control-Allow-Headers",
|
|
"value": "Content-Type"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.1,
|
|
"position": [
|
|
1220,
|
|
240
|
|
],
|
|
"id": "respond-success",
|
|
"name": "Respond - Success"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={\n \"reply\": {{ JSON.stringify($json.errorMessage || 'Mensaje no válido.') }},\n \"sessionId\": {{ JSON.stringify($json.sessionId) }}\n}",
|
|
"options": {
|
|
"responseCode": 400,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Access-Control-Allow-Origin",
|
|
"value": "*"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.1,
|
|
"position": [
|
|
1220,
|
|
420
|
|
],
|
|
"id": "respond-error",
|
|
"name": "Respond - Error"
|
|
}
|
|
],
|
|
"connections": {
|
|
"Webhook - Incoming Chat": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Sanitize & Validate",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Sanitize & Validate": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Is Input Valid?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Is Input Valid?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "AI Agent - Bryan Assistant",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Respond - Error",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"AI Agent - Bryan Assistant": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Respond - Success",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Google Gemini Chat Model": {
|
|
"ai_languageModel": [
|
|
[
|
|
{
|
|
"node": "AI Agent - Bryan Assistant",
|
|
"type": "ai_languageModel",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Window Buffer Memory": {
|
|
"ai_memory": [
|
|
[
|
|
{
|
|
"node": "AI Agent - Bryan Assistant",
|
|
"type": "ai_memory",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1"
|
|
}
|
|
}
|