本轮 AI+Java中级开发面试题库(带答案)
【面试题】1. 请解释Transformer架构中的Self-Attention机制原理,以及在GPT系列模型中是如何应用的?
【标准答案】Self-Attention是Transformer的核心创新。简单说,它让模型在处理每个词时,能同时关注句子中所有其他词,计算它们之间的关联度。
具体原理:输入序列经过三个线性变换得到Query、Key、Value三个矩阵。Query和Key做点积计算注意力分数,除以√d_k防止梯度消失,再经过Softmax归一化得到注意力权重。最后用这个权重对Value加权求和,得到当前词的上下文表示。
GPT系列用的是Masked Self-Attention,在训练时遮住当前位置后面的词,确保模型只能看到历史信息,这样才能做自回归生成。GPT-3之后还引入了稀疏注意力、多查询注意力等优化,减少计算量同时保持性能。
实际应用中,Self-Attention让模型能够捕捉长距离依赖,比RNN的序列处理效率高很多,可以并行计算,这是大模型能scaling up的关键基础。
【面试题】2. 在实际项目中,如何设计一个高质量的Prompt来提升大模型的输出效果?
【标准答案】高质量Prompt设计有五个核心原则:
一是角色定位:给模型明确身份,比如"你是一位有10年经验的Java架构师",让输出更专业。
二是任务拆解:复杂任务分成多个步骤,用"第一步...第二步..."引导模型逐步思考,Chain-of-Thought的效果已经被证明很有效。
三是示例引导:Few-shot learning,给2-3个输入输出示例,模型能快速理解你的预期格式和风格。
四是约束条件:明确输出格式、长度限制、禁止事项,比如"用JSON格式返回,不超过200字,不要解释原理"。
五是迭代优化:第一次效果不好很正常,根据输出调整措辞、增加背景信息、调整示例。
实际项目中,我会建立一个Prompt模板库,按场景分类(代码生成、文档总结、数据分析等),不断迭代优化。好的Prompt能将模型准确率从60%提升到90%以上,这是性价比最高的优化手段。
【面试题】3. RAG检索增强生成的核心流程是什么?如何解决检索内容不相关的问题?
【标准答案】RAG核心流程分三步:文档切分与向量化存入向量库 → 用户问题向量化检索相似文档 → 将检索内容作为上下文送入大模型生成答案。
检索不相关的问题很常见,我有几个实战解决方案:
检索优化层面:
- 混合检索:向量检索+关键词检索(BM25)结合,各取所长
- 重排序:先用粗检索召回Top 100,再用Cross-Encoder精排到Top 5
- 查询改写:用户问题太模糊时,让LLM先改写或扩展成多个相关查询
文档处理层面:
- 切分策略:按语义边界切分而不是固定长度,保持内容完整性
- 元数据过滤:加上时间、部门、文档类型等元数据,检索时先过滤再排序
生成层面:
- 提示词约束:告诉模型"仅基于检索内容回答,不确定就说不知道"
- 置信度判断:让模型对答案置信度打分,低于阈值转人工
我在一个企业知识库项目中,通过混合检索+重排序,检索准确率从65%提升到88%。
【面试题】4. 请解释AI Agent的ReAct框架原理,以及在实际项目中如何实现工具调用?
【标准答案】ReAct是Reasoning + Acting的缩写,核心思想是让模型交替进行思考和行动。模型先分析问题(Thought),决定下一步行动(Action),执行工具获得观察结果(Observation),再基于结果继续思考,循环直到得出最终答案。
实现流程:用户问题 → LLM生成Thought和Action决策 → 执行器调用对应工具 → 工具返回Observation → LLM继续推理 → 重复直到得出Final Answer。
实际项目中,我会这样实现:
工具定义:每个工具包含name、description、parameters schema,让模型知道何时调用、传什么参数。比如搜索工具、数据库查询工具、计算器工具。
Prompt模板:设计包含工具列表、历史轨迹的System Prompt,引导模型按ReAct格式输出。
执行器实现:解析模型输出的Action,路由到对应工具,捕获异常并返回友好的Observation。
循环控制:设置最大步数(比如10步),防止死循环;设置超时机制。
LangChain的Agent就是ReAct的经典实现,Spring AI也在快速跟进。实际效果取决于工具定义的清晰度和模型推理能力,GPT-4效果明显好于开源小模型。
【面试题】5. 在企业级应用中,Spring AI和LangChain分别有什么优势?如何选择?
【标准答案】两个框架各有侧重,选择取决于技术栈和需求:
LangChain优势:
- 生态成熟,组件丰富:向量库集成、文档加载器、各种工具开箱即用
- 社区活跃,文档完善,踩坑经验多
- 支持Python和JS,适合AI团队快速原型开发
- 高级特性多:Agent、Graph、Memory管理都很成熟
Spring AI优势:
- Java生态原生集成,Spring Boot开发者零学习成本
- 统一的API抽象,底层可切换OpenAI、Azure、Ollama等模型
- 与Spring生态无缝衔接:配置管理、依赖注入、AOP都可以用
- 企业级特性:Observability、Security集成方便
选择建议:
- 团队主力是Java,现有系统是Spring生态 → 选Spring AI
- AI团队独立开发,需要快速迭代和丰富组件 → 选LangChain Python版
- 全栈团队,后端Java+前端Node → 可以都用,LangChain JS版本
实际项目中,我用Spring AI重构过一个Python写的AI服务,代码量减少40%,运维成本降低,因为可以直接复用现有的Spring监控、配置中心、服务注册体系。
【面试题】6. Java中ConcurrentHashMap在JDK 1.7和JDK 1.8中的实现有什么区别?为什么这样改进?
【标准答案】这是经典的并发优化演进案例:
JDK 1.7实现:
- 分段锁(Segment)设计,默认16个Segment,每个Segment继承ReentrantLock
- 并发度受Segment数量限制,锁粒度较粗
- Hash冲突用链表解决
JDK 1.8实现:
- 取消Segment,改用CAS + synchronized锁单个Node
- 锁粒度更细,并发度理论上是数组长度
- Hash冲突时,链表长度>8转红黑树,查询从O(n)优化到O(log n)
为什么改进:
- Segment继承ReentrantLock,每次put都要创建Segment对象,内存开销大
- 分段锁在高并发场景下,不同key可能落在同一Segment,仍然竞争
- CAS+synchronized在低竞争时性能更好,synchronized在JDK 6之后做了大量优化
性能对比:JDK 1.8的实现在高并发写入场景下吞吐量提升明显,特别是数据量大、Hash分散时。我在压测中看到,100线程并发写入,JDK 1.8比1.7快30%左右。
【面试题】7. JVM的垃圾收集器G1和CMS有什么区别?生产环境如何选择?
【标准答案】两者都是追求低延迟的收集器,但设计思路不同:
CMS(Concurrent Mark Sweep):
- 关注点是降低STW时间,标记和清除大部分并发执行
- 四个阶段:初始标记、并发标记、重新标记、并发清除
- 缺点明显:CPU敏感、浮动垃圾、内存碎片、并发失败会退化为Serial Old
- JDK 9之后标记废弃,JDK 14移除
G1(Garbage First):
- Region分区设计,将堆划分成多个大小相等的Region
- 增量回收:每次只收集部分Region,控制停顿时间
- 标记整理算法,没有内存碎片
- 可预测停顿时间:-XX:MaxGCPauseMillis=200(默认200ms)
生产选择:
- JDK 8及以下,堆<4GB → CMS可能更合适(虽然已废弃)
- JDK 9+,或者堆>4GB → G1是首选
- 对延迟要求极高(<100ms)→ 考虑ZGC或Shenandoah
实际经验:我们有个服务迁移到G1后,配置了MaxGCPauseMillis=100,Young GC稳定在80ms左右,Full GC几乎消失。G1的缺点是内存占用比CMS略高,因为有Region管理开销。
【面试题】8. MySQL的InnoDB为什么使用B+树作为索引结构?与B树、Hash索引相比有什么优势?
【标准答案】这是考察对索引底层原理的理解:
为什么选B+树:
- B+树所有数据存在叶子节点,非叶子节点只存索引,单页能存更多key,树更矮
- 叶子节点用指针串联成双向链表,范围查询效率高
- 高度通常3-4层就能存千万级数据,磁盘IO次数可控
- 适合磁盘存储,单次IO读取一页(16KB),减少随机IO
对比B树:
- B树每个节点都存数据,单页存key少,树更高,IO次数多
- B树范围查询需要中序遍历,B+树直接遍历叶子链表
- B树适合内存数据库,B+树适合磁盘数据库
对比Hash索引:
- Hash索引等值查询O(1),但无法支持范围查询、排序、模糊匹配
- Hash冲突时性能不稳定
- Memory引擎默认Hash,InnoDB也有自适应Hash索引(只对等值查询优化)
实际场景:InnoDB的主键索引是聚簇索引,叶子存完整行数据;辅助索引叶子存主键值,查询需要回表。所以主键尽量用自增整数,避免页分裂;辅助索引建在查询频繁、区分度高的列。
【面试题】9. 在微服务架构中,如何保证分布式事务的一致性?说说你用过的方案。
【标准答案】分布式事务是CAP理论中AP和CP权衡的经典问题,生产中有几种主流方案:
1. 2PC/XA(两阶段提交):
- Prepare → Commit两阶段,强一致性但性能差
- 适合单体应用跨库事务,不适合高并发微服务
- Seata的AT模式就是改进版XA,性能好一些
2. TCC(Try-Confirm-Cancel):
- 业务层面实现三个接口,柔性事务
- Try预留资源,Confirm确认提交,Cancel回滚释放
- 适合金融、支付等对一致性要求高的场景
- 缺点是业务侵入性强,每个接口都要写三份逻辑
3. 本地消息表 + 最终一致性:
- 本地事务写业务表+消息表,定时任务扫描消息表发送MQ
- 下游消费MQ执行业务,幂等处理
- 适合对实时性要求不高、但可靠性要求高的场景
- 我在订单-库存场景用过这个方案,实现简单但有一定延迟
4. Saga模式:
- 长事务拆成多个本地事务,每个事务有补偿动作
- 某步失败时反向执行补偿
- Seata的Saga模式就是实现,适合跨多个服务的长流程
实战选择:一般业务用本地消息表+MQ就够;金融核心用TCC;跨系统长流程用Saga。强一致性场景尽量在设计层面避免分布式事务,比如合并服务、数据冗余。
【面试题】10. Spring Cloud Gateway如何实现动态路由?在实际项目中如何保证网关的高可用?
【标准答案】Spring Cloud Gateway基于WebFlux,路由配置可以动态更新,核心是RouteDefinitionLocator接口。
动态路由实现:
方案一:配置中心(Nacos/Apollo)
- 路由规则存配置中心,ConfigService监听配置变更
- 配置更新时,调用Gateway的refresh事件重新加载路由
- 实时性好,但配置中心故障会影响网关
方案二:数据库 + 定时任务
- 路由规则存数据库,提供管理界面增删改
- 定时任务或事件监听刷新路由
- 可控性强,适合企业内部管理
方案三:自定义RouteDefinitionLocator
- 实现接口,从Redis/MongoDB等存储读取路由
- 结合Caffeine本地缓存,减少存储压力
- 灵活性最高,但开发成本大
高可用保障:
- 多实例部署 + Nginx负载均衡,至少3个实例
- 网关无状态,Session存在Redis,任意实例挂掉不影响
- 熔断降级:集成Sentinel/Hystrix,后端服务异常时快速失败
- 限流:令牌桶/漏桶算法,防止流量洪峰打垮后端
- 健康检查:Spring Boot Actuator + K8s探针,异常实例自动摘除
实际经验:我们用Nacos做动态路由,网关集群3节点,QPS峰值5万+,平均延迟<50ms。关键是做好监控:路由命中率、后端服务响应时间、熔断次数,提前发现瓶颈。