ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Spring Boot+Vue3+DeepSeek实战:AI模拟面试系统完整落地经验

Spring Boot+Vue3+DeepSeek实战:AI模拟面试系统完整落地经验 大模型面试官这套东西最近在毕业生圈子里确实火。Spring Boot 做后端、Vue3 写前端、DeepSeek 当大脑再叠加上语音追问和简历诊断听起来像是把一整条企业招聘链路都搬进了实训系统。我前阵子刚好带着团队把一个类似的 AI 模拟面试项目从 PRD 一路做到了三端上线中间踩过的坑、试错出来的方案比看十篇教程都值钱。这篇文章就把整个系统从需求拆解到落地的完整思路写出来包括我是怎么设计大模型提示词、怎么解决语音多轮追问的延迟问题、以及三端和可视化大屏到底怎么跟后端配合的。1. 这套系统的价值与需求拆解1.1 它到底解决了什么问题大学生求职面试训练目前面临几个很现实的痛点企业面试官时间有限、线下模拟面试组织成本高、学生自己对着镜子练又没有反馈、简历写完根本没有人愿意花十分钟帮你逐行审核。这套系统用 AI 模拟面试官替代真人核心解决的就是“高频、低成本、可复盘”这三个问题。我把系统按用户链路拆成了三条主线求职端学生上传简历获得结构化诊断报告与 AI 面试官进行多轮真实语音对话结束后拿到能力维度评估和复盘建议。管理端老师/高校查看学生的面试活跃度、平均分、薄弱能力图谱作为教学参考。展示端大屏实时滚动面试数据、能力分布、平均时长等指标用于实验室开放日或课堂成果展示。这个定位决定了系统不是简单套壳一个聊天机器人而是要有完整的业务状态机从简历解析、面试会话创建、追问状态控制、到成绩评估和报告生成每一步都有状态流转。这就是为什么后端必须选 Spring Boot 这类适合做重型业务编排的框架而不是靠前端直连大模型 API 一把梭。1.2 PRD 阶段我重点卡了哪些细节写 PRD 的时候最容易翻车的点是“听起来好像什么都能做但落到页面上全是空洞的界面”。我在这套系统里用了一个技巧所有功能点必须绑定一个用户真实场景并且在原型上能看到完整的数据流转。核心需求优先级我排成了这样P0必须有简历上传与解析、AI 模拟面试文本语音、面试报告生成。P1应该做追问状态机、能力维度雷达图、历史记录回放。P2锦上添花实时大屏、教师后台数据分析。PRD 中有两个细节值得展开说第一简历上传的格式边界。系统必须限定只接收 PDF、DOCX 和纯文本同时在 PDF 解析失败时要给出重试或手动粘贴简历内容的兜底方案。我用的是 PDFBox 解析之后转纯文本再丢给大模型做结构化抽取这一步如果抽取出错后面所有诊断都会跟着出错。第二面试环节的状态机设计。面试会话的流转不是简单的“答完一题到下一题”而是每一道题都有追问深度状态。比如回答“Java 垃圾回收机制”时如果候选人对基础概念回答清晰就继续追到“GC Roots 有哪些”如果概念含糊就转问更基础的问题而不是继续往上加难度。这套自适应追问逻辑光靠自然对话是不可控的必须在前端页面和后端系统里显式维护状态。提示在 PRD 里不要光写“AI 能自动追问”要写清楚追问的触发条件、最大追问轮数、以及触底时的退场策略。否则开发阶段会出现“AI 一直追问面试永远结束不了”的诡异的对话局面。2. 技术选型的底层逻辑Vue3、Spring Boot 与 DeepSeek 的分工2.1 为什么是这三个技术栈这套系统不是选“最新”的技术而是选“最合适”的技术。Vue3 负责前端交互体验。面试过程中需要频繁更新对话流、录音按钮状态、波形动画和成绩图表Vue3 的组合式 APIComposition API在组织这种复杂交互逻辑时比选项式 API 清爽得多。面试对话这种长上下文、高频局部刷新的场景用 ref 和 reactive 管理会话状态非常顺手。如果你之前只写过 Vue2迁移到 Vue3 后最直观的感受就是不再需要到处找 this所有逻辑都可以在 setup 里平铺开来。Spring Boot 负责业务稳定性和生态衔接。简历文件解析、用户数据持久化、面试记录存储、权限控制、以及跟外部大模型 API 的通信——这些都需要一个成熟稳定的后端容器。Spring Boot 的自动配置机制让我能在一小时之内把 Web 层、数据层、第三方接口调用全部串起来。而且后续要接 Redis 缓存会话状态、要接 RabbitMQ 做异步评估任务都有非常成熟的生态支持。DeepSeek 负责自然语言理解和生成。这主要看中了三点中文上下文理解能力强特别是在面试追问这种需要理解语义意图的场景下表现出色支持流式输出可以实现逐字生成的效果体验上非常接近真人对话在同等效果下 API 调用成本明显比某些海外大模型低做课程设计或校园系统时预算压力小很多。2.2 前后端分离的接口契约设计这套系统的核心接口设计直接决定了开发的顺利程度。我采用的是 RESTful API 风格接口路径围绕业务资源设计POST /api/resume/upload 上传简历返回解析后的文本和文件ID GET /api/resume/{id}/diagnosis 获取简历诊断报告 POST /api/interview/start 创建面试会话返回面试ID和首轮题目 POST /api/interview/{sessionId}/talk 提交当前轮回答返回追问或转场 POST /api/interview/{sessionId}/end 结束面试触发评估报告生成 GET /api/interview/{sessionId}/report 获取完整评估报告 GET /api/dashboard/overview 大屏数据聚合接口这里的设计重点是前端不是发起一次完整的对话而是每一轮都向后端提交当前轮的上下文。后端拿到用户回答后结合预设的追问状态机决定下一轮是继续追问还是切换新题。这个设计避免了大模型“顺着用户的话题越飘越远”的问题。在身份认证上我用的是 JWT Redis 的方案Vue3 前端在 axios 拦截器里统一注入 tokenSpring Boot 后端在网关层用 Filter 校验。考虑到学生和老师两类角色我在 token 里额外塞了 role 字段后端用 PreAuthorize 注解对管理端接口做权限控制。3. 简历智能诊断模块的实现细节3.1 从 PDF 到结构化诊断的两级流水线简历诊断是整个系统的第一个核心环节。这里我坚持了两级流水线的架构本地解析文件 → 大模型结构化分析。本地文件解析层处理的是“把文件变成干净文本”。PDF 用 Apache PDFBox 提取DOCX 用 POI 解析纯文本直接读取。这个阶段要注意几个细节PDF 里经常有文本框交错的情况提取出来的文字顺序可能是乱的需要在解析后做一次基础的段落重排。文本去噪必须做把页眉页脚、页码、无意义的表格式字符串全部过滤掉否则会干扰大模型对简历结构的判断。联系方式、姓名这类隐私信息在送进大模型之前可以用正则做脱敏处理替换成占位符。大模型结构化分析层负责的是“把干净文本变成有结构的评估结论”。这里我用 DeepSeek 的对话补全接口要求它输出严格的 JSON 格式诊断报告{ basic_info: { name: , education: , major: , years_of_experience: 0 }, skills: [Java, Spring Boot, MySQL], project_highlights: [xx系统, xx优化性能提升30%], risks: [项目描述偏业务缺少技术难点描述, 技能堆砌但缺少量化结果], scores: { resume_quality: 72, skill_match: 65, project_depth: 60, expression_clarity: 80 }, suggestions: [为每个项目补充一个具体的技术挑战和解决方案] }为了让模型的输出严格符合这个结构我有一套自己调过的提示词模板核心思路是“身份设定 输出格式约束 评分维度说明”三段式。注意不要在大模型返回后再在代码里做 JSON 字符串切割、正则匹配提取字段这种脆弱操作。DeepSeek 支持 JSON 输出模式直接把 format 参数指定为 json_object 就能拿到稳定结构省时省力还不会崩。3.2 评分模型怎么定才不会被吐槽“玄学”我第一次做简历评分的时候直接让大模型拍脑袋给分结果同一份简历反复测试能差出 15 分完全不可用。后来我把评分逻辑从“直觉打分”改成了“维度化计算”先让模型对每个维度给出详细的分析文字再让模型基于分析内容给出一个 1-5 分的整数档位最后后端把原始分值映射成百分制。这个二段式设计的好处非常明显大模型先给出“为什么这样评价”的理由再给出分数分数就有据可依而不是凭空生成。面试报告页面要把这些理由直接展示给学生看告诉他们哪个项目描述太空洞、哪个技能栈写得不匹配而不是只甩一个 62 分。我还在后端加了分数稳定性校验同一份简历连续诊断两次如果总分偏差超过 10%就自动重试一次取更合理的那份结果。这算是一个保底机制因为大模型毕竟有随机性分数稳定对用户体验至关重要。4. AI 模拟面试官与语音多轮追问的核心实现4.1 面试会话的完整生命周期面试会话是整个系统交互密度最高的部分我是这样设计它的生命周期的创建会话前端从简历页点击“开始模拟面试”后端为这个用户创建一个面试会话实例把该用户的简历关键信息技术栈、项目经历组装成面试官的“参考材料”。抽取面试维度根据简历里出现的技能词从面试题库里抽出 3-4 个核心维度比如“Java 基础”、“数据库设计”、“项目深挖”。开始轮询式问答后端在每次调用大模型接口时都会把“面试官人设 本维度考察目标 上一轮回答记录 当前追问深度”组装进提示词。状态推进用户每次回答后后端根据大模型返回的判断是继续追问、切换维度还是结束面试来更新数据库里的会话状态。这个状态机的核心表结构非常朴素CREATE TABLE interview_session ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT, resume_id BIGINT, current_phase VARCHAR(20), -- QUESTIONING / EVALUATING / FINISHED current_dimension VARCHAR(50), -- JAVA / DB / PROJECT / COMPREHENSIVE question_history TEXT, -- 历史问答序列JSON 格式 max_round INT, status VARCHAR(10) );question_history 字段是整个会话的记忆核心。每一轮问答我都会把“面试官问题 用户回答 深度判断”追加到这个 JSON 数组里下一次调用大模型时完整携带。这样每次都像面试官“翻开了之前的面试记录”而不是一个失忆的对话机器人。4.2 自适应追问让 AI 像真人面试官一样调整难度“多轮追问”要做得像样关键在于难度自适应。我的实现方法是在提示词里明确要求模型扮演面试官并且提供了一个追问策略模板如果候选人的回答能覆盖 80% 以上的考察点且表述准确追问一个更深入的技术细节。如果回答只覆盖核心概念但缺乏细节追问“能否结合你使用过的框架版本具体描述实现方式”。如果回答偏离问题或者含糊不清先给一次提示和重新作答的机会再不行就换一个更简单的基础问题。每个维度最多追问 3 轮。如果某维度 3 轮追问均未通过自动切换维度不做过度的负反馈评价。这里有个我测试中发现的要点深度追问的质量取决于提示词里是否给定了“领域知识上下文”。纯粹说“你是面试官继续追问”效果很差模型容易问出重复问题。我会在每次追问前把该维度的高频面试题关键词注入提示词比如对于数据库维度注入“索引、事务隔离级别、分库分表、慢查询优化”这组关键词模型追问的针对性会明显提升。4.3 语音输入与实时交互的技术实现语音交互是这套系统体验上最出彩、但坑也最多的地方。我的技术栈选得比较顺手前端录音浏览器 Audio API 获取麦克风流MediaRecorder 录制音频。语音识别用浏览器内置的 Web Speech API 做语音转文字这样不用部署单独的 ASR 服务。流式输出后端通过 SSE 方式将大模型的回答逐字推送给前端模拟真人打字和说话节奏。核心链路是“麦克风采集 → 音频转文字 → 提交Spring Boot → 拼接Prompt → 调DeepSeek → 流式返回 → 前端逐字渲染”。SSE 是这套链路里最值得讲的一环。因为大模型生成一段完整回答通常要 3-8 秒如果让用户干等一个转圈图标体验会崩塌。用 SSE 之后我可以在前端做个简单的打字机效果收到一个 token 就往当前消息区块追加一个 token。实测下来用户的等待焦虑会大幅缓解。后端实现 SSE 建议用 SseEmitterPostMapping(/api/interview/{sessionId}/talk) public SseEmitter talk(PathVariable Long sessionId, RequestBody TalkRequest request) { SseEmitter emitter new SseEmitter(120_000L); interviewService.chatWithModel(sessionId, request.getAnswer(), emitter); return emitter; }注意SSE 有两个坑一是网关和 Nginx 默认有超时时间必须把 proxy_read_timeout 调大到合适值二是 SseEmitter 需要设置超时时间否则一线程被长时间占用Tomcat 默认线程池很容易被打满。4.4 防止 AI 越权与敏感内容的关键配置作为教育场景系统AI 面试官输出内容必须可控。我在后端设置了两道防线第一道防线是在提示词层面中注入行为边界“你是严谨的面试官只询问与岗位相关的问题不涉及个人隐私对回答不评判对错尊重候选人”。第二道防线是后端在拿到大模型输出后调用一次敏感词过滤接口。我建了一张敏感词表加载到 JVM 内存里做 AC 自动机匹配虽然技术上不算高级但效率高且足够应对校园场景。我踩过一次大坑某次测试时把“追问实现原理”的提示词写得太开放模型居然开始点评代码中涉及的内部框架的设计缺陷。后来我在提示词里加了“本轮聚焦候选人对知识点的掌握程度不对具体公司产品做评价”的强约束输出立刻收敛了很多。5. 三端协同与可视化大屏从一稿到高保真的落地经验5.1 PC端、移动端与后台管理端的差异化设计这个系统我做了三种终端形态分别面向不同场景学生端PC 移动H5核心场景是模拟面试。PC 端注重对话流展示和录音操作H5 端则弱化功能、强化移动场景下的流畅性例如竖屏优化、按钮加大、减少信息密度。教师管理后台数据看板和学生列表。教师不关心单个学生的面试过程更关心整体能力分布和均分。这一端我用的是 Vue3 若依风格的后台模板页面形态以表格和图表为主。大屏端课堂展示或开放日演示用的实时数据面板。三个终端共用一个后端服务但接口视角完全不同学生端走/api/interview/*教师端走/api/admin/*大屏端走/api/dashboard/*。权限上通过 JWT 角色区分避免学生直接调用管理接口翻其他同学的数据。实现三端时我特别注意了组件复用策略。把录音按钮、波形动画、对话气泡这三类高频组件抽成公共组件在 PC 和 H5 端复用但布局组件完全不共用因为两种终端的交互逻辑差异太大强行共用布局组件只会让代码变成一坨难维护的 if 判断。5.2 大屏可视化面板的设计与数据聚合大屏端是展示整套系统成果的门面我做的面板包含六个核心模块实时面试人数基于 SSE 推送实时增减能力维度平均分雷达图覆盖 Java/数据库/项目/综合四大维度高频面试题命中榜从最近面试记录里统计面试时长分布柱状图分组统计 0-5 分钟 / 5-10 分钟 / 10 分钟以上简历诊断平均分趋势折线按天聚合最近面试动态滚动列表脱敏展示这里要提醒一个典型误区不要把大屏做成后端的复杂查询接口。大屏场景的数据实时性要求并不高5 秒轮询一次完全够用。我在后端定义了一个聚合接口/api/dashboard/overview一次性返回大屏需要的所有指标数据前端拿到 JSON 直接渲染完全不上 WebSocket省掉了大量复杂的状态同步问题。如果你用 ECharts 做雷达图有一个小技巧雷达图的 indicator 一定要抽象到后端配置。当后端新增一个能力评估维度时前端不需要发版直接渲染新维度。这个设计在教师管理端迭代反馈时帮我省了好几轮改版时间。6. 常见问题排查与踩坑实录6.1 大模型接口调用类问题问题1DeepSeek 响应超时。大模型接口响应时间不稳定短则 3 秒长则超过 30 秒。解决方案是在后端配置了超时熔断默认 30 秒超时超时后直接降级为重试一次如果还是失败就返回一个友好提示同时把本轮问题标记为跳过不等卡死整个面试流程。问题2上下文长度超出限制。面试进行到第 5-6 轮时如果每轮带着完整历史和简历文本很快会打爆上下文限制。我的策略是把简历的核心信息压缩成一段不超过 500 字的“面试提纲”历史对话保留最近 6 轮完整内容更早的问答用摘要提取。这样实测能够支持到 10 轮以上不触顶。注意做上下文压缩时不要丢了关键信息。我在摘要逻辑里特意要求保留“候选人的错误表述”和“面试官给出的提示词”因为最终评估报告生成时要基于这些细节给出复盘建议。6.2 语音模块的兼容性坑Web Speech API 在不同浏览器下的表现差异很大Chrome 支持最好Safari 部分版本不稳定Firefox 直接不可用。我的处理方案是做一个音频降级优先用浏览器语音识别失败时弹出手动输入文本的 UI。这样至少保证面试流程不中断。另一个坑是麦克风权限被拒绝后MediaRecorder 直接报错。我在前端加了权限预检查在面试开始前先请求一次麦克风权限如果被拒绝就引导用户检查浏览器的权限设置。这个细节看似小但在真机演示时能避免极大的尴尬。6.3 Spring Boot 层的高频问题CORS 跨域配置。Vue3 开发服务器默认在 5173 端口后端在 8080 端口必然跨域。我的做法是在后端写一个全局 CORS 配置类允许本地开发服务器地址同时把前端 axios baseURL 做成环境变量生产环境走 Nginx 反向代理。开发和生产不能互相干扰。会话状态丢失。如果用户刷新页面面试会话状态是否还能恢复我在前端把当前面试 ID 存到了 sessionStorage后端根据面试 ID 恢复对话上下文这样刷新页面后还能继续刚才的面试。但这个问题值得注意如果过程中有敏感信息需要自己做好前端缓存清理。7. 一套直接能用的提示词工程模板这是我最想单独拿出来分享的部分。整套系统的智能化效果一半取决于工程代码另一半取决于提示词设计。以下是我用在模拟面试环节的核心模板你可以直接抄去改你是“某公司 Java 开发工程师岗位”的资深面试官正在面试一名候选人。 你的目标通过提问、追问和倾听评估候选人在【技能维度】上的真实水平。 你的风格专业、严谨、略带亲和力不用评价性语言打击候选人。 【技能维度】Java 基础、数据库原理、项目实践 【候选人简历摘要】 {resume_summary} 【面试规则】 1. 每次只问一个问题。 2. 根据候选人的回答从以下行为中选择一个 - CONTINUE_DIG回答正确且深入追问更细的原理。 - HINT_AND_RETRY回答偏差较大给一次提示并让候选人重答。 - SWITCH_TOPIC当前维度考察充分切换到新维度。 - END_INTERVIEW所有维度考察完毕。 3. 追问最多 3 轮超深立即切换维度。 4. 不要回答候选人关于“你是什么模型、谁开发的你”的问题。 【历史对话】 {conversation_history} 【当前问题】 {current_question} 请根据候选人最新的回答 {latest_answer}输出如下 JSON {action: ..., next_question: ..., comment: ...}这套模板的核心价值在于它把“自由对话”变成了“有约束的对话”模型每次输出都会带上明确的 next action。后端拿到 action 后更新状态机而不是靠猜模型想干什么。提示你可以把 action 设计为枚举后端代码就能基于枚举做策略分发。如果模型输出了非法 action就默认按 SWITCH_TOPIC 处理并忽略 next_question。这相当于给模型输出之上加了一层保险丝。8. 最后一层评估报告与复盘建议面试结束不代表系统工作结束恰恰是最重要的一步开始。我会在后端触发评估报告生成同样用 DeepSeek但提示词聚焦“复盘视角”而不是“考试视角”。报告包含四部分能力雷达评分按面试维度给出 0-100 分评分和一级评价。具体表现分析逐题回顾摘录候选人回答中的亮点和明显错误。改进建议针对薄弱维度给出可执行的学习路径比如“推荐先系统学习索引工作原理再练习慢查询优化”。综合评级模拟企业 HR 视角给出“建议面试通过/待定/不通过”的结果附上原因。报告生成我故意放在后端异步执行。面试结束后前端立即展示“报告生成中”的状态后端用线程池把报告任务丢到队列里去执行生成完后前端轮询接口拉取结果。这样主流程不阻塞用户体验也流畅。这套报告如果只是“给个分”那跟前端自己算分数没区别。我一直坚持的是评分必须跟对话内容强关联。比如说某位学生 MySQL 维度分数低报告里必须摘录他当时“MyISAM 和 InnoDB 的区别说反了”的原话这样学生才知道分数是怎么丢的。我在提示词中明确要求模型在输出评分时引用 2-3 条候选人的原始回答作为评分依据效果立竿见影。我在实际测试中还发现评估报告的完整度是学生二次使用系统的最大动因。第一次面试如果只拿到一个分数大概率不会再回来如果拿到一份能指出具体知识盲区的报告复访率会明显提升。最后说一个关于这个项目整体取舍的体会这套系统涉及的技术栈不少但真正决定项目成败的其实不在技术选型而在细节——大模型的提示词约束、上下文压缩策略、语音降级方案、面试状态机的健壮性。你在课设或者毕设里做类似系统时一定不要只盯着“跑通流程”多想想这些偏体验的细节。把跑通的时间压缩到三分之一把打磨细节的时间留足做出来的东西才真正拿得出手。
返回列表