Flujo de trabajo de prevención de riesgo de abandono (Azure OpenAI + n8n)
Este es unContent Creation, Multimodal AIflujo de automatización del dominio deautomatización que contiene 16 nodos.Utiliza principalmente nodos como If, Code, Gmail, GoogleDrive, Agent. Detección de riesgo de abandono del personal y recordatorios de RRHH, usando Azure OpenAI GPT-4o-mini y Gmail
- •Cuenta de Google y credenciales de API de Gmail
- •Credenciales de API de Google Drive
- •Clave de API de OpenAI
Nodos utilizados (16)
Categoría
{
"id": "ONqZuWnwkE64Mm4F",
"meta": {
"instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
"templateCredsSetupCompleted": true
},
"name": "Attrition Risk Alert Workflow (Azure OpenAI + n8n)",
"tags": [],
"nodes": [
{
"id": "c48bef67-a0d0-4a4b-a374-3df1a7ed4c19",
"name": "Azure Modelo de chat OpenAI",
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"position": [
688,
128
],
"parameters": {
"model": "gpt-4o-mini",
"options": {}
},
"credentials": {
"azureOpenAiApi": {
"id": "C3WzT18XqF8OdVM6",
"name": "Azure Open AI account"
}
},
"typeVersion": 1
},
{
"id": "a44062d6-1e37-443b-b146-cf32a9ac74cc",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
816,
128
],
"parameters": {
"jsonSchemaExample": "{\n\t\"average\": \"18\"\n}"
},
"typeVersion": 1.3
},
{
"id": "2ab2bd76-1809-4015-9d99-a278561287da",
"name": "Trigger for new resume",
"type": "n8n-nodes-base.googleDriveTrigger",
"position": [
0,
-96
],
"parameters": {
"event": "fileCreated",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"mode": "list",
"value": "1KyX5RGqeF7v0sAvvaoBnJPTQoq4aOHLz",
"cachedResultUrl": "https://drive.google.com/drive/folders/1KyX5RGqeF7v0sAvvaoBnJPTQoq4aOHLz",
"cachedResultName": "HR auto"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "gPmEPTuQP4KLm1KD",
"name": "jyothi"
}
},
"typeVersion": 1
},
{
"id": "dabfc101-cfa3-43e6-b568-320c1c2d3e92",
"name": "Download resume",
"type": "n8n-nodes-base.googleDrive",
"position": [
224,
-96
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "url",
"value": "={{ $json.webViewLink }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "gPmEPTuQP4KLm1KD",
"name": "jyothi"
}
},
"typeVersion": 3
},
{
"id": "821c7e31-be41-4e6c-b58b-19a2cf880648",
"name": "Extract text",
"type": "n8n-nodes-base.extractFromFile",
"position": [
448,
-96
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "78f8966f-dbeb-4773-b198-9dfb13defd35",
"name": "Calculate avg span",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
672,
-96
],
"parameters": {
"text": "={{ $json.text }}",
"options": {
"systemMessage": "You are given resume text as input. Your task is to extract employment experiences and return ONLY the average tenure (in months) across those experiences as a single number.\n\nInstructions:\n- Scope: Consider only professional employment entries under Experience (exclude Education, Projects, Achievements, Certifications, Skills, Languages).\n- Grouping: Treat each distinct job/employer entry as one experience. If multiple roles at the same employer have separate date ranges, treat each as separate experiences.\n- Dates:\n - Parse start and end dates in formats like \"MMM YYYY\", \"Month YYYY\", \"YYYY\", or date ranges using -, –, —.\n - If the end date is \"Present\"/current, use today's date.\n - If a date has only a year, assume the month as January.\n - If a start or end month/day is missing, default to the first day of that month.\n- Duration Calculation:\n - Compute the difference in full months for each experience (start inclusive, end exclusive).\n - Do not deduct overlaps; each listed experience is counted independently.\n - Exclude entries without any valid start date.\n- Averaging:\n - Compute the arithmetic mean of the months across all valid experiences.\n - Round to the nearest whole month (standard rounding).\n\n"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.2
},
{
"id": "5e697388-e418-495e-b91f-5eba9deb0427",
"name": "Logic",
"type": "n8n-nodes-base.if",
"position": [
1024,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "21c98617-ba35-4906-afd0-38a82211dbe7",
"operator": {
"type": "number",
"operation": "lt"
},
"leftValue": "={{ $json.output.average.toNumber() }}",
"rightValue": 12
}
]
}
},
"typeVersion": 2.2
},
{
"id": "93b32316-47a7-4769-8a82-289a4c66c73f",
"name": "Create email",
"type": "n8n-nodes-base.code",
"position": [
1248,
-96
],
"parameters": {
"jsCode": "// filename: n8n-code-node-attrition-email.js\n// This Code node builds an email for each item indicating high attrition risk.\n// Output fields:\n// - emailSubject\n// - emailBody\n// - emailTo (optional if you want to set here)\n// - emailCc (optional)\n// - emailMetadata (structured data for downstream logging)\n\nfunction formatDate(dateStr) {\n if (!dateStr) return \"N/A\";\n const d = new Date(dateStr);\n if (isNaN(d.getTime())) return dateStr;\n // Format as YYYY-MM-DD for clarity\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n}\n\nfunction clampScore(score) {\n const n = Number(score);\n if (Number.isNaN(n)) return null;\n return Math.max(0, Math.min(100, n));\n}\n\nfunction classifyRisk(score) {\n if (score == null) return \"Unknown\";\n if (score >= 80) return \"High\";\n if (score >= 50) return \"Medium\";\n return \"Low\";\n}\n\nfunction bulletList(items) {\n if (!Array.isArray(items) || items.length === 0) return \"- None\";\n return items.map(s => `- ${String(s).trim() || \"N/A\"}`).join(\"\\n\");\n}\n\nconst outputs = [];\n\nfor (const item of $input.all()) {\n const j = item.json || {};\n\n const personName = j.personName || j.name || \"The employee\";\n const role = j.role || \"N/A\";\n const department = j.department || null;\n const managerName = j.managerName || \"HR/People Ops\";\n const riskScoreRaw = j.riskScore ?? j.attritionRiskScore;\n const riskScore = clampScore(riskScoreRaw);\n const riskLevel = classifyRisk(riskScore);\n const signals = Array.isArray(j.signals) ? j.signals : [];\n const lastEngagementDate = formatDate(j.lastEngagementDate || j.lastCheckInDate);\n const recommendedActions = Array.isArray(j.recommendedActions) ? j.recommendedActions : [\n \"Schedule a 1:1 check‑in within the next 3–5 days\",\n \"Offer growth or role‑clarity conversation\",\n \"Review workload and compensation alignment\",\n ];\n\n // Email routing (optional; you can also set these in the Email node)\n const emailTo = j.emailTo || j.managerEmail || j.hrEmail || \"\";\n const emailCc = j.emailCc || \"\";\n\n // Compose subject\n const subjectParts = [\n \"[Attrition Alert]\",\n personName !== \"The employee\" ? personName : \"Employee\",\n riskLevel !== \"Unknown\" ? `– ${riskLevel} Risk (${riskScore}%)` : \"– Risk Review\",\n ];\n const emailSubject = subjectParts.filter(Boolean).join(\" \");\n\n // Compose body (plain text; you can convert to HTML if preferred)\n const headerLine = `${personName} ${role !== \"N/A\" ? `(${role})` : \"\"}${department ? `, ${department}` : \"\"}`;\n const riskLine = `Attrition Risk: ${riskLevel}${riskScore != null ? ` (${riskScore}%)` : \"\"}`;\n const signalsBlock = bulletList(signals);\n const actionsBlock = bulletList(recommendedActions);\n\n const emailBody =\n`Hi ${managerName},\n\nThis is a heads‑up that ${headerLine} may be at risk of leaving soon. ${riskLine}.\n\nKey signals observed:\n${signalsBlock}\n\nLast engagement/check‑in: ${lastEngagementDate}\n\nSuggested next steps:\n${actionsBlock}\n\nPlease prioritize a supportive outreach. If helpful, we can prepare a retention plan (growth discussion, workload review, recognition, and role clarity).\n\nThanks,\nPeople Analytics\n`;\n\n outputs.push({\n json: {\n ...j,\n emailSubject,\n emailBody,\n emailTo,\n emailCc,\n emailMetadata: {\n personName,\n role,\n department,\n managerName,\n riskScore,\n riskLevel,\n signals,\n lastEngagementDate,\n recommendedActions,\n generatedAt: new Date().toISOString(),\n },\n },\n });\n}\n\nreturn outputs;\n"
},
"typeVersion": 2
},
{
"id": "cc3e9d3e-4a29-4125-83bd-c43770219fca",
"name": "Send email to hr",
"type": "n8n-nodes-base.gmail",
"position": [
1472,
-96
],
"webhookId": "15c04fae-06e4-41c8-860d-020d07ca31f6",
"parameters": {
"sendTo": "jyothi.swarup@techdome.net.in",
"message": "={{ $json.emailBody }}",
"options": {},
"subject": "={{ $json.emailSubject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "70f5n8rPahCANHs7",
"name": "jyothi"
}
},
"typeVersion": 2.1
},
{
"id": "860057d2-af64-4ea5-8df5-1945f27b52d2",
"name": "Nota adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-336,
-128
],
"parameters": {
"content": "## Trigger for new resume \nStarts the workflow when a new resume file is added (e.g., to storage or inbox)."
},
"typeVersion": 1
},
{
"id": "ce4d511d-b14d-4a13-ac97-a319e90b2d2a",
"name": "Nota adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-304
],
"parameters": {
"content": "## Download resume \nFetches the resume file from the source and makes it available for processing."
},
"typeVersion": 1
},
{
"id": "dcf63ce0-b0f0-4ede-9c95-c7c37814b028",
"name": "Nota adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
384,
80
],
"parameters": {
"content": "## Extract text \nPulls readable text from the downloaded resume using a PDF extraction step."
},
"typeVersion": 1
},
{
"id": "114d70ee-1e49-41f4-937b-6b8e183d787b",
"name": "Nota adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
-304
],
"parameters": {
"content": "## Chat Model \nUses Azure OpenAI Chat to analyze or summarize the extracted resume content."
},
"typeVersion": 1
},
{
"id": "b5ee6f06-3ac8-402a-9051-f5ee0aaae094",
"name": "Nota adhesiva4",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
96
],
"parameters": {
"content": "## Logic \nApplies conditional checks and routing (true/false) based on parsed results."
},
"typeVersion": 1
},
{
"id": "e7fcd240-e008-419f-ac1b-e45593f3c037",
"name": "Nota adhesiva5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1168,
-320
],
"parameters": {
"content": "## Create email \nGenerates a tailored email draft to the candidate or HR using the parsed data."
},
"typeVersion": 1
},
{
"id": "e8aae9bd-426b-4020-8b47-f48bc35b1ae3",
"name": "Nota adhesiva6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
96
],
"parameters": {
"content": "## Send email to hr \nSends the composed message to HR via the configured email service."
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "a93f6021-cc9d-4249-b529-62a19a79292a",
"connections": {
"5e697388-e418-495e-b91f-5eba9deb0427": {
"main": [
[
{
"node": "93b32316-47a7-4769-8a82-289a4c66c73f",
"type": "main",
"index": 0
}
]
]
},
"93b32316-47a7-4769-8a82-289a4c66c73f": {
"main": [
[
{
"node": "cc3e9d3e-4a29-4125-83bd-c43770219fca",
"type": "main",
"index": 0
}
]
]
},
"821c7e31-be41-4e6c-b58b-19a2cf880648": {
"main": [
[
{
"node": "78f8966f-dbeb-4773-b198-9dfb13defd35",
"type": "main",
"index": 0
}
]
]
},
"dabfc101-cfa3-43e6-b568-320c1c2d3e92": {
"main": [
[
{
"node": "821c7e31-be41-4e6c-b58b-19a2cf880648",
"type": "main",
"index": 0
}
]
]
},
"78f8966f-dbeb-4773-b198-9dfb13defd35": {
"main": [
[
{
"node": "5e697388-e418-495e-b91f-5eba9deb0427",
"type": "main",
"index": 0
}
]
]
},
"2ab2bd76-1809-4015-9d99-a278561287da": {
"main": [
[
{
"node": "dabfc101-cfa3-43e6-b568-320c1c2d3e92",
"type": "main",
"index": 0
}
]
]
},
"Azure OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "78f8966f-dbeb-4773-b198-9dfb13defd35",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"a44062d6-1e37-443b-b146-cf32a9ac74cc": {
"ai_outputParser": [
[
{
"node": "78f8966f-dbeb-4773-b198-9dfb13defd35",
"type": "ai_outputParser",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Creación de contenido, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Rahul Joshi
@rahul08Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.
Compartir este flujo de trabajo