面灵AI→

网易云音乐全栈一面:Agent 闭环与百万级遍历

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

《面试题目》

  1. 你的语言是 Python 还是 Java?
  2. 挑一个你觉得比较好的项目,再介绍一下。
  3. 闭环验证是怎么做到的?
  4. 如果用户点了不满意,你们后续会有什么措施?
  5. 意图识别,它识别的一定准确吗?
  6. 有没有存在用户就想一次性询问两个意图的情况?
  7. 你在调用这个 agent 的时候,会不会出现一些幻觉问题?
  8. 场景问题:有一个任务要拆成 N 个子任务并行执行,等 N 个子任务都执行完成了才能继续往下走(后端服务场景,子任务属于其他系统、耗时长),这个场景你觉得应该怎么设计?
  9. 讲详细一点,细化讲一下多个并行子任务全部结束后如何触发后续任务。
  10. 百万级别歌曲 ID 集合,判断传入的歌曲是否存在某标签,存在则走后续流程,该怎么设计?
  11. 一首歌曲进来,完整的处理流程是什么?要判断一定存在。
  12. 你对 JVM 了解吗?
  13. 垃圾回收器,你知道哪些呢?
  14. 双亲委派模型你知道吗?
  15. 双亲委派模型它可以被打破吗?
  16. 表包含歌曲 ID、版权 ID 和状态,查询指定版权方、状态为生效的所有歌曲 ID,写出查询语句并设计索引。
  17. 指定版权方下有几百万条歌曲,每次只能查询 1000 条,如何遍历拿到全部数据?该把哪个字段作为游标标准?
  18. 说一下 HashMap 的 1.7 和 1.8 的区别。
  19. HashMap 它是一个怎么样的结构?
  20. 你平时用 AI 是怎么辅助你的工作的呢?

《参考解析》

Agent 的闭环验证要讲成一条可追踪的链路。 主链路拆开是:接收输入 → 意图识别与路由 → 选择合适的工具或子 agent → 执行并汇总 → 生成回复 → 采集用户反馈。闭环验证相应地分两层:过程层校验工具调用是否正确(参数是否合法、是否越权、是否真的被调用、耗时与 token 消耗),结果层校验输出是否符合事实与格式(结构化字段能否解析、引用是否指向真实数据源、业务规则是否满足)。可验证的部分尽量写确定性断言,语义部分用模型评审加人工抽检校准;用户点「不满意」之后要有明确的分流:记录 badcase(带 trace、输入、检索片段、模型版本)→ 归因到具体环节 → 修复后回填评测集做回归,否则同一个问题会反复出现。要主动说清「哪一层错」的判据:召回内容里根本没有答案属于检索问题,召回里有正确片段但答错才是生成问题。

意图识别不可能百分之百准,工程上的答案是「知道它不准之后怎么办」。 常见手段有四个:一是收敛候选空间,先用规则或关键词召回一个小候选工具集,再让模型在候选里选,比让它在几十个工具里自由发挥稳得多;二是置信度阈值,低于阈值不进业务链路,改为澄清追问(「你是想查版权还是查歌曲状态?」);三是支持多意图拆解,用户一句话里两个诉求是常态,识别出多意图后拆成多个子任务分别执行再合并回答,或者按优先级确认先做哪一个;四是把识别与执行解耦,识别错了至少不要让执行产生破坏性副作用(写操作前二次确认、幂等键去重)。要能给出评测口径:意图分类的准确率与混淆矩阵、工具选择成功率、澄清率与澄清后的转正率。

幻觉治理的核心是「让模型只基于给定材料说话」。 手段按收益排序:把工具返回和检索结果作为唯一事实来源放进上下文,并要求引用来源;用结构化输出(JSON schema、function calling)约束格式,解析失败就重试而不是放行;对关键字段做程序校验(歌曲 ID、版权方是否存在,生成后再查一次);检索或工具没有返回时明确拒绝回答,宁缺勿编;把「不确定」表达出来(「我查到的是…,如果不对请补充」)。另外要区分幻觉与数据错误:模型基于过期的知识库正确生成,问题在数据而不在模型,所以评测集里要能把这两类分开统计。

N 个子任务并行、全部完成才继续,这题要分层给方案。 子任务在同进程内且都是短耗时的,用 CompletableFuture.allOf(...) 或协程/WaitGroup 聚合等待,注意线程池隔离(别把业务线程池打满)、异常处理(allOf 要逐个 join 取异常,否则失败会被吞掉)和超时(整体超时 + 单任务超时);子任务其实很长、还属于别的系统、可能跨进程甚至跨天,就必须把「等待」从内存里搬出来:建任务表(父任务 ID、子任务 ID、状态、重试次数、截止时间),每个子任务完成后回调或发消息,用状态机推进;「全部完成」用计数器(Redis 原子递减到 0)或对子任务状态做一次判定(count(未完成) = 0),满足条件才触发后续。要讲清工程细节:回调必须幂等(重复投递不能重复触发,用唯一键或状态 CAS),失败要有补偿与人工兜底(死信队列、对账任务扫描超时未完成的父任务),触发动作与状态更新要么在同一事务里、要么走可靠消息保证「至少一次 + 幂等」。追问「怎么细化触发」时,把「计数器归零 → 发一条『父任务可继续』的消息 → 消费者幂等消费 → 推进状态并执行后续」这条链路说完整,比只说「用回调」扎实得多。

