面试面经 · 懂车帝
【懂车帝】【Agent 开发】【暑期实习二面】面经(八股+大模型项目+手撕)
懂车帝 Agent 开发 暑期实习二面面经
背景:海外一年制硕士,投递岗位为懂车帝 Agent 开发实习生,一面通过后约一周收到二面邀请。
题目摘要
二面整体风格偏向工程落地与系统设计,面试官为 Agent 方向的技术负责人,全程节奏紧凑,不闲聊,直接进入主题。核心考点如下:
- 编程语言基础(Go):goroutine 与 channel 的底层实现、内存逃逸分析、slice 与 array 的区别及扩容机制。
- MySQL/Redis 八股:MySQL 索引失效场景、事务隔离级别与 MVCC 实现;Redis 持久化机制(RDB/AOF)及缓存穿透/击穿/雪崩的解决方案。
- Agent 项目深挖:围绕 RAG 检索增强生成、Function Calling 的工具调用设计、多轮对话上下文管理、推理成本优化(Token 压缩、缓存策略)。
- 手撕算法题:实现一个带过期时间的 LRU Cache(LeetCode 460 变体),要求 get 和 put 操作平均时间复杂度 O(1),并支持 key 过期自动删除。
项目深挖
面试官对我简历中一个「汽车领域智能问答 Agent」项目非常感兴趣,连续追问了三个方向,每个都要求给出具体设计决策和权衡。
追问 1:你的 RAG 检索为什么用混合检索?向量检索和关键词检索的结果如何融合?
参考回答方向:不要只说「向量召回不够准所以加 BM25」。要具体说明:汽车领域存在大量型号、参数、专有名词(如「L2 级辅助驾驶」「WLTC 续航」),这些词在 embedding 空间中区分度不高,容易产生语义漂移。因此采用 BM25 做关键词召回 + 向量检索做语义召回,使用 RRF(Reciprocal Rank Fusion)融合排序,公式为 score = Σ 1/(k + rank),k 取 60。同时要提到对召回结果做去重和过滤(如过滤低分片段、去除与 query 无关的广告内容)。
追问 2:Function Calling 的工具调用你如何设计?如果模型连续调用多个工具,上下文如何管理?
参考回答方向:强调工具描述信息对 Token 消耗的影响。我在项目中为每个工具维护了精简的 JSON Schema 描述,工具调用时会动态拼装「模型已完成的工具调用历史」而非全部对话历史。关键点:上下文管理采用「滑动窗口 + 关键信息摘要」策略——将早期工具调用结果压缩为结构化摘要(如「已查询北京地区 2024 款 汉EV 价格区间」),后续轮次只保留摘要和最近 3 轮完整对话。这样能显著降低推理成本,实测 Token 消耗减少约 40%。
追问 3:你如何评估 Agent 的推理质量?有没有做成本优化?
参考回答方向:先说明评估体系,再谈成本。评估方面,我构建了一个包含 200 条汽车问答的测试集,人工标注了「答案正确性」「工具调用准确性」「多轮一致性」三个维度。成本优化方面,除了上文提到的上下文压缩,还做了两层缓存:一是对高频 query 的答案做 Redis 缓存(TTL 按问题类型区分);二是对工具调用结果做缓存——同一车型、同一参数多次查询时直接复用结果,避免重复调用外部 API。另外,模型选择上采用「小模型兜底 + 大模型精调」策略:简单问题(如「XX 车轴距多少」)直接用小模型回答,复杂推理(如「XX 和 YY 哪个更适合家用」)才调用大模型。
手撕算法题
题目:设计一个支持过期时间的 LRU Cache,实现 get(key) 和 put(key, value, ttl) 方法。get 时若 key 已过期返回 -1,put 时若 key 已存在则更新 value 和过期时间。要求所有操作平均时间复杂度 O(1)。
关键思路:在标准 LRU(哈希表 + 双向链表)基础上,需要额外处理过期逻辑。有两种方案:
- 惰性删除:
get时检查是否过期,过期则删除并返回 -1。put时若 key 已存在,更新节点并重置过期时间;若 key 不存在,插入新节点并设置过期时间。问题在于过期节点可能长期占用内存。 - 惰性删除 + 定时清理:在惰性删除基础上,维护一个最小堆(按过期时间排序)或使用时间轮,定期(如每 1/10 TTL 间隔)清理过期节点。堆的插入和删除操作 O(log n),但实际应用中过期节点数量远小于总节点数,性能可接受。
我选择了方案二:用双向链表维护访问顺序,用哈希表实现 O(1) 查找,另用一个小顶堆存储「过期时间 -> key」的映射。put 时向堆中插入过期时间,后台 goroutine 每秒检查堆顶,若过期则从哈希表和链表中删除对应节点。面试官追问了「如果堆顶节点被重新 put 更新了过期时间怎么办」,我回答在哈希表中额外存储节点的过期时间,堆中存储的是「过期时间 + key」的副本,删除时通过哈希表校验版本号,避免误删。
准备建议
- Go 语言重点看内存模型和并发原语:面试官问到了 goroutine 的 GMP 模型、channel 的阻塞/非阻塞收发、slice 扩容的触发条件(容量小于 1024 时按 2 倍扩容,超过后按 1.25 倍)。建议把《Go 语言设计与实现》中「数据结构」「并发同步」两章过一遍,能画出 GMP 调度流程图即可。
- Agent 项目准备要「设计决策 + 数据支撑」:不要只讲「我用了 RAG」「我做了 Function Calling」,面试官会追问「为什么用这个方案」「性能提升了多少」。建议提前整理:你的检索方案对比实验数据(如准确率提升了 X%)、推理成本优化前后的 Token 消耗对比、上下文压缩策略的消融实验。没有数据支撑的设计决策在二面很难过关。
- 手撕题要练「带约束的 LRU 变体」:单纯 LRU 已经不够了,二面大概率会考带过期时间、带频率限制、带容量动态调整等变体。建议在 LeetCode 上刷 460(LFU Cache)、146(LRU Cache)以及「LRU with TTL」的实现题,重点关注「惰性删除 vs 主动删除」的取舍和「多数据结构协同」的设计思路。
二面结束后约 3 天收到 HR 电话,告知面试通过,下一轮为技术终面,间隔约一周。
想系统备战大厂大模型/Agent 开发?NiceOffer 提供 SDE+LLM 双轨 1v1 陪跑,合同保底 40w 年薪,文末扫码咨询。