AI+Java中级开发面试题库
生成时间:2026-05-20 23:52 题目数量:10道 覆盖范围:AI方向(大模型、RAG、提示词工程、AI Agent、向量数据库)、Java中级(JUC并发、JVM、Spring Cloud、Redis、MySQL)
【面试题】1. Transformer的自注意力机制是如何计算的?为什么它能解决NLP中的长距离依赖问题?
【标准答案】自注意力的核心计算分三步:
第一,将输入向量通过三个权重矩阵变换为Query、Key、Value三个矩阵。
第二,计算注意力分数:Attention(Q,K,V) = softmax(QK^T/√d_k)V。这里除以√d_k是为了防止点积过大导致softmax梯度消失。
第三,输出就是加权后的Value。
为什么能解决长距离依赖?传统RNN是序列计算,后面的词必须等前面的词处理完,距离远的词之间信息传递会逐层衰减。而自注意力机制中,每个词都可以直接和序列中其他所有词计算关联,一步到位,不存在信息传递距离的问题。比如"他昨天在图书馆借的书今天终于读完了","他"和"读"之间的语义关系,自注意力能直接捕捉,不需要经过中间所有词的传递。
实际项目中,我做过一个长文本摘要模型,用BERT提取文档特征时,2000字的文章,开头的实体和结尾的结论之间能建立直接的注意力关联,摘要质量比LSTM提升了15%。
【面试题】2. RAG系统中文档切分策略对检索效果有什么影响?如何选择合适的chunk大小?
【标准答案】文档切分是RAG系统最容易被忽视但影响巨大的环节。切分不好会直接导致检索召回率下降和回答幻觉。
切分太小的风险:语义不完整。比如把"虽然这个方案成本低,但是性能不如方案B"切成两段,检索时只召回后半段,模型会得出错误的结论。实际项目中,我看到过chunk小于100字时,召回率下降30%以上。
切分太大的风险:检索精度下降。一个chunk包含多个主题,向量会被稀释,检索时容易引入噪音。而且超出模型上下文窗口会造成截断。
我的实践经验:首选语义切分而非固定长度切分。按段落、章节、句子边界切分,保证语义完整性。通用场景chunk大小控制在300-500字,重叠50-100字。技术文档可以更大,800-1000字。问答对这种短内容可以直接作为chunk。
另外,层级索引是个很好的优化:先用父文档做粗粒度检索,定位后再加载子文档做精细检索。我们的知识库系统用这个方案,检索准确率从72%提升到89%。
【面试题】3. 什么是Chain-of-Thought提示技术?它在什么场景下效果最明显?能否举个实际例子?
【标准答案】Chain-of-Thought(思维链)就是在提示词中引导模型一步步推理,而不是直接给出答案。核心思想是:把复杂问题拆解成多个简单步骤,让模型显式地展示中间推理过程。
效果最明显的场景:数学计算、逻辑推理、复杂决策、多步骤任务。这些问题需要中间推理,直接给答案容易出错。
举个实际例子:我们做过一个订单异常诊断系统。用户投诉"订单状态异常"。
不用CoT的提示词:
判断这个订单是否异常,异常类型是什么。模型经常直接乱猜一个类型。
用CoT的提示词:
请按以下步骤分析订单:
1. 检查订单当前状态和支付状态是否一致
2. 检查库存扣减记录是否存在
3. 检查物流状态更新时间是否合理
4. 根据以上分析,给出异常类型判断准确率从67%提升到91%。关键是让模型的推理过程可见、可验证,而且每一步都是简单判断,减少出错概率。
实际应用中,我发现Zero-shot CoT加一句"让我们一步步思考"就能在很多场景下有10-20%的提升。
【面试题】4. AI Agent中ReAct模式的执行流程是什么?如何设计才能避免陷入无限循环?
【标准答案】ReAct = Reasoning + Acting,核心思想是把思考和行动交织进行,而不是一次性规划完所有步骤。
执行流程:
- Thought:模型分析当前状态,决定下一步做什么
- Action:执行具体的工具调用(搜索、查API、读文件等)
- Observation:获取工具返回的结果
- 循环:基于Observation继续Thought,直到得出最终答案
举例:用户问"今天北京天气怎么样,适合户外跑步吗?"
- Thought:需要获取北京天气信息
- Action:调用天气API,参数city=北京
- Observation:温度18度,湿度65%,空气质量良
- Thought:数据已获取,判断是否适合跑步
- Action:根据温度、湿度、空气质量给出建议
- Final Answer:适合跑步,建议...
避免无限循环的设计:
- 最大步数限制:我一般设置10-15步上限
- 重复检测:如果连续两次调用相同工具、相同参数,强制退出
- 进度评估:每隔几步让模型判断"是否接近答案"
- Fallback机制:超时或循环时,用当前信息给出一个答案
我们做的客服Agent系统,加上这些保护机制后,任务完成率从78%提升到95%,而且基本消除了卡死问题。
【面试题】5. 向量数据库中HNSW和IVF索引有什么区别?在实际项目中如何选择?
【标准答案】这是两种主流的向量索引算法,核心区别在于构建方式和查询效率。
HNSW(Hierarchical Navigable Small World):
- 基于图的索引,构建一个多层导航结构
- 查询时从顶层粗粒度搜索,逐层下沉到精细匹配
- 优点:查询速度快(O(log n)),召回率高,不需要训练
- 缺点:构建内存占用大,索引文件较大
IVF(Inverted File Index):
- 基于聚类的索引,先用K-means把向量分成nlist个簇
- 查询时只搜索最近的nprobe个簇
- 优点:内存占用小,支持增量更新,索引文件小
- 缺点:需要训练聚类模型,召回率取决于nprobe设置
选型建议:
- 数据量小于100万、追求高召回:选HNSW
- 数据量大于1000万、内存受限:选IVF
- 需要频繁增量更新:选IVF
- 对延迟敏感(如实时推荐):选HNSW
我们项目的实际经验:知识库系统用Milvius,500万向量,HNSW索引,查询延迟在10ms以内,召回率92%。如果用IVF,同样召回率下延迟要到30-50ms。但另一个日志检索项目,数据量8000万,内存不够只能用IVF,nprobe设为64,延迟50ms,召回率88%。
【面试题】6. CompletableFuture如何实现多任务的异步编排?在实际项目中如何优雅处理异常?
【标准答案】CompletableFuture是Java 8引入的异步编程工具,核心优势是支持链式调用和多任务编排。
常用编排方法:
thenApply():前一个任务完成后,对结果进行转换(有返回值)thenAccept():前一个任务完成后,消费结果(无返回值)thenCompose():前一个任务完成后,执行另一个异步任务(扁平化)thenCombine():两个任务都完成后,合并结果allOf():所有任务完成anyOf():任一任务完成
实际项目案例:电商订单详情聚合
CompletableFuture<OrderInfo> orderFuture =
CompletableFuture.supplyAsync(() -> orderService.getOrder(orderId));
CompletableFuture<UserInfo> userFuture =
CompletableFuture.supplyAsync(() -> userService.getUser(userId));
CompletableFuture<List<Item>> itemsFuture =
CompletableFuture.supplyAsync(() -> itemService.getItems(orderId));
CompletableFuture<OrderDetail> result = CompletableFuture
.allOf(orderFuture, userFuture, itemsFuture)
.thenApply(v -> {
OrderDetail detail = new OrderDetail();
detail.setOrder(orderFuture.join());
detail.setUser(userFuture.join());
detail.setItems(itemsFuture.join());
return detail;
});异常处理三要素:
exceptionally():捕获异常,返回默认值handle():无论成功失败都执行,可以统一处理- 指定线程池:默认ForkJoinPool不适合IO密集型任务
result.exceptionally(ex -> {
log.error("聚合订单详情失败", ex);
return OrderDetail.empty(); // 降级返回
});我们系统的订单详情接口,原来串行调用耗时800ms,改成CompletableFuture并行后降到300ms,QPS提升了2倍多。
【面试题】7. G1垃圾收集器的Region划分有什么特点?Mixed GC的触发条件是什么?
【标准答案】G1是JDK 9后的默认GC,核心思想是把堆划分成多个大小相等的Region(默认2048个),每个Region可以是Eden、Survivor、Old或Humongous。
Region划分特点:
- 逻辑分代,物理不分代:不再要求Eden、Survivor、Old连续,可以交错分布
- 可预测停顿:通过停顿预测模型,控制每次GC的停顿时间(默认200ms)
- 大对象特殊处理:超过Region 50%的对象直接分配到Humongous区
- 优先回收垃圾最多的Region:这就是G1名字的来源(Garbage First)
Mixed GC触发条件:
- Old区占用达到IHOP阈值(默认堆的45%)
- 系统判断需要做Mixed GC才能满足下次停顿目标
- 执行过程:先Young GC → 标记并发 → Mixed GC(回收部分Old Region)
和CMS的关键区别:
- CMS用标记-清除,有内存碎片问题
- G1用标记-整理(Region内),碎片可控
- G1的Mixed GC是增量式的,每次只回收部分Old Region
调优经验:
-XX:MaxGCPauseMillis=200:根据业务容忍度调整-XX:InitiatingHeapOccupancyPercent=45:老年代占用阈值,调高可减少Mixed GC频率-XX:G1HeapRegionSize:大对象多的场景适当增大
我们的支付系统,用G1替代CMS后,Full GC频率从每天2次降到每周1次,P99延迟从500ms降到150ms。
【面试题】8. Sentinel限流的核心原理是什么?滑动窗口和漏桶算法各有什么优缺点?
【标准答案】Sentinel是阿里开源的流量控制组件,核心是滑动时间窗口算法实现实时统计,再结合规则判断是否限流。
滑动窗口原理:
- 把时间分成多个格子(比如1秒分成10个100ms的格子)
- 维护一个环形数组,每个格子记录通过请求数
- 计算QPS时,取最近N个格子的请求总和
- 随着时间推进,窗口向前滑动,旧的格子被覆盖
漏桶算法原理:
- 请求像水一样流入桶中
- 桶以固定速率漏水(处理请求)
- 桶满了就丢弃(限流)
- 本质是恒定速率输出
对比分析:
| 维度 | 滑动窗口 | 漏桶 |
|---|---|---|
| 精度 | 高(毫秒级) | 低(只能恒定速率) |
| 内存 | 需维护多个格子 | 只需一个计数器 |
| 突发流量 | 允许(窗口内可聚集) | 严格限制 |
| 适用场景 | API网关、通用限流 | 削峰填谷、保护数据库 |
Sentinel的选择: 默认用滑动窗口,因为API场景下用户请求往往有突发性,漏桶会过多拒绝正常请求。但Sentinel也支持其他模式:
- QPS模式:滑动窗口
- 并发线程数模式:直接计数
- 匀速排队模式:漏桶(Warm Up是令牌桶的变种)
实际项目中,我们的网关用Sentinel的QPS模式,单机限流设为500,超过去排队等待,P95延迟增加了20ms但可用性大幅提升。
【面试题】9. Redis分布式锁用SETNX实现时有什么问题?如何设计一个可靠的分布式锁方案?
【标准答案】直接用SETNX有几个经典问题:
问题1:死锁 客户端获取锁后崩溃,锁无法释放。解决方案:设置过期时间。但要SETNX和EXPIRE是两条命令,不是原子的。
问题2:SETNX+EXPIRE原子性问题 两条命令之间崩溃,锁没有过期时间。解决方案:Redis 2.6.12后支持SET key value NX EX seconds,一条命令完成。
问题3:锁误删 A获取锁,业务执行太久,锁过期自动释放。B获取锁。A执行完释放锁,把B的锁删了。解决方案:锁的value设为唯一标识(如UUID),释放时校验。
问题4:单点故障 Redis主节点挂了,锁信息丢失。主从切换后可能多个客户端同时获取锁。
可靠方案:Redisson实现
RLock lock = redissonClient.getLock("order:123");
try {
// 尝试获取锁,最多等待3秒,锁自动过期10秒
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 执行业务
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}Redisson的优势:
- Watch Dog机制:后台线程自动续期,防止业务未完成锁过期
- 可重入:同一线程可多次获取
- 主从一致性:RedLock算法,向多个节点申请锁
高可靠场景:用RedLock,向5个独立的Redis节点申请锁,获得3个以上就认为成功。我们的库存扣减服务用这个方案,即使2个节点故障,锁依然安全。
【面试题】10. MySQL的B+树索引结构有什么特点?联合索引为什么要遵循最左匹配原则?
【标准答案】MySQL InnoDB用B+树作为索引结构,有这些关键特点:
B+树特点:
- 非叶子节点不存数据,只存指针,一个节点能存更多key,树更矮
- 叶子节点用双向链表连接,范围查询效率高(找到起点后顺序扫描)
- 聚簇索引:主键索引的叶子节点存完整行数据
- 二级索引:叶子节点存主键值,需要回表查询
最左匹配原则本质: 联合索引(a,b,c),B+树先按a排序,a相同再按b排序,b相同再按c排序。查询时必须从左边开始连续匹配。
-- 能走索引
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3
WHERE a = 1 AND b > 2 -- 范围查询后无法再用c
-- 不能走索引(或只能部分走)
WHERE b = 2 -- 跳过了a,无法使用索引
WHERE a = 1 AND c = 3 -- 跳过了b,c无法走索引实际设计原则:
- 等值条件放前面,范围条件放后面
- 区分度高的列放前面,过滤更多数据
- 覆盖索引:查询列都在索引中,避免回表
优化案例: 订单表查询:WHERE user_id = ? AND status = ? AND create_time > ? ORDER BY create_time LIMIT 10
原索引(user_id, status, create_time)可以,但如果status区分度低(只有几种状态),考虑调整:
- 高频查询都是某状态:建(status, user_id, create_time)索引
- 用户量小状态多:保持原索引
我们系统的订单列表页,通过调整联合索引顺序,查询从800ms降到50ms。
本轮题目覆盖:
- AI方向:Transformer原理、RAG切分策略、提示词工程、AI Agent架构、向量数据库索引
- Java方向:异步并发、JVM垃圾收集、限流算法、分布式锁、MySQL索引优化