8
n8n 中文网amn8n.com

使用GPT-4评分和Gmail通知自动化候选人评估

中级

这是一个HR, AI Summarization领域的自动化工作流,包含 12 个节点。主要使用 If, Set, Code, Gmail, Webhook 等节点。 使用GPT-4评分和Gmail通知自动化候选人评估

前置要求
  • Google 账号和 Gmail API 凭证
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "nodes": [
    {
      "id": "1",
      "name": "CV 提交 Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        240,
        300
      ],
      "parameters": {
        "path": "cv-received",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "2",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        140,
        180
      ],
      "parameters": {
        "width": 240,
        "height": 160,
        "content": "## 招聘 AI 配置"
      },
      "typeVersion": 1
    },
    {
      "id": "3",
      "name": "职位要求",
      "type": "n8n-nodes-base.set",
      "position": [
        440,
        300
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "minExperience",
              "value": 5
            },
            {
              "name": "passingScore",
              "value": 75
            }
          ],
          "string": [
            {
              "name": "requiredSkills",
              "value": "JavaScript,React,Node.js,Python"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4",
      "name": "使用 AI 解析简历",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        640,
        300
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "body": {
          "model": "gpt-4",
          "messages": [
            {
              "role": "system",
              "content": "You are a CV parsing expert. Extract key information from CVs and return structured JSON data."
            },
            {
              "role": "user",
              "content": "Parse this CV and extract: name, email, phone, skills, experience_years, education, previous_roles. CV content: {{ $json.cv_content }}"
            }
          ],
          "response_format": {
            "type": "json_object"
          }
        },
        "method": "POST",
        "headers": {
          "Content-Type": "application/json",
          "Authorization": "Bearer {{ $credentials.openai.apiKey }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5",
      "name": "候选人评分",
      "type": "n8n-nodes-base.code",
      "position": [
        840,
        300
      ],
      "parameters": {
        "jsCode": "// Intelligent candidate scoring algorithm\nconst candidate = $json.choices[0].message.content;\nconst requirements = $node['Job Requirements'].json;\nconst candidateData = JSON.parse(candidate);\n\nlet totalScore = 0;\n\n// Skills matching (40% weight)\nconst requiredSkills = requirements.requiredSkills.split(',');\nconst candidateSkills = candidateData.skills || [];\nconst skillsMatch = requiredSkills.filter(skill => \n  candidateSkills.some(cSkill => cSkill.toLowerCase().includes(skill.toLowerCase()))\n);\nconst skillsScore = (skillsMatch.length / requiredSkills.length) * 40;\ntotalScore += skillsScore;\n\n// Experience scoring (30% weight)\nconst experienceYears = candidateData.experience_years || 0;\nlet experienceScore = 0;\nif (experienceYears >= requirements.minExperience) {\n  experienceScore = 30;\n} else if (experienceYears >= requirements.minExperience * 0.8) {\n  experienceScore = 25;\n} else if (experienceYears >= requirements.minExperience * 0.6) {\n  experienceScore = 20;\n} else {\n  experienceScore = 10;\n}\ntotalScore += experienceScore;\n\n// Education scoring (20% weight)\nconst education = candidateData.education || '';\nlet educationScore = 0;\nif (education.toLowerCase().includes('master') || education.toLowerCase().includes('phd')) {\n  educationScore = 20;\n} else if (education.toLowerCase().includes('bachelor') || education.toLowerCase().includes('degree')) {\n  educationScore = 15;\n} else {\n  educationScore = 10;\n}\ntotalScore += educationScore;\n\n// Role relevance (10% weight)\nconst previousRoles = candidateData.previous_roles || [];\nconst roleRelevance = previousRoles.some(role => \n  role.toLowerCase().includes('engineer') || role.toLowerCase().includes('developer')\n) ? 10 : 5;\ntotalScore += roleRelevance;\n\n// Determine recommendation\nlet recommendation = 'reject';\nlet priority = 'low';\n\nif (totalScore >= 85) {\n  recommendation = 'hire';\n  priority = 'high';\n} else if (totalScore >= requirements.passingScore) {\n  recommendation = 'interview';\n  priority = 'medium';\n} else if (totalScore >= 60) {\n  recommendation = 'phone_screen';\n  priority = 'low';\n}\n\nreturn {\n  candidateData,\n  scoring: {\n    skillsScore,\n    experienceScore,\n    educationScore,\n    roleRelevance,\n    totalScore: Math.round(totalScore),\n    recommendation,\n    priority\n  },\n  jobTitle: requirements.jobTitle,\n  evaluatedAt: new Date().toISOString()\n};"
      },
      "typeVersion": 1
    },
    {
      "id": "6",
      "name": "检查是否为顶尖候选人",
      "type": "n8n-nodes-base.if",
      "position": [
        1040,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.scoring.recommendation }}",
              "rightValue": "hire"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7",
      "name": "提醒 HR 团队",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1240,
        100
      ],
      "parameters": {
        "sendTo": "={{ $node['Job Requirements'].json.hrEmail }}",
        "message": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: Arial, sans-serif; margin: 20px; background-color: #f8f9fa; }\n    .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; }\n    .top-candidate { background: linear-gradient(45deg, #28a745, #20c997); color: white; padding: 20px; text-align: center; margin: 20px 0; border-radius: 10px; }\n    .candidate-info { background: #d4edda; padding: 15px; margin: 15px 0; border-radius: 5px; }\n    .score-breakdown { background: #f8f9fa; padding: 15px; margin: 15px 0; border-radius: 5px; }\n    .cta { background: #007bff; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; display: inline-block; margin: 20px 0; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"top-candidate\">\n      <h2>🌟 EXCEPTIONAL CANDIDATE</h2>\n      <p>High-priority candidate for {{ $json.jobTitle }}</p>\n    </div>\n    \n    <div class=\"candidate-info\">\n      <h3>👤 Candidate Profile</h3>\n      <p><strong>Name:</strong> {{ $json.candidateData.name }}</p>\n      <p><strong>Email:</strong> {{ $json.candidateData.email }}</p>\n      <p><strong>Phone:</strong> {{ $json.candidateData.phone }}</p>\n      <p><strong>Experience:</strong> {{ $json.candidateData.experience_years }} years</p>\n      <p><strong>Education:</strong> {{ $json.candidateData.education }}</p>\n    </div>\n    \n    <div class=\"score-breakdown\">\n      <h3>📊 Scoring Breakdown</h3>\n      <p><strong>Overall Score:</strong> {{ $json.scoring.totalScore }}/100</p>\n      <p><strong>Skills Match:</strong> {{ $json.scoring.skillsScore }}/40</p>\n      <p><strong>Experience:</strong> {{ $json.scoring.experienceScore }}/30</p>\n      <p><strong>Education:</strong> {{ $json.scoring.educationScore }}/20</p>\n      <p><strong>Role Relevance:</strong> {{ $json.scoring.roleRelevance }}/10</p>\n    </div>\n    \n    <div style=\"text-align: center;\">\n      <a href=\"mailto:{{ $json.candidateData.email }}\" class=\"cta\">📧 Contact Candidate</a>\n      <a href=\"https://calendly.com/interview-{{ $json.candidateData.name }}\" class=\"cta\">📅 Schedule Interview</a>\n    </div>\n    \n    <p style=\"color: #666; font-size: 14px; margin-top: 30px;\">\n      Recommendation: {{ $json.scoring.recommendation }} | Priority: {{ $json.scoring.priority }}\n    </p>\n  </div>\n</body>\n</html>",
        "options": {
          "contentType": "html"
        },
        "subject": "🌟 Top Candidate Alert - {{ $json.jobTitle }}"
      },
      "typeVersion": 1
    },
    {
      "id": "8",
      "name": "检查是否值得面试",
      "type": "n8n-nodes-base.if",
      "position": [
        1040,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.scoring.recommendation }}",
              "rightValue": "interview"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "9",
      "name": "发送面试邀请",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1240,
        300
      ],
      "parameters": {
        "sendTo": "={{ $json.candidateData.email }}",
        "message": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: Arial, sans-serif; margin: 20px; background-color: #f8f9fa; }\n    .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; }\n    .interview-invite { background: #007bff; color: white; padding: 20px; text-align: center; margin: 20px 0; border-radius: 8px; }\n    .next-steps { background: #e8f4f8; padding: 15px; margin: 15px 0; border-radius: 5px; }\n    .cta { background: #28a745; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; display: inline-block; margin: 20px 0; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"interview-invite\">\n      <h2>🎉 Interview Invitation</h2>\n      <p>Congratulations! We'd like to interview you for the {{ $json.jobTitle }} position.</p>\n    </div>\n    \n    <p>Dear {{ $json.candidateData.name }},</p>\n    \n    <p>We were impressed by your profile and would like to invite you for an interview.</p>\n    \n    <div class=\"next-steps\">\n      <h3>📅 Next Steps</h3>\n      <ul>\n        <li>Schedule your interview using the link below</li>\n        <li>Prepare for technical questions about your experience</li>\n        <li>Review our company culture and values</li>\n        <li>Prepare questions about the role and team</li>\n      </ul>\n    </div>\n    \n    <div style=\"text-align: center;\">\n      <a href=\"https://calendly.com/interview-{{ $json.candidateData.name }}\" class=\"cta\">\n        📅 Schedule Your Interview\n      </a>\n    </div>\n    \n    <p>Looking forward to meeting you!</p>\n    \n    <p style=\"color: #666; font-size: 14px; margin-top: 30px;\">\n      Best regards,<br>\n      HR Team\n    </p>\n  </div>\n</body>\n</html>",
        "options": {
          "contentType": "html"
        },
        "subject": "Interview Invitation - {{ $json.jobTitle }} Position"
      },
      "typeVersion": 1
    },
    {
      "id": "10",
      "name": "检查是否被拒绝",
      "type": "n8n-nodes-base.if",
      "position": [
        1040,
        400
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.scoring.recommendation }}",
              "rightValue": "reject"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "11",
      "name": "发送拒绝邮件",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1240,
        400
      ],
      "parameters": {
        "sendTo": "={{ $json.candidateData.email }}",
        "message": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: Arial, sans-serif; margin: 20px; background-color: #f8f9fa; }\n    .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; }\n    .thank-you { background: #6c757d; color: white; padding: 20px; text-align: center; margin: 20px 0; border-radius: 8px; }\n    .feedback { background: #f8f9fa; padding: 15px; margin: 15px 0; border-radius: 5px; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"thank-you\">\n      <h2>Thank You for Your Interest</h2>\n      <p>We appreciate you taking the time to apply for the {{ $json.jobTitle }} position.</p>\n    </div>\n    \n    <p>Dear {{ $json.candidateData.name }},</p>\n    \n    <p>Thank you for your application. While your background is impressive, we have decided to move forward with other candidates who more closely match our current requirements.</p>\n    \n    <div class=\"feedback\">\n      <h3>💡 For Future Opportunities</h3>\n      <p>We encourage you to:</p>\n      <ul>\n        <li>Continue developing your technical skills</li>\n        <li>Gain more relevant experience in your field</li>\n        <li>Apply for future positions that match your expertise</li>\n      </ul>\n    </div>\n    \n    <p>We'll keep your resume on file and will reach out if a suitable position becomes available.</p>\n    \n    <p style=\"color: #666; font-size: 14px; margin-top: 30px;\">\n      Best regards,<br>\n      HR Team\n    </p>\n  </div>\n</body>\n</html>",
        "options": {
          "contentType": "html"
        },
        "subject": "Thank you for your application - {{ $json.jobTitle }}"
      },
      "typeVersion": 1
    },
    {
      "id": "12",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1140,
        60
      ],
      "parameters": {
        "width": 240,
        "height": 160,
        "content": "## AI 驱动决策"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Score Candidate": {
      "main": [
        [
          {
            "node": "Check If Top Candidate",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check If Interview Worthy",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check If Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Job Requirements": {
      "main": [
        [
          {
            "node": "Parse CV with AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CV with AI": {
      "main": [
        [
          {
            "node": "Score Candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Rejected": {
      "main": [
        [
          {
            "node": "Send Rejection Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CV Submission Webhook": {
      "main": [
        [
          {
            "node": "Job Requirements",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Top Candidate": {
      "main": [
        [
          {
            "node": "Alert HR Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Interview Worthy": {
      "main": [
        [
          {
            "node": "Send Interview Invitation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

中级 - 人力资源, AI 摘要总结

需要付费吗?

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

工作流信息
难度等级
中级
节点数量12
分类2
节点类型7
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

外部链接
在 n8n.io 查看

分享此工作流