百万级歌曲 ID 判断标签,先问清「必须精确还是可以近似」。 如果允许极小误判,布隆过滤器(或 Redis 的 bitmap + 分片)最省内存,但只能做前置过滤,命中后还要回源确认,而且不支持删除,得用计数式变体或定期重建;如果要求精确,按 ID 分片后用 Redis Set/bitmap(稀疏 ID 要先做映射,否则内存爆掉)或本地 RoaringBitmap 批量加载,判断走内存是 O(1) 级的位运算。工程上还要考虑批量判断:单次百万 ID 的查询不要一秒一个请求,而是分批(每批几千)走 pipeline 或批量接口,或者把「标签 → 歌曲集合」离线预计算好,请求时只做集合求交。要主动提热更新与一致性:离线产物怎么增量更新、上线新版本时怎么双跑切换。

JVM、双亲委派与 HashMap 这三问按「机制 + 为什么 + 能被打破吗」回答。 垃圾回收器要能报出谱系和选型:Serial(单线程、客户端小堆)、ParNew/Parallel Scavenge(吞吐优先、JDK 8 默认)、CMS(并发标记清除、低停顿但有碎片与并发失败)、G1(分 region、可预测停顿、JDK 9 起默认)、ZGC/Shenandoah(着色指针与读屏障、亚毫秒停顿、大堆场景),并说清「停顿时间与吞吐量是权衡」,选型看堆大小与延迟目标。双亲委派的机制是「优先让父加载器加载」,好处是核心类库唯一、防止自定义类冒充 java.lang;可以被打破,典型有四处:SPI 场景用线程上下文类加载器(JDBC 的 DriverManager 加载各厂商驱动)、Tomcat 的 WebAppClassLoader 先自己找再委派(Web 应用要隔离且不能覆盖 JDK 类)、OSGi 的网状委派、以及热部署/热替换场景直接重写 loadClass。HashMap 的对比要讲结构演进:1.7 是数组 + 链表、插入用头插法、扩容时并发可能形成环形链表;1.8 改成数组 + 链表 + 红黑树(链表长度到 8 且容量到 64 才树化)、插入改尾插、扩容用高低位拆分减少 rehash 重排,哈希扰动也从四次移位异或简化;同时说清它线程不安全、并发场景要用 ConcurrentHashMap,以及 1.8 里 hashCode 冲突严重时树化能把查询从 O(n) 降到 O(log n)。

SQL 与索引设计,写出语句就要顺手讲清索引为什么这么建。 表里是歌曲 ID、版权 ID、状态,查指定版权方下状态为生效的歌曲 ID,SQL 大致是 SELECT song_id FROM t WHERE copyright_id = ? AND status = 1;。联合索引按「等值条件在前、区分度高的在前」建 (copyright_id, status, song_id):copyright_id 选择性最高放最左,status 是低区分度的等值条件放中间,song_id 放最后是为了让查询走覆盖索引、避免回表(这条 SQL 只取三列,直接从二级索引拿数据)。要主动验证:EXPLAIN 里 type 应是 ref 或 range、Extra 出现 Using index 才算覆盖;不要建 (status, copyright_id) 之类顺序反了的索引,也不要只建单列索引指望索引合并。

几百万条、每次只能取 1000 条,用游标分页而不是 OFFSET。 LIMIT 1000 OFFSET n 在深页会退化成扫描前 n 行,越翻越慢,而且期间数据变动会导致漏读或重复。正确的做法是键集分页:游标字段必须「唯一 + 有序且稳定」——自增主键或全局唯一的歌曲 ID(雪花号不行,它是趋势递增但可能跨实例乱序;时间戳不唯一),WHERE copyright_id = ? AND status = 1 AND song_id > ? ORDER BY song_id LIMIT 1000,每批把最后一条的 song_id 作为下一批游标,并让索引顺序与 ORDER BY 一致(上面的联合索引刚好满足)。还要交代几个实务点:批次之间不要开长事务,按批提交避免锁与 undo 膨胀;如果要求「快照一致」,就固定一个版本字段(如更新时间水位)在查询里带上;遍历速度要限流以免打爆从库;丢数据的排查口径是「总数对账 + 游标推进的单调性」。