面灵AI→

百度后端开发一面凉经 全程 AI 八股

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 为什么使用 Spring AI Alibaba 或者 Spring 的框架来做 AI 智能体?
  2. ReAct 模式的核心原理是什么?如何避免一直循环?
  3. ReAct 和 Plan、Execute、RePlan 模式有什么区别?各自适用于什么场景?
  4. 要查询数据库状态你会选择哪种模式?
  5. Function Calling 的原理了解吗?JSON Schema 起到什么作用?
  6. 假如 SSE 流式传输只返回了一半的 JSON,要不要执行这个工具?或者说你会采取什么策略来应对这种未知情况?
  7. 你使用到了多 Agent,为什么?单个 Agent 加多个工具不行吗?有什么优势?
  8. 上下文超了如何解决?
  9. 任务很复杂,需要拆分为多个子任务,子任务中有难有易,如何派发给子 Agent 执行?如何进行多个 Agent 之间的协调?
  10. 子 Agent 工作应该需要给哪些信息?
  11. 工具很多导致上下文被挤占,如何解决?
  12. 用的什么模型?有做过模型间的横向对比吗?
  13. RAG 是为了解决什么问题?web search、web fetch 等工具也可以获取新知识,为什么一定要 RAG?
  14. RAG 为什么要用向量数据库?MySQL 这种不行吗?
  15. 语义查询的底层原理讲一下。
  16. 抽奖算法如何实现的?
  17. 为什么要使用 Lua 脚本?起什么作用?
  18. HashMap 底层实现原理是什么?数据结构是怎么样的?是否线程安全?不使用 ConcurrentHashMap、HashTable 这种已有实现,你会如何改进现有的 HashMap 实现线程安全?
  19. Java 的垃圾回收机制。
  20. 缓存三兄弟。
  21. RabbitMQ 和 Kafka 的区别,两边介绍一下。
  22. 手撕:矩阵螺旋输出(Leetcode 54 变体,需反转方向)。

《参考解析》

ReAct 与 Plan-Execute-RePlan 的取舍:ReAct 是「推理—行动」交替的循环:模型先输出一段思考,再选择工具并给出参数,执行结果作为观察回填上下文,然后继续,直到它认为可以给最终答案。关键点是这个循环由你的代码控制,终止条件不能只靠模型自觉:要设最大步数上限、检测重复动作(同一工具加同一参数连续出现就打断并提示)、检测无进展(连续 N 步没有新信息)、限制总 token 与总耗时,另外只读工具可以放宽、有副作用的工具要限制调用次数。Plan-Execute-RePlan 是先一次性产出完整计划再逐步执行,执行中偏差过大才重新规划,适合步骤多、依赖关系明确、需要给用户展示进度的任务;ReAct 更适合下一步取决于上一步结果的探索型任务。至于「查数据库状态」,这是一次确定性的事实查询,最优解是直接一次工具调用(甚至是参数化的固定 SQL),用 ReAct 会多烧几轮 token 还可能查错;只有当状态需要多条件组合、或查询结果决定后续动作时,才值得上 Plan 先排好顺序。

Function Calling 与「半截 JSON」:模型自己不执行任何东西。Function Calling 是把可用函数的名称、参数结构与用途说明随请求一起发给模型,模型返回一个结构化的调用意图(函数名 + 参数 JSON),由你的代码解析、校验、真正执行,再把结果回填成一条消息。JSON Schema 干三件事:约束模型的生成空间(字段名、类型、枚举、必填项,减少它编造字段)、作为服务端入参校验的依据、同时也是给模型的文档——description 写得准不准,直接决定它选工具和填参数的准确率。流式 SSE 下工具参数的 JSON 是分片到达的,流中断在半截 JSON 时绝对不能执行:可能缺必填字段、也可能某个字符串被截断,执行等于拿幻觉参数去改数据。正确策略是按工具调用 id 把分片缓冲起来,用增量 JSON 解析器判断是否已完整闭合,只有解析成功且通过 Schema 校验才执行;不完整就按可重试错误处理(重新发起该轮,或降级为让模型重新输出参数),并且对有副作用的工具带幂等键,防止重试造成重复写入。

上下文超限与工具过多:四条路可以组合用。第一,压缩历史:把早期对话结构化摘要(目标、结论、未决事项),丢掉原文。第二,裁剪输入:工具返回只保留需要的字段、大文件分页读、长文本先摘要再进上下文,别把整份日志塞进去。第三,外置记忆:事实与知识写进向量库或文件,按需检索注入,而不是常驻上下文。第四,从工具侧治理:把工具按场景分组、按意图动态注册(先判断用户想干什么,只把候选工具给模型),或者把一批同质工具收敛成一个带 action 参数的统一入口,压缩工具定义占用的系统提示空间。更彻底的做法是用子 Agent 做隔离——把「工具多」这件事关进子上下文,主 Agent 只看到子 Agent 的结论,这样主链路的上下文增长与工具数量解耦。

多 Agent 的收益与派发:单 Agent 加多工具在工具数量上去之后有两个硬伤:工具定义把上下文吃掉,导致选错工具;长任务里中间结果污染上下文,主目标被冲淡。多 Agent 用「上下文隔离 + 职责拆分」换这两个问题,主 Agent 负责规划与汇总,子 Agent 各带一小撮工具和专属提示词。派发时不要平均分配:简单子任务交给便宜快的小模型或干脆用固定脚本,难题交给强模型并允许多轮尝试,路由依据是任务类型加预估复杂度。给子 Agent 的信息必须自包含:目标与验收标准、必要的背景事实、可用工具的边界、输出格式,以及步数与时间预算,同时明确它不能做什么(权限范围)。协调靠共享产物(文件、结构化黑板)而不是让 Agent 之间自由聊天——自由聊天会把隔离又破坏掉;主 Agent 负责校验每个子结果、决定重派还是采纳。

HashMap 的线程安全改造:HashMap 是数组 + 链表 + 红黑树:按 hash 定位桶,冲突挂链;链表长度到 8 且数组容量 ≥ 64 时转红黑树,元素降到 6 退回链表;扩容翻倍,JDK 8 用高低位拆分避免重算 hash。它线程不安全:并发 put 会丢更新(两个线程读到同一个桶头再各自挂链),JDK 7 的头插在并发扩容时还可能成环导致 CPU 打满,JDK 8 改尾插解决了环但依然会丢数据。如果不用现成并发容器而要自己改造,思路是拆锁粒度 + 保证可见性:要么分段锁(每段一把锁,等价于自己实现一遍 JDK 7 的 ConcurrentHashMap),要么 CAS 加只锁单个桶头(JDK 8 的做法),桶头与节点 next 用 volatile 保证可见性,扩容时用状态位做协作式迁移(多线程一起搬)。面试想听的是你理解「锁粒度」和「可见性」这两个要害,答的时候要顺带说清自研的代价——内存开销、复杂度、扩容协作难写难测,所以生产上直接用 ConcurrentHashMap 更划算(读基本无锁,写只锁桶)。