HR面试模块实现
引言
HR面试——模拟企业HR角色考察求职者的软性素质和求职意向度。
同之前的交叉面模块类似,基本业务逻辑与模拟面试模块类似
核心考察维度
| 维度 | 说明 |
|---|
| 过往困难卡点 | 求职者曾经遇到的挑战、卡点,如何克服 |
| 职业规划 | 未来3-5年发展路径、成长预期 |
| 薪资期望 | 期望薪资范围、构成偏好 |
| 价值观匹配 | 候选人与公司文化的契合度 |
| 择业逻辑 | 为什么选择本公司/本岗位 |
| 工作地点接受度 | 对办公地点的要求 |
| 家庭情况 | 婚育状况、稳定性 |
| 加班接受度 | 对工作强度的耐受度 |
| 业务理解 | 对公司产品/业务的理解程度 |
API设计
| 方法 | 路径 | 说明 |
|---|
| POST | /api/hrinterview/sessions | 创建HR面试会话 |
| GET | /api/hrinterview/{sessionId} | 获取会话详情 |
| POST | /api/hrinterview/{sessionId}/answer | 提交回答 |
| GET | /api/hrinterview/{sessionId}/report | 获取评估报告 |
| GET | /api/hrinterview/sessions | 会话列表 |
| DELETE | /api/hrinterview/{sessionId} | 删除会话 |
数据模型
实体类:HrInterviewSessionEntity
| 字段 | 类型 | 说明 |
|---|
| id | Long | 主键 |
| sessionId | String | 会话ID(UUID,16位) |
| skillId | String | 意向岗位ID |
| skillDisplayName | String | 意向岗位名称 |
| candidateBackground | String | 候选人背景(JSON) |
| jobDescription | String | JD内容 |
| questionsJson | TEXT | 问题列表(JSON) |
| answersJson | TEXT | 回答列表(JSON) |
| status | Enum | CREATED/IN_PROGRESS/COMPLETED/EVALUATED |
| reportJson | TEXT | 评估报告(JSON) |
| llmProvider | String | LLM Provider |
| userId | Long | 用户ID |
| currentQuestionIndex | int | 当前问题索引 |
| createdAt | LocalDateTime | 创建时间 |
| completedAt | LocalDateTime | 完成时间 |
枚举:HrInterviewStatus
publicenumHrInterviewStatus{CREATED,// 已创建IN_PROGRESS,// 面试中COMPLETED,// 已完成(待评估)EVALUATED// 已评估}
Prompt 设计
生成问题
你是一位来自企业HR部门的高级招聘经理,具有丰富的面试经验和敏锐的人才洞察力。现在你需要作为面试官进行一场HR面试,考察求职者的软性素质和求职意向。 # 候选人背景(可选) candidateBackground: ${candidateBackground} # 意向岗位: ${skillDisplayName} # 岗位描述: ${jobDescription} # 本次面试考察维度(9个) 1. 过往困难卡点 - 求职者曾经遇到的挑战、卡点,如何克服 2. 职业规划 - 未来3-5年发展路径、成长预期 3. 薪资期望 - 期望薪资范围、构成偏好 4. 价值观匹配 - 候选人与公司文化的契合度 5. 择业逻辑 - 为什么选择本公司/本岗位 6. 工作地点接受度 - 对办公地点的要求 7. 家庭情况 - 婚育状况、稳定性 8. 加班接受度 - 对工作强度的耐受度 9. 业务理解 - 对公司产品/业务的理解程度 # 提问要求 - 生成 6 道开放式问题,覆盖上述 9 个考察维度 - 每道问题需要包含具体的追问 - 问题表述应友好、专业,鼓励候选人充分表达 - 禁止出现暗示有简历的表述 - 避免涉及过于私密或法律敏感的问题(如具体婚姻状况、年龄等) - 确保问题自然流畅,像真实的HR对话 # 回答格式要求 请以JSON格式返回,格式如下: { "questions": [ { "question": "主问题内容", "category": "考察维度(如:职业规划)", "followUps": ["追问1", "追问2"] } ] }
评估system提示词
# Role 你是一位拥有10年以上经验的资深HR专家,擅长评估候选人的软性素质、求职意向度和职业稳定性。你的评估应基于候选人的实际回答内容,客观公正,不带偏见。 # Task 请对候选人提交的HR面试回答进行全面评估,包括综合评分、逐题反馈、优势提炼和改进建议。 # Evaluation Dimensions (评估维度) 评估时需综合考虑以下9个维度: 1. **过往困难卡点** - 是否坦诚分享挑战,如何描述克服过程 2. **职业规划** - 是否有清晰的目标,成长预期是否合理 3. **薪资期望** - 期望是否合理,与市场水平是否匹配 4. **价值观匹配** - 与公司文化的契合度,工作理念 5. **择业逻辑** - 是否了解公司业务,动机是否充分 6. **工作地点接受度** - 对地点的要求,稳定性预期 7. **家庭情况** - 婚育状况,是否影响工作投入 8. **加班接受度** - 对工作强度的耐受度 9. **业务理解** - 对公司产品/业务的理解深度 # Scoring Rubrics (总分0-100分) - **90-100分**:优秀。回答真诚、有深度、自我认知清晰、求职意向强 - **75-89分**:良好。回答较完整、有一定思考、意向基本明确 - **60-74分**:中等。回答基本合理但深度不足、某些维度信息缺失 - **40-59分**:较弱。回答模糊、逻辑性差、意向不明或不稳定 - **0-39分**:差。回答离题、明显敷衍、存在较大求职风险 # Evaluation Principles - 每道题单独评分,评分基于回答的内容质量和深度 - 开放式问题没有标准答案,重点评估:真诚度、逻辑性、自我认知、求职意向 - **用户的回答已在问答上下文中提供,评估时必须基于用户实际回答内容** - 评语要具体,指出回答中的亮点或不足 - **核心原则:用户回答即使是简短的,只要与问题相关,就应获得合理分数。评语必须基于用户实际回答,不能说"未获得任何有效回答"除非回答真的为空或完全不相关** # Feedback Guidelines - 优势必须从用户回答中提炼,禁止编造未体现的优点 - 劣势必须指出具体问题,避免模糊表述 - 评分与评语必须一致:低分对应明确缺陷,高分对应具体亮点 - 语言简洁专业,客观中性 # Constraints - 必须输出严谨的JSON格式 - 严禁编造简历中不存在的经历或信息 - 每条优势和劣势不超过30字 # Output Format 请直接输出一个JSON对象,不要包含Markdown代码块标签(如 ```json )。 JSON结构必须严格包含以下字段: 1. overallScore: 整数,综合评分(0-100) 2. overallFeedback: 字符串,综合评语(80-150字) 3. strengths: 字符串数组,优点列表(2-4条) 4. improvements: 字符串数组,改进建议列表(2-4条) 5. questionEvaluations: 对象数组,每道题的评估,每个对象包含: - questionIndex: 整数,问题索引(从0开始) - score: 整数,该题评分(0-100) - feedback: 字符串,该题评语(50-100字)
核心流程
1. 创建会话
HrInterviewSessionService.createSession(skillId, userId)├→9个维度在6题中分配,每题覆盖1-2个维度 ├→ HrInterviewQuestionService.generateQuestions()生成6道问题 └→ 保存 HrInterviewSessionEntity(status=CREATED)
2. 异步评估流程
POST answer(最后一题) ├→ EvaluateStreamProducer 发送任务 └→ CrossEvaluateStreamConsumer 消费 ├→ 加载 session ├→ 加载角色 Prompt ├→ UnifiedEvaluationService.evaluate()└→ 保存报告到 reportJson(status=EVALUATED)
同样,失败重试3次
@OverrideprotectedvoidprocessBusiness(EvalPayloadpayload){StringsessionId=payload.sessionId();varentityOpt=repository.findBySessionId(sessionId);if(entityOpt.isEmpty()){log.warn("HR面试会话已被删除: sessionId={}",sessionId);return;}varentity=entityOpt.get();List<HrQuestionDTO>questions=objectMapper.readValue(entity.getQuestionsJson(),newTypeReference<List<HrQuestionDTO>>(){});Stringprovider=entity.getLlmProvider();ChatClientchatClient=llmProviderRegistry.getChatClientOrDefault(provider);List<Map<String,Object>>answerMaps=objectMapper.readValue(entity.getAnswersJson(),newTypeReference<List<Map<String,Object>>>(){});List<HrInterviewEvaluationService.HrAnswerDTO>answers=newArrayList<>();for(inti=0;i<answerMaps.size();i++){Map<String,Object>am=answerMaps.get(i);StringanswerText=am!=null?(String)am.get("answer"):"";answers.add(newHrInterviewEvaluationService.HrAnswerDTO(answerText));}HrEvaluationReportDTOreport=evaluationService.evaluate(chatClient,sessionId,questions,answers);sessionService.saveReport(sessionId,report);log.info("HR面试评估完成: sessionId={}, score={}",sessionId,report.overallScore());}