Skip to content

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,核心思想是把思考和行动交织进行,而不是一次性规划完所有步骤。

执行流程:

  1. Thought:模型分析当前状态,决定下一步做什么
  2. Action:执行具体的工具调用(搜索、查API、读文件等)
  3. Observation:获取工具返回的结果
  4. 循环:基于Observation继续Thought,直到得出最终答案

举例:用户问"今天北京天气怎么样,适合户外跑步吗?"

  • Thought:需要获取北京天气信息
  • Action:调用天气API,参数city=北京
  • Observation:温度18度,湿度65%,空气质量良
  • Thought:数据已获取,判断是否适合跑步
  • Action:根据温度、湿度、空气质量给出建议
  • Final Answer:适合跑步,建议...

避免无限循环的设计:

  1. 最大步数限制:我一般设置10-15步上限
  2. 重复检测:如果连续两次调用相同工具、相同参数,强制退出
  3. 进度评估:每隔几步让模型判断"是否接近答案"
  4. 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():任一任务完成

实际项目案例:电商订单详情聚合

java
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;
    });

异常处理三要素:

  1. exceptionally():捕获异常,返回默认值
  2. handle():无论成功失败都执行,可以统一处理
  3. 指定线程池:默认ForkJoinPool不适合IO密集型任务
java
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划分特点:

  1. 逻辑分代,物理不分代:不再要求Eden、Survivor、Old连续,可以交错分布
  2. 可预测停顿:通过停顿预测模型,控制每次GC的停顿时间(默认200ms)
  3. 大对象特殊处理:超过Region 50%的对象直接分配到Humongous区
  4. 优先回收垃圾最多的Region:这就是G1名字的来源(Garbage First)

Mixed GC触发条件:

  1. Old区占用达到IHOP阈值(默认堆的45%)
  2. 系统判断需要做Mixed GC才能满足下次停顿目标
  3. 执行过程:先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实现

java
RLock lock = redissonClient.getLock("order:123");
try {
    // 尝试获取锁,最多等待3秒,锁自动过期10秒
    if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
        // 执行业务
    }
} finally {
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

Redisson的优势:

  1. Watch Dog机制:后台线程自动续期,防止业务未完成锁过期
  2. 可重入:同一线程可多次获取
  3. 主从一致性:RedLock算法,向多个节点申请锁

高可靠场景:用RedLock,向5个独立的Redis节点申请锁,获得3个以上就认为成功。我们的库存扣减服务用这个方案,即使2个节点故障,锁依然安全。


【面试题】10. MySQL的B+树索引结构有什么特点?联合索引为什么要遵循最左匹配原则? ​

【标准答案】MySQL InnoDB用B+树作为索引结构,有这些关键特点:

B+树特点:

  1. 非叶子节点不存数据,只存指针,一个节点能存更多key,树更矮
  2. 叶子节点用双向链表连接,范围查询效率高(找到起点后顺序扫描)
  3. 聚簇索引:主键索引的叶子节点存完整行数据
  4. 二级索引:叶子节点存主键值,需要回表查询

最左匹配原则本质: 联合索引(a,b,c),B+树先按a排序,a相同再按b排序,b相同再按c排序。查询时必须从左边开始连续匹配。

sql
-- 能走索引
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无法走索引

实际设计原则:

  1. 等值条件放前面,范围条件放后面
  2. 区分度高的列放前面,过滤更多数据
  3. 覆盖索引:查询列都在索引中,避免回表

优化案例: 订单表查询: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索引优化