2026创新项目实训-个人博客(一)

张开发
2026/4/19 17:40:26 15 分钟阅读

分享文章

2026创新项目实训-个人博客(一)
引言本文针对“面试全流程”功能初步分析实现简单的“模拟一轮面试”功能的思路更多功能在之后进行完善。ChatClient统一的大模型调用接口Chatclient是Spring Al 提供的流式 API用于与大语言模型进行对话。在面试功能上以下场景使用 ChatClient:面试问题生成基于简历生成个性化面试问题含追问)面试评估评估面试回答并生成报告有状态会话管理从 Stateless 到 Stateful大语言模型LLM本身是无状态Stateless的每一轮交互默认不保留历史。但在面试场景中系统必须追踪以下上下文:候选人的简历背景;已提问数量与当前进度历史回答的逻辑连贯性。本项目采用Stateful Session模式通过外部存储RedisPostgreSQL维护会话快照避免在每轮对话中重复传输冗长的简历文本由业务层精准控制状态流转。有限状态机FSM面试生命周期被抽象为标准的有限状态机以确保操作的合法性与流程的确定性。状态转换的业务规则publicenumSessionStatus{CREATED,// 会话已创建IN_PROGRESS,// 面试进行中COMPLETED,// 面试已完成EVALUATED// 已生成评估报告}关键业务规则CREATED → IN_PROGRESS只能转换一次记录面试开始时间IN_PROGRESS → IN_PROGRESS允许用户跳题、修改答案每题独立提交IN_PROGRESS → COMPLETED用户主动交卷或全部题目答完COMPLETED → EVALUATED由异步任务触发不可逆问题生成策略题型分布算法问题生成采用加权分布算法确保技术栈覆盖均衡。ServicepublicclassInterviewQuestionService{// 问题类型权重分配按优先级privatestaticfinaldoublePROJECT_RATIO0.20;// 20% 项目经历privatestaticfinaldoubleMYSQL_RATIO0.20;// 20% MySQLprivatestaticfinaldoubleREDIS_RATIO0.20;// 20% RedisprivatestaticfinaldoubleJAVA_BASIC_RATIO0.10;// 10% Java基础privatestaticfinaldoubleJAVA_COLLECTION_RATIO0.10;// 10% 集合privatestaticfinaldoubleJAVA_CONCURRENT_RATIO0.10;// 10% 并发// Spring/SpringBoot 数量由总题数减去其余类型得出余额调整无单独比例常量privateQuestionDistributioncalculateDistribution(inttotal){intprojectMath.max(1,(int)Math.round(total*PROJECT_RATIO));intmysqlMath.max(1,(int)Math.round(total*MYSQL_RATIO));intredisMath.max(1,(int)Math.round(total*REDIS_RATIO));intjavaBasicMath.max(1,(int)Math.round(total*JAVA_BASIC_RATIO));intjavaCollection(int)Math.round(total*JAVA_COLLECTION_RATIO);intjavaConcurrent(int)Math.round(total*JAVA_CONCURRENT_RATIO);intspringtotal-project-mysql-redis-javaBasic-javaCollection-javaConcurrent;springMath.max(0,spring);returnnewQuestionDistribution(project,mysql,redis,javaBasic,javaCollection,javaConcurrent,spring);}privaterecordQuestionDistribution(intproject,intmysql,intredis,intjavaBasic,intjavaCollection,intjavaConcurrent,intspring){}}追问的数据模型设计追问系统采用线性展开策略将树状的问题结构主问题 追问扁平化为线性列表。问题Question的字段有“questionIndex”、 “question”、 “category”、“isFollowUp”、“parentQuestionIndex”通过isFollowUp标识是否为准问通过parentQuestionIndex关联主问题与追问。历史问题去重机制为避免用户多次面试时遇到重复问题系统实现了历史问题感知// InterviewSessionService#createSession当 request.resumeId() ! null 时ListStringhistoricalQuestionspersistenceService.getHistoricalQuestionsByResumeId(request.resumeId());ListInterviewQuestionDTOquestionsquestionService.generateQuestions(request.resumeText(),request.questionCount(),historicalQuestions);// InterviewPersistenceService#getHistoricalQuestionsByResumeIdListInterviewSessionEntitysessionssessionRepository.findTop10ByResumeIdOrderByCreatedAtDesc(resumeId);returnsessions.stream().map(InterviewSessionEntity::getQuestionsJson).filter(json-json!null!json.isEmpty()).flatMap(json-{ListInterviewQuestionDTOquestionsobjectMapper.readValue(json,newTypeReferenceListInterviewQuestionDTO(){});returnquestions.stream().filter(q-!q.isFollowUp())// 排除追问只保留主问题.map(InterviewQuestionDTO::question);}).distinct().limit(30)// 只保留最近 30 条主问题.toList();存储方案面试过程属于长耗时、高频交互场景系统设计了热冷分离的存储策略以平衡响应速度与可靠性。Redis 缓存面试进行中的中间状态如已填写的答案实时暂存在 Redis 中。将会话对象序列化为 JSON 存储。PostgreSQL 持久化当状态发生关键变更如初始化、交卷时将会话写入PostgreSQL数据库。数据优先写入 Redis快速响应用户同步尝试写入 PostgreSQLDB 写入失败时记录 WARN 日志不阻塞主流程缓存键设计ServicepublicclassInterviewSessionCache{/** * 缓存键前缀 */privatestaticfinalStringSESSION_KEY_PREFIXinterview:session:;/** * 简历ID到会话ID的映射前缀用于查找未完成会话 */privatestaticfinalStringRESUME_SESSION_KEY_PREFIXinterview:resume:;/** * 会话默认过期时间24小时 */privatestaticfinalDurationSESSION_TTLDuration.ofHours(24);privateStringbuildSessionKey(StringsessionId){returnSESSION_KEY_PREFIXsessionId;}privateStringbuildResumeSessionKey(LongresumeId){returnRESUME_SESSION_KEY_PREFIXresumeId;}/** * 根据简历ID查找未完成的会话ID断点续答核心方法 */publicOptionalStringfindUnfinishedSessionId(LongresumeId){StringkeybuildResumeSessionKey(resumeId);StringsessionIdredisService.get(key);// ... 验证逻辑}}提示词设计基于RISEN框架设计提示词# Role你是一位拥有10年以上经验的资深 Java 后端技术专家及大厂如阿里、腾讯、字节面试官。你擅长通过候选人的简历Resume精准定位其技术边界设计出既考察基础功底、又考察架构思维和底层原理的面试题目。# Task请根据用户提供的【候选人简历内容】生成一套针对性的面试问题集问题应覆盖简历中提及的核心技术栈并按照难度梯度进行合理分布。# Question Generation Standards (出题标准)1. **项目深度探测(PROJECT)** - 针对简历中提到的技术栈提问例如针对你项目中的分布式锁实现如何处理锁续期问题- 追问技术选型理由、遇到的挑战、解决方案 - 探测候选人在项目中的实际贡献度2. **技术链条追问** - 遵循使用经验 → 核心原理 → 边界/优化的递进顺序 - 例如HashMap 使用 → 红黑树转换机制 → 并发安全问题3. **难度分布要求** - 基础30%核心概念与常用 API考察技术广度 - 进阶50%底层实现、并发安全、性能瓶颈考察技术深度 - 专家20%架构选型对比、源码级理解、复杂故障排查# Question Types (问题分类)|类型|说明|出题重点||------|------|---------||PROJECT|项目经历|技术选型、架构设计、问题解决||JAVA_BASIC|Java 基础|面向对象、异常处理、JVM 原理||JAVA_COLLECTION|Java 集合|数据结构、源码实现、使用场景||JAVA_CONCURRENT|Java 并发|线程模型、锁机制、并发工具类||MYSQL|数据库|索引优化、事务隔离、SQL 调优||REDIS|缓存|数据结构、持久化、分布式锁||SPRING|Spring 框架|IoC/AOP 原理、Bean 生命周期||SPRING_BOOT|Spring Boot|自动配置、Starter 机制|# Constraints (重要约束)- 问题必须与简历内容高度相关严禁出现简历未涉及的技术栈 -type字段仅限上述8种枚举值 - 每道题目必须具有区分度避免过于简单或过于偏门 - 项目类问题必须结合简历中的具体项目描述 - 每个主问题都必须提供追问写入followUps字段数组# Output Format请直接输出一个 JSON 对象不要包含 Markdown 代码块标签如 json 。 JSON 结构必须严格包含以下字段 - questions: 对象数组每个对象包含 - question: 字符串面试问题内容必须是完整的问句 - type: 字符串问题类型必须是 PROJECT, JAVA_BASIC, JAVA_COLLECTION, JAVA_CONCURRENT, MYSQL, REDIS, SPRING, SPRING_BOOT 之一 - category: 字符串细分类别如Redis - 分布式锁、Java并发 - 线程池 - followUps: 字符串数组当前主问题的追问列表每条都必须是完整问句 注意只需要返回问题列表不需要包含 questionIndex、referenceAnswer、keyPoints、userAnswer、score、feedback 等字段。通过构造方法注入提示词模板publicInterviewQuestionService(ChatClient.BuilderchatClientBuilder,StructuredOutputInvokerstructuredOutputInvoker,Value(classpath:prompts/interview-question-system.st)ResourcesystemPromptResource,Value(classpath:prompts/interview-question-user.st)ResourceuserPromptResource,Value(${app.interview.follow-up-count:1})intfollowUpCount)throwsIOException{this.chatClientchatClientBuilder.build();this.structuredOutputInvokerstructuredOutputInvoker;this.systemPromptTemplatenewPromptTemplate(systemPromptResource.getContentAsString(StandardCharsets.UTF_8));this.userPromptTemplatenewPromptTemplate(userPromptResource.getContentAsString(StandardCharsets.UTF_8));this.outputConverternewBeanOutputConverter(QuestionListDTO.class);this.followUpCountMath.max(0,Math.min(followUpCount,MAX_FOLLOW_UP_COUNT));}

更多文章