NiceOffer

面试面经 · 腾讯

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

腾讯面经Agent 开发实习

腾讯 Agent 开发 暑期实习三面面经

背景:海外一年制硕士,主攻 LLM 应用方向,此前有两段大模型相关项目经历,一面二面均为技术面,三面为综合技术面+压力面。

题目摘要

  1. Go 语言基础:goroutine 与 channel 的底层实现、GMP 调度模型简述、内存逃逸分析场景
  2. MySQL/Redis 八股:索引失效场景、MVCC 原理、Redis 持久化机制对比(RDB vs AOF)、缓存穿透/击穿/雪崩的解决方案
  3. Agent 项目深挖:RAG 检索质量优化、Function Calling 的容错设计、多轮对话上下文管理策略、推理成本优化方案
  4. 手撕算法LFU Cache(LeetCode 460)——要求实现 get 和 put 操作,时间复杂度 O(1)

项目深挖

面试官全程围绕我简历上的一个「客服 Agent 系统」项目进行追问,节奏快且细节密集,没有一句废话。

追问 1:你的 RAG 检索效果不好时,怎么定位是召回问题还是生成问题?

参考回答方向:先做模块化评估,不直接看端到端效果。我会把检索和生成拆开——用召回率/命中率指标评估检索模块(比如 top-5 是否包含标准答案),用 Rouge-L/BLEU 评估生成模块(固定检索结果,只调 prompt 或生成参数)。如果召回率低,优先调 chunk 大小、embedding 模型或 rerank 策略;如果召回正常但生成差,再考虑 prompt 指令或模型微调。面试官大概率会追问「具体怎么调 chunk」,要能说出根据文档类型调整 chunk size(如代码类用 300-500 token,长文档用 800-1000)并配合 overlap 的设置。

追问 2:Function Calling 如果模型返回了不存在的函数名或参数格式错误,怎么处理?

参考回答方向:三层容错。第一层是 schema 校验,在调用前用 JSON Schema 对模型输出做严格校验,不合法直接触发重试(带错误信息重新调用模型);第二层是函数名模糊匹配,用编辑距离或向量相似度匹配到最接近的可用函数;第三层是兜底策略,如果重试两次仍失败,走人工接管或返回默认话术。另外要提到超时控制和并发限制,防止恶意或异常的 function call 打爆下游服务。

追问 3:你的 Agent 多轮对话上下文怎么管理?推理成本怎么优化?

参考回答方向:上下文管理分三层——短期记忆(当前会话窗口内的消息,用滑动窗口截断)、长期记忆(关键信息抽取后存 Redis/向量库,按用户维度存储)、摘要记忆(当窗口超限时,用 LLM 对历史对话做摘要压缩)。成本优化有两个方向:一是减少 token 消耗,如 system prompt 静态化、历史消息按相关性裁剪、只保留函数调用结果而非完整中间输出;二是降低模型调用次数,如用规则引擎先拦截简单意图、对相似请求做缓存(语义缓存,用 embedding 相似度命中)。面试官对「语义缓存」比较感兴趣,可以展开说下命中阈值和失效策略。

手撕算法:LFU Cache

题意:设计并实现一个 LFU(最不经常使用)缓存,支持 get 和 put 操作。当缓存容量满时,淘汰访问频率最低的 key;如果频率相同,淘汰最久未使用的 key(即 LFU + LRU 组合策略)。要求 get 和 put 的时间复杂度均为 O(1)。

关键思路

  • 使用 map[key]*Node 存储节点,O(1) 定位
  • 使用 map[freq]*List 存储每个频率对应的双向链表,链表内按访问时间排序(头部最新)
  • 维护 minFreq 变量记录当前最低频率,淘汰时从 freq[minFreq] 链表的尾部删除
  • 每次 get 或 put 命中时,将节点从原频率链表移除,插入新频率链表的头部,并更新 minFreq

代码要点(Go 实现核心逻辑):

type Node struct {
    key, val, freq int
    prev, next     *Node
}

func (c *LFUCache) Get(key int) int {
    if node, ok := c.cache[key]; ok {
        c.incrFreq(node)
        return node.val
    }
    return -1
}

func (c *LFUCache) Put(key int, value int) {
    if node, ok := c.cache[key]; ok {
        node.val = value
        c.incrFreq(node)
        return
    }
    if len(c.cache) >= c.capacity {
        c.evict()
    }
    // 插入新节点,freq=1
}

注意 Go 语言里双向链表可以自己实现,不建议用 container/list(因为需要额外维护 freq 到 list 的映射,自己实现更清晰)。手写时注意边界条件——容量为 0 的情况、get 不存在的 key、put 更新已有 key 的 value 等。

准备建议

  1. 项目深挖准备要按「指标→瓶颈→方案→验证」四步走。不要只讲「我做了什么」,要准备「效果不好时怎么排查」「有没有对比实验」「为什么选这个方案而不是另一个」。腾讯面试官非常吃这一套,几乎每个项目问题都会追到「你怎么验证你的方案有效」。
  1. Go 语言重点准备并发模型和内存管理。腾讯大量业务用 Go,Agent 服务的并发调用、流式返回都是高频考点。建议手写一个 goroutine pool 或 channel 实现的生产者-消费者模型,同时能说出 GMP 调度的基本流程和内存逃逸的常见场景(如返回局部变量指针、闭包捕获变量、interface 装箱等)。
  1. 手撕算法不要只刷 Hot 100,要练设计类题目。LFU、LRU、跳表、前缀树这类「数据结构设计题」在腾讯面试中出现频率很高。建议把 LeetCode 的 LRU、LFU、Trie、时间轮定时器都手写一遍,用 Go 或 Java 各写一遍,注意边界条件和复杂度分析要能脱口而出。

结果反馈

三面通过,两天后约了 HR 面,目前已拿到 offer。

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