Ai简历分析:interview模块

interview-guide项目 interview模块的设计流程以及难点

Interview 模拟面试模块设计与实现

这篇笔记记录我在 interview-guide 项目里对 Interview 模块的实现思路、核心接口和评估链路。重点是把“出题、答题、评估、导出”做成一个完整闭环,并且保证文字面试与语音面试的评估逻辑保持一致。

模块能力概览

  • Skill 驱动出题:内置 10+ 面试方向(Java 后端、大厂专项、前端、Python、算法、系统设计、测开、AI Agent 等),每个方向由 SKILL.md 约束考察范围与难度分布。
  • 历史题目去重:创建会话时优先排除历史会话中已问过的题,减少重复考察。
  • 面试时长联动:总时长变更后,各阶段(自我介绍、技术考察、项目深挖、反问)按比例自动分配。
  • 智能追问流:支持多轮追问配置(默认 1 条),模拟真实面试追问过程。
  • 统一评估引擎:文字面试和语音面试复用同一套评估架构(分批评估 + 结构化输出 + 汇总 + 兜底)。
  • 报告导出:支持异步生成并导出 PDF 面试报告。
  • 面试中心:统一入口,支持继续面试、重新面试和历史面试查看。

核心状态流转

关键接口设计

GET /api/interview/sessions 列出面试会话

用途:

  • 面试记录页展示,按创建时间倒序返回会话列表。

调用链:

persistenceService.findAll().stream();

POST /api/interview/sessions 创建面试会话

限流策略:

  • 全局限流 + IP 限流(5 次)

核心逻辑:

sessionService.createSession(request);
persistenceService.getHistoricalQuestions(skillId, request.resumeId());
sessionRepository.findTop10ByResumeIdAndSkillIdOrderByCreatedAtDesc(...);
sessionRepository.findTop10BySkillIdOrderByCreatedAtDesc(...);
questionService.generateQuestionsBySkill(...);
sessionCache.saveSession(...);
persistenceService.saveSession(...);

GET /api/interview/sessions/{sessionId} 获取会话信息

核心逻辑:

sessionService.getSession(sessionId);
sessionCache.getSession(sessionId);
restoreSessionFromDatabase(sessionId);

GET /api/interview/sessions/{sessionId}/question 获取当前问题

核心逻辑:

sessionService.getCurrentQuestionResponse(sessionId);
getCurrentQuestion(sessionId);
getOrRestoreSession(sessionId);
  • 当会话为 CREATED 状态时,按 currentIndex 返回当前题目。

POST /api/interview/sessions/{sessionId}/answers 提交答案并推进

限流策略:

  • 全局限流(10 次)

核心逻辑:

sessionService.submitAnswer(request);
  • 更新题目答案、会话状态、缓存与数据库。
  • 如果是最后一题:
persistenceService.updateEvaluateStatus(sessionId, AsyncTaskStatus.PENDING, null);
evaluateStreamProducer.sendEvaluateTask(sessionId);

POST /api/interview/sessions/{sessionId}/answers 暂存答案(不推进)

核心逻辑:

sessionService.saveAnswer(request);
  • 同步更新 Redis 与数据库。

POST /api/interview/sessions/{sessionId}/complete 提前交卷

核心逻辑:

sessionService.completeInterview(sessionId);
sessionCache.updateSessionStatus(sessionId, SessionStatus.COMPLETED);
  • 持久化数据库状态。
evaluateStreamProducer.sendEvaluateTask(sessionId);

GET /api/interview/sessions/unfinished/{resumeId} 查找未完成会话

核心逻辑:

sessionService.findUnfinishedSessionOrThrow(resumeId);
findUnfinishedSession(resumeId);
sessionCache.findUnfinishedSessionId(resumeId);
persistenceService.findUnfinishedSession(resumeId);

GET /api/interview/sessions/{sessionId}/report 生成面试评估报告

核心逻辑:

sessionService.generateReport(sessionId);
evaluationService.evaluateInterview(...);
unifiedEvaluationService.evaluate(...);
evaluateInBatches(...);
summarizeBatchResults(...);
structuredOutputInvoker.invoke(...);
securedSystemPrompt = systemPromptWithFormat + ANTI_INJECTION_INSTRUCTION;

通过反注入指令降低用户输入污染模型行为的风险。

GET /api/interview/sessions/{sessionId}/details 获取面试详情

调用链:

historyService.getInterviewDetail(sessionId);
interviewPersistenceService.findBySessionId(sessionId);

GET /api/interview/sessions/{sessionId}/export 导出面试报告 PDF

调用链:

historyService.exportInterviewPdf(sessionId);
interviewPersistenceService.findBySessionId(sessionId);
pdfExportService.exportInterviewReport(session);

DELETE /api/interview/sessions/{sessionId} 删除面试会话

调用链:

persistenceService.deleteSessionBySessionId(sessionId);
sessionRepository.findBySessionId(sessionId);
sessionRepository.delete(session);

评估引擎实现要点

  • 同一条评估链路兼容文字面试与语音面试,减少分叉实现成本。
  • 分批评估后再汇总,兼顾长上下文稳定性与结构化输出质量。
  • 引入反注入提示词拼接,降低恶意输入对评估结果的干扰。
  • 异常场景通过统一调用器与兜底字段输出,避免报告直接失败。

小结

Interview 模块目前已经实现了从创建会话、动态出题、过程作答、异步评估到报告导出的完整流程。对我来说,这一模块最关键的价值是把“面试过程管理”和“评估结果生产”拆成可演进的两个层次,后续无论替换题库策略还是升级评估模型,整体改动都能保持可控。