最近帮一个学弟调的毕业设计实战项目挺有意思,题目是《基于 Vue3 + Spring Boot + DeepSeek 大模型面试官与语音多轮追问的 AI 模拟面试与简历智能诊断系统》。项目规格很完整,带了 PRD 文档、三端高保真源码和数据大屏,不是那种随便糊弄的 demo。借着复盘这套系统的机会,我把整个设计思路、技术拆解、落地实现和踩坑记录整理出来,给正在做类似选题、或者想在大模型应用方向做实战项目的同学当参考。
这套系统核心就两件事:一是用 DeepSeek 大模型模拟面试官,对用户进行多轮问答并支持语音交互;二是对用户上传的简历做结构化解析和智能诊断,给出评分和改进建议。前端用 Vue3 做管理后台和用户端,H5 移动端做面试主场景,Spring Boot 做后端聚合层,把简历解析、会话管理、大模型调用、数据统计全部串起来。数据大屏则把面试记录、诊断结果、用户活跃等数据可视化呈现。
做这个项目最值钱的地方不在于"调一个 API",而在于整套业务闭环——从 PRD 需求拆解,到后端接口设计,再到前端交互和提示词工程,每一步都有实际的业务考量。下面我按项目落地的顺序,把每一层的设计和实现细节完整拆开讲。
1. 项目全景:这套 AI 面试系统到底在解决什么问题
1.1 场景痛点与需求拆解
大学生求职面试练习有一个很现实的问题:找真人模拟面试的成本高、约时间难、反馈主观,而且大部分人不好意思反复麻烦学长学姐或者老师。市面上的面试刷题工具又停留在"题库+标准答案"的模式,根本没有追问、没有压力感、没有针对个人简历的定制提问。
这个项目要解决的痛点就三条:
- 面试练习需要有的"现场感",不是背题而是被追问。
- 简历反馈需要"具体到人"的诊断,而不是泛泛而谈的模板评价。
- 练习数据需要"可视化复盘",让用户看到自己的进步曲线。
需求拆解下来,系统分成三个核心模块:AI 模拟面试、简历智能诊断、数据可视化大屏。再加上支撑这些模块的用户体系、面试记录管理、历史报告查看,整个系统大概有六个主要功能域。
我比较欣赏这个项目的一点是它没有把大模型当"玩具",而是真正放进了业务流程里。模拟面试不是简单一问一答,而是让模型扮演面试官,根据候选人的回答动态生成追问;简历诊断也不是纯模型"自由发挥",而是先做结构化解析、后做维度评分。
1.2 技术选型:Vue3 + Spring Boot + DeepSeek 的组合逻辑
这套技术栈选型很经典,适合课程设计和毕业设计,也适合作为大模型应用的实战入门。
前端选 Vue3 的原因在于 Composition API 对复杂交互的管理能力。面试过程中需要同时处理音频录制、WebSocket 消息、倒计时、题目切换、回答转写文本等多个状态,用 setup 语法配合 reactive/ref 组织业务状态,比 Options API 清晰得多。加上 Element Plus 做后台管理界面、ECharts 做大屏图表,开发效率非常高。
后端选 Spring Boot的原因更直接——Java 生态最成熟的 Web 开发框架,自带 Spring MVC、数据校验、事务管理,和 MySQL、Redis 的整合有大量文档支撑。对于需要实现用户认证、会话管理、简历文件存储、面试记录持久化的系统来说,Spring Boot 能够提供一个稳定、清晰的工程骨架。
接 DeepSeek 大模型则是性价比的考量。做 AI 面试场景需要大量的上下文 token 消耗,单次多轮面试可能吃掉几千 token。DeepSeek 的 API 定价在当时看来非常有竞争力,而且它的中文理解能力和指令遵循能力在面试场景下表现稳定,尤其是对追问逻辑的把握,不会像一些小模型那样容易跑偏。
提示:大模型的选型一定要结合业务场景。法律咨询、医疗问答、代码生成和面试模拟对模型能力的要求侧重点完全不同。面试模拟更看重中文表达、上下文连贯、角色扮演稳定度这三个维度。
2. 核心功能一:AI 模拟面试官的建模与多轮追问实现
2.1 面试官角色建模与提示词工程
模拟面试的体验好坏,七分看提示词。我见过很多大模型项目效果差,不是模型不行,而是提示词写得太随意。这个项目采用的方案是把面试官角色拆成三个层次的提示词结构:
基础人设层:定义面试官的身份、语气、行为规则。
你是一位资深的互联网行业技术面试官,面试岗位是后端开发工程师。 你的面试风格是专业、友好但保持适度压力。 你会根据候选人的回答质量决定追问力度,而不是机械地按题库提问。动态上下文层:注入候选人简历信息和当前面试进度。
候选人简历摘要: - 姓名:张同学 - 学校:某理工类大学 软件工程专业 - 项目经历:校园二手交易平台(Spring Boot + Vue) - 技能标签:Java、MySQL、Redis、Spring Boot 当前面试进展:已完成自我介绍环节,候选人对"项目难点"的回答质量中等。输出约束层:控制模型输出的格式和行为边界。
每次只输出一个问题,不要输出多个问题。 问题长度控制在50字以内。 如果候选人的回答偏离主题,用追问的方式拉回,不要直接批评。 当候选人连续3轮回答质量较高时,可以适当提高问题难度。 当候选人回答明显错误时,先追问一次,不要立即公布正确答案。这三层结构缺一不可。很多人只写了基础人设就接入 API,结果模型自由发挥,问出一些跟简历毫无关系的问题。动态上下文层的作用就是让模型"记住"面前坐的是谁、面的是什么岗位。
2.2 语音多轮追问的实现链路
语音交互是这套系统里最容易翻车的部分。完整链路分四步:录音采集、语音转文字、文本送入大模型、语音播报回复。
前端录音采用 MediaRecorder API,采集格式为 webm,录制完成后通过 FormData 上传到 Spring Boot 后端,后端调用语音识别接口得到文本。这里有个重要的工程决策:把语音转文字放在后端做,而不是让前端直接调用 ASR 服务商 SDK,原因是为了统一管理 API Key、做调用审计,同时后续如果要更换 ASR 服务商,只需要改后端一个适配层。
语音转文字的技术方案,项目里用的是国内云厂商的通用短音频识别接口,支持普通话和英文,返回带标点的文本。实测下来中文识别准确率在 90% 以上,对"Spring Boot""MySQL"这类专业词的识别需要做自定义词库修正,这个细节后面会细说。
文本送入大模型后,模型返回的是文本回复,再通过 TTS 合成语音播报。这里我建议一个优化点:TTS 不要在每一轮都调用,因为语音合成有延迟,会让面试节奏变慢。项目里采用的处理是前几轮用文本展示,当检测到候选人连续回答时间超过 20 秒后才触发 TTS 播报,模拟真人的自然反应节奏。
2.3 追问策略与上下文管理
多轮追问是大模型应用的核心难点。简单的"一问一答不记上下文"很容易让面试变得碎片化。这个项目用 Redis 维护会话上下文,遵循一个简化的滑动窗口策略:
- 每次存储最近 6 轮对话(用户回答 + 面试官提问)。
- 把完整对话记录拼接成消息数组传给 DeepSeek。
- 系统自动提取当前轮次的关键词,和上一轮追问方向做比对,判断是否有明显偏离。
追问策略在提示词层面做了三层约束:第一层是"当候选人回答不完整时,要求举例说明";第二层是"当候选人提到的技术点与该岗位核心要求相关时,深入追问技术细节";第三层是"当候选人回答暴露知识盲区时,换一个相近的问题验证技术水平"。
上下文管理的另一种实现思路是使用向量数据库做长期记忆,但在这个场景下没有必要。面试会话通常持续 5-10 轮,6 轮滑动窗口加简历摘要已经足够覆盖有效信息。过度设计反而会因为 token 浪费导致响应变慢。
注意:上下文拼接时要做好占位符和角色标识。DeepSeek 的 chat 接口支持 system、user、assistant 三种角色,把面试官的提问标记为 assistant 角色、候选人回答标记为 user 角色,模型才能稳定理解对话流向。
3. 核心功能二:简历智能诊断系统
3.1 简历解析与结构化处理
简历诊断的前提是把 PDF/Word 简历转成可分析的文本结构。这块比想象中麻烦,PDF 里表格、分栏、图片中的文字都会干扰提取效果。
项目采用的技术方案是分层解析:后端用 Apache PDFBox 提取 PDF 文本,用 poi-tl 处理 Word 文档,然后对提取结果做规则清洗,包括去除页眉页脚、合并断裂行、识别邮箱和手机号格式。清洗后的纯文本再送入 DeepSeek,让模型按统一模板输出结构化 JSON。
这里有一个非常实用的技巧:简历文本中夹杂着排版噪声,直接丢给大模型容易让模型混乱。项目在送模型前做了一层"分节预处理",用正则和关键词把简历粗分成"基本信息""教育背景""专业技能""项目经历""实习经历""自我评价"几个区块,再让模型针对每个区块做精读。
这个方案相比直接让模型"读完整份简历"有两个好处:一是模型不需要消耗大量 token 去理解排版混乱的内容;二是分区块提取的结果更稳定,不容易串字段。
3.2 诊断维度与评分机制
简历诊断的标准维度设计直接决定产品的专业度。这个项目界定了六个诊断维度:
| 维度 | 考察内容 | 权重 |
|---|---|---|
| 结构完整性 | 简历是否包含基本信息、教育、技能、项目、经历等必备模块 | 10% |
| 技能匹配度 | 技能栈与目标岗位的匹配程度 | 25% |
| 项目含金量 | 项目复杂度、技术深度、结果量化程度 | 25% |
| 表达质量 | 语言精炼度、专业术语准确性、逻辑清晰度 | 15% |
| 成果量化度 | 是否用数据说明成果(如性能提升百分比、用户量) | 15% |
| 个性化程度 | 是否针对目标岗位定制内容 | 10% |
评分不是让大模型凭空打分,而是先用规则引擎对结构化字段做硬性检测:教育经历是否存在、技能标签数量、项目描述字符数、是否包含数字量化指标等。这些硬指标先给一个基础分,再让模型对内容质量做主观评分,最后加权得到总分。
这样做的好处是把模型的主观性限制在可控范围内。如果完全依赖大模型打分,同一份简历换个模型、换个 prompt 就可能差出 10 分,用户会认为系统不专业。规则引擎提供了稳定底线,大模型负责内容质量层面的判断。
3.3 诊断报告生成与改进建议
诊断报告的生成是简历模块的收官环节,也是最容易做成"废话生成器"的部分。项目里对输出做了非常明确的格式绑定,要求模型严格按照五个部分输出:整体评分、优势亮点、主要问题、针对性改进建议、推荐调整方向。
改进建议部分做了一个我很认可的设计——按"立即执行"和"长期优化"两个层级输出。立即执行的意思是改几个字、调整某个描述就能见效;长期优化是需要补充项目经验或者学习某项技术才能实现的。这种建议分层方式,让用户真正拿到报告知道先干什么,而不是看完一堆正确的废话。
同时为了避免报告内容太散,项目中在提示词里给模型一个参考框架:
整体评分:85分 优势亮点:给出2-3条,每条必须对应简历中的具体内容。 主要问题:给出2-3条,每条约30字,必须说明问题导致的影响。 立即执行建议:给出2条,每条必须包含修改后的参考话术。 长期优化建议:给出2条,需要明确方向和学习路径。实测这样的结构化输出,用户阅读体验远好于自由格式的长文报告。
4. 后端工程化:Spring Boot 集成 DeepSeek 的关键实现
4.1 API 调用封装与配置管理
Spring Boot 后端接 DeepSeek 的 API 不算复杂,但要做成生产级代码,封装是必须的。项目里定义了一个DeepSeekClient类,统一处理请求构建、签名、超时和重试。
配置放在application.yml里,包括 API Base URL、API Key、模型名称、温度、最大 token 数。
deepseek: api-key: ${DEEPSEEK_API_KEY} base-url: https://api.deepseek.com model: deepseek-chat temperature: 0.7 max-tokens: 2048 timeout-seconds: 60API Key 不要硬编码在代码里,也不要提交到 Git 仓库,项目里使用环境变量注入,这是一个必须养成的工程习惯。很多人做项目把密钥写死在配置文件中,一旦代码开源或者打包部署,密钥泄露后果很严重。
请求封装时有一个细节:DeepSeek 的 chat/completions 接口支持 messages 数组和 stream 参数。非流式调用代码比较简单,但面试场景下用户等待时间较长,最好使用流式返回。流式调用在 Spring Boot 中的实现需要处理 SSE(Server-Sent Events)或者 WebSocket,项目选择的是 WebSocket 方案,下面展开讲。
4.2 流式输出与 WebSocket 实时消息推送
面试场景对实时性要求比较高。如果用户每说一句话,前端要等大模型全部生成完才看到结果,体验会非常卡。解决方案是把 DeepSeek 的流式响应通过 WebSocket 转发给前端。
实现流程如下:
- 用户在前端点击发送回答,语音转文本完成后,前端把文本通过 WebSocket 发给后端。
- 后端收到消息后,组装对话上下文,调用 DeepSeek API 并开启 stream 模式。
- 后端逐块接收模型的增量输出,每次收到一个 chunk 就立即通过 WebSocket 推送给前端。
- 前端边接收边渲染,用户看到的就是逐字输出的效果。
Spring Boot 集成 WebSocket 并不复杂,配置一个WebSocketConfigurer注册 handler 即可。比较容易被忽视的是 session 管理和心跳机制。WebSocket 连接如果长时间不通信会被中间层断开,需要前端定时发送 ping 消息保活,后端也要做好 session 失效的清理,避免内存泄漏。
流式调用的另一个注意点是 API 返回的 chunk 中 content 字段可能为空,只有 role 或 finish_reason 字段。代码里要判空再拼接,否则前端会收到大量空消息。
4.3 降级策略与异常容错
大模型 API 毕竟是一个外部依赖,不可用时系统不能直接崩溃。项目做了三级降级策略:
- 第一级:DeepSeek 正常响应,走完整 AI 面试流程。
- 第二级:API 超时或返回错误,重试一次,间隔 2 秒。
- 第三级:重试仍失败,启用预置题库兜底,从本地题库中按难度和标签匹配下一道问题。
第三级降级尤其关键。面试场景中用户正在说话,如果后端直接报错,面试中断,体验会非常糟糕。预置题库虽然少了"智能追问"的灵活性,但至少保证面试流程不断,用户也不会察觉异常。
容错处理的代码要集中在一个切面或者一个包装类中,不要在业务代码里到处写 try-catch。项目里做了一个AiInterviewService门面类,把上下文组装、调用、降级、会话持久化全部封装在这个类里,Controller 层只负责接收请求和返回结果。
实操心得:降级逻辑一定要做日志埋点。每次触发降级,记录下来是什么原因,调用链哪里出了问题。一方面方便排查,另一方面也是答辩时很好的材料——证明你考虑到了系统的健壮性。
5. 前端三端实现与数据大屏
5.1 Vue3 用户端与管理端工程实践
前端工程拆分为用户端和管理端两个独立子应用,共用一个代码仓库,但通过不同的路由和构建配置区分。用户端面向求职者,核心页面是面试大厅、会话窗口、简历上传、诊断报告、历史记录。管理端面向后台运营,核心页面是用户管理、面试数据统计、模型调用监控、题库管理。
Vue3 工程初始化用 Vite 构建工具,比 Vue CLI 启动速度快很多,配置也更轻量。
npm create vite@latest ai-interview-web -- --template vue npm install element-plus axios echarts pinia项目里用户端没有使用 Element Plus,而是自定义了一套更轻的组件,因为面试会话界面需要高度定制化的布局。管理端直接用 Element Plus 快速搭建,表格、表单、弹窗这些标准组件非常省时间。
面试会话界面的状态管理是前端最复杂的一块。项目使用 Pinia 维护会话状态,包括当前问题、录音状态、转写文本、流式回复内容、剩余时间、追问轮次等十几个字段。Pinia 的 store 拆分策略是按模块划分,useInterviewStore管面试流程,useUserStore管用户信息,useResumeStore管简历上传和报告缓存。
5.2 移动端适配与录音兼容处理
移动端是这个项目的亮点,因为大学生用手机面试的场景非常普遍。项目采用移动端 H5 方案,通过 viewport 视口配置和 rem 适配实现在不同尺寸的屏幕上正常显示。
移动端录音的兼容性是个让人头痛的问题。安卓机和 iOS 机对 MediaRecorder 的支持差异很大,有些 WebView 内核不识别 webm 格式。项目的处理方案是:录音结束后把 Blob 转成 ArrayBuffer,后端拿到原始音频数据后交给 ASR 接口,不在前端做格式转换。这样规避了大部分兼容问题。
另外,移动端浏览器的自动播放限制会影响语音播报。TTS 生成的音频必须由用户点击交互触发播放,否则会被浏览器拦截。项目里做了一个处理:用户点击"开始回答"按钮时唤醒音频上下文,之后再播放 TTS 就不会被拦截了。这个坑几乎每个人都会踩,提前在代码里做了规避。
5.3 数据大屏可视化设计
数据大屏是项目展示的门面,也是答辩的高分亮点。大屏页面的设计思路是"一屏看全局",主要展示以下数据指标:
- 累计面试次数、累计诊断简历数
- 面试完成率(开始面试但未完成的占比)
- 各岗位方向的面试数量分布
- 简历平均评分及趋势
- 高频面试知识点云图
- 用户在面试中的平均回答时长
实现上使用 ECharts 完成图表渲染,大屏采用深色科技风背景,结合 CSS 动画和数据滚动更新。数据来源由后端统计接口统一提供,前端每 30 秒轮询一次刷新。
大屏可视化做了一个我非常赞同的取舍——不过度追求动态特效。很多同学做大屏喜欢加各种旋转动画、流光边框、3D 特效,结果页面乱成一团,数据本身反而看不清。项目里只在标题栏做了轻微呼吸灯效果,图表全部保持静态可读,重点靠布局疏密和配色区分层级。
6. PRD 文档:从需求到代码的桥梁设计
6.1 PRD 的核心章节与信息架构
这个项目把 PRD 文档作为一个正式交付物,这点非常难得。很多做开发的同学觉得 PRD 是产品经理的事,自己写代码就行,但实际进入工作后就会发现,一份清晰的 PRD 是所有协作的起点。这套系统的 PRD 从四个层面构建:
- 背景与目标:说明项目要解决什么问题、目标用户是谁、核心使用场景。
- 功能需求:按模块拆分成详细的功能列表,每个功能包含触发条件、交互流程、前置后置条件。
- 数据需求:定义核心数据实体、字段、数据类型、是否必填。
- 非功能需求:包括性能指标(接口响应时间、并发量)、兼容性要求、安全要求。
这套系统 PRD 里最有价值的部分是"面试流程状态机",规定了面试会话从创建、等待回答、追问、完成、异常中断之间的转换关系。这个状态机直接指导了后端接口设计和数据库表结构设计。
6.2 从 PRD 到接口设计的映射
PRD 写得好不好,最终要看能不能直接指导技术实现。项目里 PRD 的每个功能点都对应有明确的接口需求和数据结构定义。举个例子,PRD 里写"用户上传简历后,系统应在 30 秒内完成解析并返回结构化报告",落到后端就是POST /api/resume/upload接口,要求返回结果包含解析状态字段。
接口设计遵循 RESTful 规范,资源命名统一:/api/interview、/api/resume、/api/report、/api/user、/api/dashboard。每个接口都定义了请求参数、响应结构、错误码和鉴权方式。
PRD 到接口的映射表格是项目里一个很实用的工具:
| PRD 功能点 | 对应接口 | 请求方式 | 核心字段 |
|---|---|---|---|
| 创建模拟面试 | /api/interview/start | POST | jobType, resumeId |
| 提交面试回答 | /api/interview/answer | POST | sessionId, content |
| 获取下一轮追问 | /api/interview/next | GET | sessionId |
| 上传简历 | /api/resume/upload | POST | file, targetPosition |
| 获取诊断报告 | /api/report/{id} | GET | reportId |
| 大屏统计数据 | /api/dashboard/overview | GET | 无 |
6.3 提示词模板作为 PRD 的一部分
这个项目还有一个比较前卫的做法:把核心提示词模板纳入了 PRD 文档范围。原因是大模型应用和传统软件不一样,提示词就是产品逻辑的一部分,面试官的语气、追问规则、评分标准全部由提示词决定,如果不把提示词当需求来管理,产品行为就没法稳定复现。
PRD 中记录了每个核心场景的提示词版本、每次调整的内容和原因。比如"面试官追问力度"从最初的"一直追问直到候选人不耐烦"改成了"根据回答质量动态调整追问力度",这个改动直接改变了产品体验,理应被记录在需求文档中。
7. 踩坑实录与经验复盘
7.1 DeepSeek 接入的典型问题与处理方案
接入 DeepSeek 过程中我遇到了几个典型问题,值得单独说。
问题一:上下文越长,越容易"失忆"。面试进行到第 7、8 轮时,模型偶尔会忘记候选人项目经历里的细节。排查发现原因是滑动窗口中早期的关键信息被挤出去了。解决方案是把简历摘要和初始面试目标始终挂在 system message 中,不参与滑动窗口淘汰。
问题二:模型输出格式不稳定。要求模型输出 JSON 时,偶尔会在 JSON 前面多出一段解释性文字,导致 JSON 解析失败。解决方案是提示词里明确写"只输出 JSON,不要输出其他内容",并且后端解析时做容错——先尝试直接解析,失败则用正则抽取 JSON 片段再解析。
问题三:并发调用时偶发超时。管理端和后端同时调用 DeepSeek 时,接口响应变慢。排查后发现是单例的 HTTP Client 连接池配置过小,请求排队导致超时。调大连接池并设置合理的超时时间后问题解决。
7.2 Vue3 与 Spring Boot 联调注意事项
前后端联调阶段最影响效率的是跨域问题和数据格式约定问题。
跨域配置在 Spring Boot 中需要实现WebMvcConfigurer的addCorsMappings方法,允许前端开发服务器的地址跨域访问。生产环境上线时建议通过 Nginx 反向代理,让前后端同域部署,彻底规避跨域。
数据格式约定上的坑主要出现在时间字段和数字类型上。Java 后端返回的LocalDateTime默认序列化格式是 ISO 标准格式,前端如果不做转换直接展示,用户会看到一串带 T 的字符串。项目里统一在application.yml配置了spring.jackson.date-format,并在前端封装了全局的日期格式化工具函数,两边对齐了格式约定。
7.3 项目复现与扩展建议
想复现这套项目的同学,建议按顺序分四步走:
- 先把 PRD 文档理清楚,把面试状态机画出来——这是整个系统的骨骼。
- 先做后端接口,用 Postman 验证每个接口的请求和返回值,确保逻辑正确。
- 再写前端核心流程,先用假数据走通面试流程,再接入大模型。
- 最后做简历诊断和大屏,这两个模块对前序模块的依赖较弱,可以并行开发。
项目扩展空间还挺大的。可以给面试系统加一个"面试报告生成"功能,面试结束后自动整理问答记录、给出综合能力雷达图;也可以做岗题匹配,让用户选择岗位后自动从题库和简历中提取高频考点生成个性化题目。简历诊断侧可以考虑接入更多简历模板,甚至做"简历优化建议一键应用"的功能——让模型直接输出优化后的简历文本。
我在带这个项目的过程中最深的体会是:大模型应用项目的核心难点不在于大模型本身,而在于怎么把模型输出嵌入到稳定的业务流程里。提示词工程、降级策略、状态管理、结构化输出,这些才是真正拉开项目档次的地方。如果你正在做类似选题,希望这篇复盘能帮你少走一些弯路。