NiceOffer

面试面经 · 第四范式

【第四范式】【Agent 开发】【暑期实习三面】面经(八股+大模型项目+手撕)

第四范式面经Agent 开发实习

第四范式 Agent 开发 暑期实习三面(技术终面)面经

背景:海外一年制硕士,本科国内 211,投递时无正式实习,但有 2 个自己的 Agent 开源项目和 1 段实验室 RAG 落地经历。

题目摘要

  1. Go 语言基础:goroutine 与 channel 的底层实现,GMP 模型中调度器如何避免 goroutine 饥饿,以及 sync.Pool 在什么场景下使用、有什么坑。
  2. MySQL 与 Redis 八股:MySQL 的 RR 隔离级别下如何用 MVCC + Next-Key Lock 解决幻读;Redis 的 zset 底层跳表与哈希索引的取舍,以及缓存穿透/击穿/雪崩的应对策略。
  3. Agent 项目深挖:围绕一个基于 Function Calling 的客服 Agent,追问工具调用的错误恢复、上下文窗口超限处理、多轮对话中的状态管理。
  4. RAG 优化追问:检索质量差时如何诊断?重排(Rerank)的引入时机与代价?
  5. 推理成本优化:如何估算一次 Agent 交互的 token 成本?有哪些具体降本手段?
  6. 手撕算法题「设计一个支持插入、删除、随机获取元素的集合(允许重复元素)」,即 LeetCode 381。要求写出核心思路与时间复杂度分析。

项目深挖

面试官对项目细节的追问非常猛,基本不聊框架,全在抠边界条件和工程取舍。

追问 1:你的 Agent 在 Function Calling 中,如果模型调用了错误的工具参数(比如日期格式不对),系统怎么处理?

参考回答方向:首先,在工具定义时用 JSON Schema 严格约束参数类型,并给每个参数写 description 提示模型。其次,在执行层加一层参数校验,如果校验失败,不会直接报错,而是把错误信息格式化为“工具返回的错误消息”回传给模型,让模型自行修正后重新调用。最后,设置最大重试次数(比如 2 次),超过后切换为兜底话术。面试官追问“如果模型连续两次修正都失败呢”,我补充了降级策略:放弃工具调用,走预设的 FAQ 流程,并记录日志用于后续优化 prompt 或工具描述。

追问 2:上下文管理怎么做?长对话中如何避免 token 爆炸?

参考回答方向:我的项目里用了双缓冲策略——短期记忆(最近 5 轮完整消息)和长期记忆(通过摘要提取关键信息,存到向量库)。具体实现是:当对话轮次超过阈值时,调用一次轻量模型(如 GPT-4o-mini)对历史对话做摘要,只保留用户的核心诉求和已确认的实体信息。同时,在每次工具调用时,只把与该工具相关的历史消息拼入上下文,而不是全量拼接。面试官追问“摘要会不会丢失关键信息”,我补充了“摘要后保留原始消息的索引,如果模型需要查细节,可以主动调一个 get_message_detail 工具”。

追问 3:如何评估你的 Agent 效果?A/B 测试怎么做?

参考回答方向:我用了离线评估(基于标注的测试集,算工具调用准确率和任务完成率)和在线评估(真实用户会话的 CSAT 评分、平均轮次、兜底触发率)。A/B 测试时,我按用户 ID 哈希分流,确保同一用户只进入一个实验组,同时用 t-test 检验指标差异的显著性。面试官追问“如果在线指标波动大怎么判断”,我回答会先看兜底触发率和工具错误率这两个过程指标,如果它们稳定,再去看最终的任务完成率,避免被随机噪声干扰。

手撕算法题

题意:设计一个数据结构,支持在平均 O(1) 时间内完成 insert(val)remove(val)getRandom(),其中 val 可能重复,getRandom() 需要按元素的实际数量等概率返回某个元素。

关键思路:这是 LeetCode 381 的变体。核心难点在于 remove 时如何保持 O(1) 且不破坏随机等概率。

  • map[int]map[int]struct{} 记录每个值对应的索引集合(用哈希集合存索引,保证 O(1) 插入/删除)。
  • 用一个 slice 存所有元素值,getRandom 直接随机索引取 slice 中元素。
  • remove(val) 时,从 val 的索引集合中随便取一个索引 i,把 slice 末尾元素 last 移到 i 位置,更新 last 的索引集合(删除末尾索引,加入 i),然后删掉 slice 末尾,最后从 val 的索引集合中删除 i。如果 val 的索引集合空了,就删掉这个 key。

时间复杂度insertremove 平均 O(1),getRandom 严格 O(1)。空间复杂度 O(n)。

我写完代码后,面试官追问“如果 removei 正好是最后一个索引怎么办”,我答“直接 pop 即可,不需要移动元素,因为 last 就是 val 本身”,面试官点头。

准备建议

  1. 把项目里的每个技术选型都准备一个“为什么不用 X”的答案。比如我用 Redis 做缓存,一定要能说清为什么不用本地内存或 Memcached;我用 Function Calling 而不是直接让模型输出 JSON,得能对比两者的可靠性、成本、延迟。面试官几乎每个问题都在试探你是否真正做过取舍,而不是照着教程堆技术。
  1. 手撕题不要只刷高频题,要刷“数据结构设计类”。第四范式的面试风格明显偏向工程设计,LeetCode 380/381、LRU、LFU、Trie、并查集这类题出现概率极高。建议把每道题的时间复杂度推导写清楚,特别是“为什么均摊 O(1)”这种细节。
  1. 准备一个“成本优化”的硬核案例。Agent 方向必问推理成本。提前算清楚:你的项目一次完整对话平均消耗多少 token?其中系统 prompt、历史消息、工具返回各占多少?你用什么手段降本(模型降级、缓存、摘要、动态上下文裁剪)?最好能给出一个具体数字,比如“引入摘要后,长对话的 token 消耗降低了 62%”,这种数据非常加分。

结果反馈

三面结束后 3 天收到 HR 电话,确认通过,约了 5 天后的 HR 面,后续顺利拿到 offer。整体感受是:第四范式的技术面非常务实,不考偏题怪题,但每个问题都会往工程落地上深挖,准备时务必以“能跑通、能量化、能对比”为标准。

想系统备战大厂大模型/Agent 开发?NiceOffer 提供 SDE+LLM 双轨 1v1 陪跑,合同保底 40w 年薪,文末扫码咨询。