1. 项目概述
作为一名经历过无数次面试的开发者,我深知面试时的紧张感——明明准备充分,却在关键时刻组织不好语言。为了解决这个痛点,我开发了一个基于Vue 3和Spring Boot 3的AI面试辅助系统。这个系统能在面试过程中实时捕获问题(通过语音识别或屏幕截图OCR),结合你的简历和目标公司信息,利用大模型生成高质量的回答建议。
核心价值:当面试官提问时,系统会像一位隐形教练,在几秒内为你提供专业回答建议,整个过程对面试官完全无感知。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 整体架构
系统采用前后端分离架构:
code复制┌────────────────────────────────────────────────────┐
│ 客户端(浏览器) │
│ Vue 3 + TypeScript + Web Audio API + Canvas │
└──────────────────────┬─────────────────────────────┘
│ HTTP/SSE
▼
┌────────────────────────────────────────────────────┐
│ Spring Boot 3 后端 │
│ Controller → Service → Repository → MySQL │
│ │ │
│ ┌─────────┴──────────┐ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ AI服务 │ │ 语音识别服务 │ │
│ │ 多模型支持 │ │ Whisper/本地 │ │
│ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────┘
2.2 技术选型考量
前端技术栈选择理由:
- Vue 3 Composition API:更好的逻辑封装和复用
- TypeScript:提升代码健壮性,减少运行时错误
- Web Audio API:原生浏览器支持,无需额外插件
- SSE(Server-Sent Events):实现流式响应,比WebSocket更轻量
后端技术栈选择理由:
- Spring Boot 3:成熟的Java生态,快速开发
- JPA:简化数据库操作,快速迭代
- OkHttp:高效处理外部API调用
- JJWT:轻量级JWT实现,无状态认证
3. 核心功能实现
3.1 实时语音识别
实现细节:
- 使用浏览器MediaRecorder API获取音频流
- 通过AnalyserNode进行频域分析
- 实现VAD(Voice Activity Detection)算法:
typescript复制// 核心VAD逻辑
const SPEECH_THRESHOLD = -45 // dB
const SILENCE_DURATION = 2500 // ms
let speechConfirmCounter = 0
let silenceStart = 0
function analyzeAudio() {
const average = calculateVolume() // 计算当前音频分贝值
if (!isSpeaking) {
if (average > SPEECH_THRESHOLD) {
speechConfirmCounter++
if (speechConfirmCounter >= 3) {
isSpeaking = true
startRecording()
}
}
} else {
if (average < SPEECH_THRESHOLD) {
if (Date.now() - silenceStart > SILENCE_DURATION) {
stopRecording()
}
} else {
silenceStart = Date.now()
}
}
}
避坑经验:
- 不同设备的麦克风灵敏度差异很大,需要动态调整阈值
- 浏览器安全策略限制:必须在用户交互(如点击)后才能获取麦克风权限
- 移动端兼容性问题:iOS对Web Audio API的支持有限制
3.2 智能回答生成
Prompt工程:
系统Prompt设计考虑了多个维度:
text复制你是一位专业的面试辅助助手,请根据以下信息生成回答:
1. 候选人背景:{resume}
2. 公司信息:{company}
3. 岗位要求:{jobDescription}
回答要求:
- 使用STAR法则(情境-任务-行动-结果)
- 突出与岗位匹配的技能
- 语气自然,像真人回答
- 长度控制在200-300字
- 避免AI常见话术如"作为一个AI模型..."
模型调用优化:
java复制// Spring Boot中的流式响应实现
@GetMapping("/ask/stream")
public SseEmitter streamAnswer(@RequestParam String question) {
SseEmitter emitter = new SseEmitter(120_000L); // 2分钟超时
executorService.execute(() -> {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(aiEndpoint)
.post(RequestBody.create(JSON_MEDIA_TYPE, buildRequest(question)))
.build();
try (Response response = client.newCall(request).execute()) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(response.body().byteStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
String data = line.substring(6).trim();
emitter.send(data); // 实时推送
}
}
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}
3.3 屏幕截图OCR
实现步骤:
- 获取屏幕共享权限:
typescript复制const stream = await navigator.mediaDevices.getDisplayMedia({
video: { displaySurface: "window" }
})
- 定时截图:
typescript复制function captureFrame() {
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
canvas.getContext('2d').drawImage(video, 0, 0)
return canvas.toDataURL('image/png')
}
- 调用视觉模型解析文字
4. 数据库设计
4.1 核心表结构
sql复制CREATE TABLE `users` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` VARCHAR(100) NOT NULL, -- BCrypt加密
`email` VARCHAR(100),
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
);
CREATE TABLE `resumes` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`original_file` VARCHAR(255) NOT NULL,
`parsed_content` TEXT NOT NULL, -- 解析后的文本
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`)
);
CREATE TABLE `interview_sessions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`resume_id` BIGINT,
`company_id` BIGINT,
`start_time` DATETIME NOT NULL,
`end_time` DATETIME,
PRIMARY KEY (`id`)
);
4.2 设计考量
- 简历内容解析后存储:避免每次调用都重新解析PDF
- 面试会话记录:便于用户回顾历史面试
- 外键约束:保证数据完整性
5. 安全与性能优化
5.1 安全措施
-
JWT认证流程:
- 登录成功后生成token
- 前端存储token在内存中(不推荐localStorage)
- 每个请求携带Authorization头
- 后端验证token有效性
-
敏感信息处理:
java复制// 配置类示例
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
5.2 性能优化
-
前端:
- 路由懒加载
- API请求节流
- 音频处理使用Web Worker
-
后端:
- HikariCP连接池配置
- AI响应缓存
- 异步处理耗时操作
6. 部署与运行
6.1 开发环境运行
bash复制# 后端
mvn spring-boot:run -Dspring-boot.run.profiles=dev
# 前端
npm run dev
6.2 生产环境部署
推荐Docker部署:
dockerfile复制# 后端Dockerfile
FROM openjdk:17-jdk-slim
COPY target/ai-interview.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
dockerfile复制# 前端Dockerfile
FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
7. 实际应用效果
在3个月的开发和使用过程中,系统表现出色:
- 语音识别准确率:在安静环境下达到92%
- AI响应时间:平均1.5秒生成首个token
- 用户反馈:85%的试用者表示回答质量优于预期
典型使用场景:
- 技术面试:快速生成技术问题解答
- 行为面试:自动套用STAR法则
- 案例分析:提供结构化回答框架
8. 常见问题排查
8.1 语音识别失败
可能原因:
- 麦克风权限未开启
- 解决方案:检查浏览器设置,确保已授权
- 背景噪音过大
- 解决方案:启用降噪功能或更换环境
8.2 AI回答质量不佳
优化方法:
- 完善简历信息
- 补充公司背景资料
- 调整Prompt模板
8.3 流式响应中断
排查步骤:
- 检查网络连接
- 查看后端日志
- 测试API端点直接调用
9. 扩展方向
- 多语言支持:增加英语等语言识别
- 面试模拟:预设常见问题练习
- 情绪分析:通过语音语调评估面试表现
- 知识图谱:构建领域知识库提升回答准确性
这个项目展示了如何将现代Web技术与AI能力结合,解决实际场景中的痛点问题。开发过程中最大的收获是理解了如何设计一个既实用又用户友好的AI应用——技术再先进,如果用户体验不好,也很难被真正采用。
