这是一个基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 的 AI 智能面试辅助平台。系统提供三大核心功能:
- 智能简历分析:上传简历后,AI 自动进行多维度评分并给出改进建议。
- 模拟面试系统:基于简历内容生成个性化面试题,支持实时问答和答案评估。
- RAG 知识库问答:上传技术文档构建私有知识库,支持向量检索增强的智能问答。

项目地址
- Github:https://github.com/Snailclimb/interview-guide
- Gitee:https://gitee.com/SnailClimb/interview-guide
完整代码完全免费开源。
系统架构
系统采用前后端分离架构,整体分为三层:前端展示层、后端服务层、数据存储层。

后端层:
- REST Controllers:统一的 API 入口,处理 HTTP 请求。
- 业务服务层:
- Resume Service:简历上传、解析、AI 分析。
- Interview Service:面试会话管理、问题生成、答案评估。
- Knowledge Service:知识库上传、文本分块、向量化。
- RAG Chat Service:检索增强生成,流式问答。
- 异步处理层:基于 Redis Stream 的消费者,异步处理耗时的 AI 任务(如简历分析、向量化、面试评估)。
- AI 集成层:Spring AI + DashScope(通义千问)。统一的 LLM 调用接口,支持对话生成和文本向量化。
数据存储层:
- PostgreSQL + pgvector:关系数据与向量检索。
- Redis:会话缓存与消息队列(Stream)。
- RustFS/MinIO (S3):原始文件存储。
异步处理流程:
上传请求 → 保存文件 → 发送消息到 Stream → 立即返回 ↓ Consumer 消费消息 ↓ 执行分析/向量化任务 ↓ 更新数据库状态 ↓ 前端轮询获取最新状态
状态流转:PENDING → PROCESSING → COMPLETED / FAILED
知识库问答处理流程:
知识库问答 → 问题向量化 → pgvector 相似度搜索 → 检索相关文档 ↓ 构建 Prompt → LLM 生成回答 → SSE 流式返回
技术栈概览
后端技术
| 技术 | 版本 | 说明 |
|---|---|---|
| Spring Boot | 4.0 | 应用框架 |
| Java | 21 | 开发语言 |
| Spring AI | 2.0 | AI 集成框架 |
| PostgreSQL + pgvector | 14+ | 关系数据库 + 向量存储 |
| Redis | 6+ | 缓存 + 消息队列(Stream) |
| Apache Tika | 2.9.2 | 文档解析 |
| iText 8 | 8.0.5 | PDF 导出 |
| MapStruct | 1.6.3 | 对象映射 |
| Gradle | 8.14 | 构建工具 |
前端技术
| 技术 | 版本 | 说明 |
|---|---|---|
| React | 18.3 | UI 框架 |
| TypeScript | 5.6 | 开发语言 |
| Vite | 5.4 | 构建工具 |
| Tailwind CSS | 4.1 | 样式框架 |
| React Router | 7.11 | 路由管理 |
| Framer Motion | 12.23 | 动画库 |
| Recharts | 3.6 | 图表库 |
| Lucide React | 0.468 | 图标库 |
技术选型常见问题解答
为什么选择 Spring AI?
Spring AI 是 Spring 官方推出的 AI 集成框架,提供了统一的 LLM 调用抽象。
- 统一抽象:一套代码支持多种 LLM 提供商(OpenAI、阿里云 DashScope、Ollama 等),切换模型只需修改配置。
- Spring 生态集成:与 Spring Boot 无缝集成,支持自动配置、依赖注入、声明式调用。
- 内置向量存储支持:原生支持 pgvector、Milvus、Pinecone 等向量数据库,简化 RAG 开发。
- 结构化输出:通过
BeanOutputConverter将 LLM 输出直接映射为 Java 对象,无需手动解析 JSON。
// 示例:Spring AI 结构化输出
var converter = new BeanOutputConverter<>(ResumeAnalysisDTO.class);
String result = chatClient.prompt()
.system(systemPrompt)
.user(userPrompt + converter.getFormat())
.call().content();
return converter.convert(result); // 直接得到 Java 对象
数据存储为什么选择 PostgreSQL + pgvector?
本项目需要同时存储结构化数据(简历、面试记录)和向量数据(文档 Embedding)。
| 方案 | 优点 | 缺点 |
|---|---|---|
| PostgreSQL + pgvector | 一套数据库搞定,运维简单 | 向量检索性能不如专业向量库 |
| PostgreSQL + Milvus | 向量检索性能更好 | 多一个组件,运维复杂度增加 |
| PostgreSQL + Pinecone | 云托管,无需运维 | 成本高,数据在第三方 |
选择 pgvector 的理由:
- 架构简单:不引入额外组件,降低部署和运维复杂度。
- 性能够用:HNSW 索引支持毫秒级检索,万级文档场景完全够用。
- 事务一致性:向量数据和业务数据在同一数据库,天然支持事务。
- SQL 查询:可以结合 WHERE 条件过滤,比如'只在某个分类的知识库中检索'。
-- pgvector 相似度搜索示例
SELECT content, 1-(embedding <=> $1) as similarity
FROM vector_store
WHERE metadata->>'category'='Java'
ORDER BY embedding <=> $1
LIMIT 5;
PostgreSQL 最大的优势在于其强大的可扩展性。开发者可以在不修改内核的情况下,像'即插即用'一样为数据库安装各种功能强大的插件,这让 PostgreSQL 变成了一个无所不能的'数据瑞士军刀'。
- AI 向量检索? 有官方推荐的 pgvector 扩展,性能强大,生态成熟。
- 全文搜索? 内置支持,或使用 pg_bm25 等扩展。
- 时序数据? 有顶级的 TimescaleDB 扩展。
- 地理信息? 有行业标准的 PostGIS 扩展。
这种'一站式'解决能力,正是其魅力所在。它意味着许多项目不再需要依赖 Elasticsearch、Milvus 等大量外部中间件,仅凭一个增强版的 PostgreSQL 即可满足多样化需求,从而极大地简化了技术栈,降低了开发和运维的复杂度与成本。
为什么引入 Redis?
本项目主要有两个场景用到了 Redis:
- Redis 替代
ConcurrentHashMap实现会话的缓存。 - 基于 Redis Stream 实现简历分析、知识库向量化等场景的异步(还能解耦,分析和向量化可以使用其他编程语言来做)。
为什么引入 Redis Stream?为何不选择 Kafka、RabbitMQ 等更成熟的消息队列?
简历分析、知识库向量化等 AI 任务耗时较长(10-60 秒),不适合同步处理。需要消息队列实现异步解耦。
| 维度 | Redis Stream | RabbitMQ | Kafka | 内存队列 |
|---|---|---|---|---|
| 吞吐量 | 高(十万级 QPS) | 中(万级 QPS) | 极高(百万级,水平扩展) | 极高(千万级/秒,受限于 CPU/内存) |
| 延迟 | 极低(亚毫秒级) | 低(毫秒级) | 中(毫秒到十毫秒级) | 极低(纳秒/微秒级) |
| 持久化 | 支持(RDB/AOF) | 支持(Mnesia/磁盘) | 强支持(原生分段日志) | 无(进程终止即失) |
| 消息堆积能力 | 一般(受限于内存) | 中(磁盘堆积,性能下降明显) | 极强(TB 级磁盘存储) | 差(受限于堆内存) |
| 消费模式 | 发布订阅 / 消费者组 | 灵活路由 / 多种交换机模式 | 发布订阅 / 消费者组 | 点对点 / 多消费者(取决于实现) |
| 消息回溯 | 支持(按 ID / 时间范围) | 不支持 | 强支持(按 Offset / 时间戳) | 不支持 |
| 消息顺序性 | 单 Stream 有序 | 单队列有序 | 单 Partition 有序 | 有序(单队列) |
| 可靠性 | 中(异步复制可能丢失) | 高(Publisher Confirm / 事务) | 极高(多副本 ISR + acks) | 低(无持久化、无确认) |
| 运维复杂度 | 低 | 中 | 高(KRaft 模式已简化) | 极低 |
| 适用场景 | 轻量级流处理、已有 Redis 基础设施 | 复杂路由、企业级集成 | 大数据流、事件溯源、日志聚合 | 进程内解耦、极致性能场景 |
选择 Redis Stream 的理由:
- 复用现有组件:Redis 已用于会话缓存,无需引入新中间件。
- 功能满足需求:支持消费者组、消息确认(ACK)、持久化。
- 运维简单:对于中小型项目,Redis Stream 完全够用。
构建工具为什么选择 Gradle?
SpringBoot 官方现在用的就是 Gradle,加上国内现在都是 Maven 更多,换个 Gradle 还更新颖一些。
为什么使用 MapStruct?
项目中有大量 Entity ↔ DTO 转换需求,MapStruct 是编译时代码生成的对象映射框架:
| 方案 | 性能 | 类型安全 | 使用复杂度 |
|---|---|---|---|
| MapStruct | 零反射,最快 | 编译时检查 | 定义接口即可 |
| BeanUtils | 反射,慢 | 运行时报错 | 一行代码 |
| ModelMapper | 反射,较慢 | 运行时报错 | 配置复杂 |
| 手写转换 | 最快 | 编译时检查 | 重复代码多 |
为什么使用 Apache Tika?
系统需要解析多种格式的文档(PDF、Word、TXT),Apache Tika 是 Apache 基金会的文档解析库:
- 格式支持全:PDF、DOCX、DOC、TXT、HTML、Markdown 等上百种格式。
- 自动识别:根据文件内容自动检测格式,无需依赖文件扩展名。
- 文本提取:统一的 API 提取纯文本,屏蔽格式差异。
// Tika 解析示例
Tika tika = new Tika();
String content = tika.parseToString(inputStream); // 自动识别格式并提取文本
为什么使用 SSE 而不是 WebSocket?
知识库问答需要流式输出(像 ChatGPT 那样逐字显示),有两种技术选择:
| 方案 | 优点 | 缺点 |
|---|---|---|
| SSE | 简单,基于 HTTP,单向推送 | 仅支持服务端 → 客户端 |
| WebSocket | 双向通信,功能强大 | 协议复杂,需要维护连接状态 |
选择 SSE 的理由:
- 场景匹配:LLM 流式输出是单向的(服务端 → 客户端),不需要双向通信。
- 实现简单:基于 HTTP,天然支持重连、跨域。
- Spring 支持好:
Flux<ServerSentEvent<String>>一行代码搞定。
前端为什么选择 React + TypeScript + Tailwind CSS?
| 技术 | 选择理由 |
|---|---|
| React | 生态最成熟,组件化开发,社区资源丰富 |
| TypeScript | 类型安全,IDE 智能提示,减少运行时错误 |
| Vite | 开发服务器启动快(秒级),HMR 热更新体验好 |
| Tailwind CSS | 原子化 CSS,快速开发,无需写 CSS 文件 |
效果展示
简历与面试
简历库:

简历上传分析:

简历分析详情:

面试记录:

面试详情:

模拟面试:

知识库
知识库管理:

问答助手:



