8
n8n 中文网amn8n.com

人员流失风险预警工作流(Azure OpenAI + n8n)

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 16 个节点。主要使用 If, Code, Gmail, GoogleDrive, Agent 等节点。 员工流失风险检测与HR提醒,使用Azure OpenAI GPT-4o-mini和Gmail

前置要求
  • Google 账号和 Gmail API 凭证
  • Google Drive API 凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "ONqZuWnwkE64Mm4F",
  "meta": {
    "instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
    "templateCredsSetupCompleted": true
  },
  "name": "人员流失风险预警工作流 (Azure OpenAI + n8n)",
  "tags": [],
  "nodes": [
    {
      "id": "c48bef67-a0d0-4a4b-a374-3df1a7ed4c19",
      "name": "Azure 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": "结构化输出解析器",
      "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": "新简历触发器",
      "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": "下载简历",
      "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": "提取文本",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        448,
        -96
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1
    },
    {
      "id": "78f8966f-dbeb-4773-b198-9dfb13defd35",
      "name": "计算平均跨度",
      "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": "",
      "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": "便签",
      "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": "便签1",
      "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": "便签2",
      "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": "便签3",
      "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": "便签4",
      "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": "便利贴5",
      "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": "便签 6",
      "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": {
    "Logic": {
      "main": [
        [
          {
            "node": "Create email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create email": {
      "main": [
        [
          {
            "node": "Send email to hr",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract text": {
      "main": [
        [
          {
            "node": "Calculate avg span",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download resume": {
      "main": [
        [
          {
            "node": "Extract text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate avg span": {
      "main": [
        [
          {
            "node": "Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger for new resume": {
      "main": [
        [
          {
            "node": "Download resume",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Azure OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Calculate avg span",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Calculate avg span",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 内容创作, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量16
分类2
节点类型10
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Rahul Joshi

Rahul Joshi

@rahul08

Rahul 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.

外部链接
在 n8n.io 查看

分享此工作流