面灵AI→

蚂蚁集团大模型算法岗面经

时间
2026-09
来源
牛客网

《面试题目》

RL 训练与 Agent 系统

  1. PPO 的原理?从维护的四个 model 讲,再详细讲一下训练流程和损失函数各个参数含义?
  2. 为什么有了 reward model 还需要 critic model?critic model 作用是什么?
  3. 交叉熵和 KL 散度的联系和区别?PPO 的 KL 散度可以改成交叉熵吗?分类任务可以用 KL 散度吗?
  4. GRPO 的 KL 散度和 PPO 的 KL 散度区别?K1 K2 K3 估计区别?
  5. rollout 数量、batchsize 数量和计算资源(卡的数量)有什么关系?线性?非线性?
  6. 真实采样数量一定等于 rollout 数量吗?
  7. 提到了拒绝采样,详细讲一下。
  8. 你是怎么设计 agent 的记忆系统?
  9. 长期记忆如何存储?如果历史记录量非常大,怎么优化查询效率?
  10. 你们有没有用到类似 AutoGen 或 LangChain 的框架?为什么选这个框架?
  11. vLLM 框架是怎么做推理加速的?

存储中间件与工程基础

  1. Redis 数据结构 zset 底层是怎么实现的?
  2. Zset 为什么用跳表,MySQL 为什么用 B+ 树,HashMap 为什么用红黑树?
  3. Bitmap 底层、误判、稀疏怎么解决?
  4. 布隆过滤器怎么实现?
  5. 为什么需要 Agent,和大模型有什么不同?
  6. CLIP 是什么?
  7. function calling 和 MCP 有了解吗?
  8. 让你提供一个 MCP 服务怎么做?
  9. RocketMQ 事务消息怎么实现的?
  10. MySQL 和 Redis 同步会有什么问题?
  11. 觉得自己和同龄人比有什么优缺点?
  12. 知道什么向量相似度计算方法?
  13. 实习中最有挑战的事是什么?
  14. Redis 网络模型?
  15. 看过 RocketMQ 哪些源码?
  16. 实习中怎么解决数据一致性问题?
  17. 实现一个 RPC 接口应该注意什么?

语言基础与工程实践

  1. Python 的数据类型有哪些?
  2. 几种下载方式:uv、pip、conda 有什么区别?
  3. 包冲突怎么解决,除了用虚拟环境以外呢,用 uv 怎么解决的?
  4. 怎么调用大模型的,你最近调用的大模型是什么,用它的什么功能呢?
  5. StringBuffer 和 StringBuilder 区别?
  6. String.length() 和 String.getBytes().length 的区别?
  7. 你现在使用的 JDK 比起 8 来做出了什么进步?
  8. 前端用过什么?
  9. 温度(Temperature)是什么?
  10. 幻读现象怎么解决?
  11. 怎么调试呢?你的项目是怎么使用的呢?
  12. 怎么训练呢?比如你涉及了什么算法?
  13. 结合 word 转图片再用大模型提取成 JSON,你们为什么要选用大模型呢?对比传统模型的优点在哪儿呢?
  14. 如果 word 中添加附件怎么处理?
  15. 最近有用大模型做过什么项目没,请你说说。
  16. 你怎么看待加班这一现象?

RAG、Agent 与模型落地

  1. RAG 中如何处理非结构化知识与结构化知识的混合检索?
  2. 在金融业务中,像「杠杆」、「对冲」等许多术语都存在歧义性,你如何在 RAG 中实现术语的准确消歧?
  3. 你认为 RAG 是大模型能力的一种补偿手段还是主流范式?未来还会存在吗?
  4. 如何控制 Agent 生成的内容在业务上规避风险,比如合规和隐私之类的?
  5. Agent 执行链中失败重试会导致长尾耗时,你如何优化策略以控制 SLA?
  6. LoRA 微调时哪些层是可以不冻的?为什么有时候逐层解冻效果更好?
  7. 如何部署一个高并发低延迟的大模型 API 服务?
  8. 你觉得通用大模型和垂类小模型之间最终会形成怎样的分工?哪个更适合企业落地?

RL 算法对比

  1. PPO 和 DPO 的区别和原理?

《参考解析》

1. PPO 的四个模型与训练流程:PPO 在 RLHF 里维护四份权重:actor(策略,被训练)、critic(价值网络,被训练)、reward model(打分,冻结)、reference model(冻结的 SFT 策略)。一轮迭代:actor 对一批 prompt 做 rollout,采出回答与逐 token logprob;reward model 给终局打分得到奖励 r;critic 逐 token 估状态价值 V;用 GAE 把回报与价值的差折算成优势 A;actor 用裁剪目标 min(ratio × A, clip(ratio, 1-ε, 1+ε) × A) - β × KL 更新,ratio 是新旧策略的概率比,ε 取 0.1~0.2 控制单步幅度,β 是 KL 惩罚系数;critic 用价值回归损失更新,ratio 偏出裁剪区间后梯度即被截断。

2. 有了 reward model 为什么还要 critic:reward model 只在序列结束给一个稀疏的终局奖励,它回答不了”这一步比平均好多少”。critic 的作用是把未来回报的期望估出来当作 baseline,用「优势 = 实际回报 − 状态价值」把绝对回报转成相对优势,否则同一批里所有 token 都被同一个正奖励强化,方差极大、信用分配完全失效。有了 critic,即使是负回报的整条回答,其中高于预期的 token 也能得到正优势、被保留下来。代价是 critic 与 actor 同规模、显存和算力几乎翻倍,而且价值估计本身很难训(尺度不断漂移,需要做 reward normalization、value clipping),这也是 GRPO 干脆用组内均值当 baseline、把 critic 砍掉的原因。

3. 交叉熵与 KL 散度的联系与区别:对 one-hot 标签分布 p 和模型分布 q,交叉熵是 H(p,q) = -Σ p·log q,KL 是 D(p||q) = H(p,q) - H(p),即交叉熵减去真实分布的熵。当 p 是确定标签(熵为 0)时,最小化交叉熵等价于最小化 KL;但 p 是软标签或参考分布时,两者差一个与 q 无关的常数项。KL 还不对称:D(p||q) 是 mass-covering,会在 p 有概率的地方都铺上概率;D(q||p) 是 mode-seeking,容易塌缩。所以 PPO 用交叉熵替代 KL 只在参考分布接近 one-hot 时近似成立,实践中一般直接用采样估计的 k3。分类任务可以用 KL,知识蒸馏就是拿 KL 对齐 teacher 与 student 的分布。

4. GRPO 的 KL 估计与 K1/K2/K3:GRPO 去掉 critic,对同一 prompt 采样一组(如 8 条)回答,用组内 reward 的均值与标准差归一化成优势(A_i = (r_i - mean) / std),损失里同样带对 reference 的 KL 惩罚。KL 无法算全词表求和,只能采样估计,三种常见形式:k1 是 -log(π_ref / π_θ),单样本但方差很大;k2 是 0.5 × (log(π_θ / π_ref))^2,恒非负、实现简单,但平方放大了偏差;k3 是 π_ref / π_θ - log(π_ref / π_θ) - 1,恒非负、方差最低,且在 π_θ = π_ref 处恰好取 0,因此被 GRPO 默认采用。注意 k3 要同时保留 ref 与 policy 两套逐 token logprob,显存开销更高。

5. rollout 数量、batch size 与卡数的关系:三者不是简单线性。设 global batch(每次参数更新消费的样本数)为 B,rollout batch 为 R,mini batch 为 b,则一个 step 的采样量大致是 R = B × ppo_epochs × gradient_accumulation;每张卡能放多少条受限于 KV cache 显存、序列长度和 offload 策略。加卡后吞吐在计算受限区间近似线性上升,进入访存带宽或通信(权重同步、all-gather)受限区间就变成次线性:actor 生成、reward 打分、reference 前向、critic 前向这四段串行,加卡只压缩其中并行度高的部分。所以扩容优先做推理引擎与训练引擎分离、长序列按长度分桶减少 padding,而不是单纯堆卡。

6. 真实采样数量往往不等于 rollout 数量:rollout 数量是配置里要求生成的条数,真实进入训练的样本通常更少。原因有几类:过滤掉被截断或格式不合法的输出;reward 打分后被丢弃的极端样本(比如全 0 或超出范围);去重与相似度过滤;多轮 Agent 任务里轨迹失败、工具超时导致的中断;以及 GRPO 里组内方差为 0(全对或全错)的组没有梯度信号,会被直接丢掉。另外做了 dynamic sampling / 拒绝采样的流程,会一直重采样直到凑出所需的有效条数,此时”生成的”远多于”训练的”。做资源估算时按有效样本量算,否则会低估生成阶段的耗时。

7. 拒绝采样:核心想法是”多采样、只留好的”。工程上有两种用法:一是数据生产(RFT / rejection sampling fine-tuning),对同一 prompt 采多条回答,用规则或 reward model 打分,只保留高分的去 SFT,等价于做一次离线的策略改进;二是 RL 训练内部的动态采样,丢弃无梯度或低质量的样本再补采,保证有效 batch 充足。关键细节是过滤阈值不能太严:只留满分样本会让分布迅速塌缩、多样性归零、长度也越训越短;常见做法是保留 top-k 或分位数阈值、配合温度扰动,并对通过筛选的数据做去重与难度分层,避免难题全被丢掉后模型只学到简单分布。

8. Agent 记忆系统的分层设计:一般分三层——短期(working memory,直接放在上下文里的最近若干轮对话和当前任务状态)、情节记忆(episodic,把一次任务的关键轨迹压缩成带时间戳的摘要落库)、长期语义记忆(semantic,抽取出的用户偏好、事实、领域知识)。写入侧做的是”抽取 + 压缩 + 去重”:用 LLM 从对话里抽结构化事实,按实体或主题归并,冲突时以新覆盖旧或保留版本;读取侧是”召回 + 重排 + 注入”:先用向量检索或混合检索拿候选,再用 reranker 精排,最后按 token 预算裁剪成结构化片段塞进 prompt。工程上要强制 token 预算和降级策略(检索超时就退回只用短期记忆),否则上下文膨胀会直接把延迟和成本吃掉。

9. 长期记忆的存储与大规模检索优化:存储通常是”向量库 + 关系库/宽表 + 可选图”的组合:向量库存 embedding 用于语义召回,关系库存结构化字段(用户、时间、类型)用于过滤,图库存实体关系用于多跳推理。量级上去之后,瓶颈在召回精度和延迟:先把元数据过滤下推到向量检索(pre-filter)而不是检索后再过滤,避免候选被过滤光;索引层面用 HNSW 调 ef_search 换召回,超大集合用 IVF + PQ 量化压缩内存,必要时做分片(按 user_id 哈希)保证单次查询只扫一个分片;数据侧做冷热分层,热数据放内存索引,冷数据落到磁盘索引按需加载;再配合查询侧的 embedding 缓存、结果缓存和 top-k 截断。真正省钱的往往是记忆的衰减与合并——给每条记忆一个权重(新鲜度 + 命中次数),定期压缩低频记忆,而不是无限堆向量。

10. vLLM 的推理加速:核心是 PagedAttention:把 KV cache 按固定大小的 block 管理,用类似虚拟内存的页表把逻辑连续的 KV 映射到不连续的物理块,消除为最长序列预留连续显存造成的碎片,同样显存能容纳更多并发序列。上层配 continuous batching(新请求在每个 decode step 就能插入批次)和前缀共享(相同 prompt 前缀的 block 用 copy-on-write 复用,对 few-shot、system prompt 很有效)。此外还有 CUDA Graph 降低 kernel 启动开销、chunked prefill 把长 prompt 切块与 decode 混跑以平抑延迟抖动、量化压缩权重与 KV 占用、张量并行把模型摊到多卡。

11. zset 的底层实现(跳表 + 哈希表):zset 同时维护一个 dict(member 到 score 的映射)和一个 skiplist。dict 让 ZSCORE 这类按成员查分数的操作是 O(1);skiplist 按 score 有序,让范围查询、排名(ZRANGE、ZRANK)能在 O(log n) 内完成。跳表节点里存 member、score 和多层 forward 指针,还带一个 span 字段记录跨越的节点数,正是靠 span 才能 O(log n) 求出排名。元素少时(默认 128 个以内且成员较短)会退化成 ziplist/listpack 节省内存,一旦超过阈值就转成 skiplist。

12. 跳表、B+ 树、红黑树的选型逻辑:三者对应三种访问模式。跳表是纯内存结构,实现简单、范围遍历靠底层链表顺序访问,天然适合 zset 这种既要按 score 排序又要按 member 查的场景;代价是每层指针的额外内存。MySQL 用 B+ 树是因为数据在磁盘上,一次查询的代价由 I/O 次数决定——B+ 树每个节点存几百个键,树高只有 3~4 层,一次查询最多几次页读;同时叶子节点用链表相连,范围扫描和排序天然友好,且数据全在叶子节点让插入删除更稳定。HashMap 用红黑树(Java 8 起在链表长度超过 8 且容量到 64 时树化)是为了兜底哈希冲突的退化:链表在极端冲突下查找 O(n),树化后保证 O(log n),还顺带缓解哈希碰撞攻击,而选红黑树而非 AVL 是因为它的插入删除旋转次数更少、写多读少的场景更划算。

13. 布隆过滤器与 Bitmap:Bitmap 就是一段二进制位,用位的下标表示 key,一位表示一个状态,插入即 SETBIT,不会误判但不能表示复杂 key;稀疏时大量位是 0、空间利用率低,解决办法是稀疏索引、Roaring Bitmap(按 65536 分块,块内按稠密程度选数组或位图)或换用哈希结构。布隆过滤器用 k 个独立哈希把 key 映射到位数组的 k 个位置:查询时只要有一位是 0 就一定不存在,全是 1 则可能存在,所以只有假阳性、没有假阴性,误判率约为 (1 - e^(-kn/m))^k,由位数组长度 m、元素数 n 和哈希个数 k 决定,最优哈希个数是 k = (m/n) · ln2。标准布隆不支持删除(清位会影响其他元素),需要删除就用计数布隆或 Cuckoo Filter;位数组也不能直接扩容,扩容要用分层或可扩展布隆。

14. 高并发低延迟的模型服务部署:推理侧选择 vLLM / TensorRT-LLM 这类带 PagedAttention、continuous batching、前缀缓存和量化的引擎,按”最大化吞吐”还是”压低 P99”来调 batch 上限和 prefill 分块;容量侧按 GPU 数与单卡 KV 容量算能并发多少序列,做多副本 + 负载均衡,并按 prompt 长度和任务类型分池,避免长请求把短请求堵在同一个批次里;延迟优化包括流式输出降低首 token 等待、对相同或相似请求做语义缓存与精确缓存、prompt 压缩与上下文裁剪;稳定性上要有限流、排队与超时降级(超时切小模型或返回缓存)、KV cache 水位监控与自动扩缩容。工程上还要把网关、鉴权、计费、日志与 trace 独立于推理进程,否则日志写盘就能把 TPOT 拖垮。

15. RAG 的混合检索与术语消歧:混合检索的常规做法是”多路召回 + 融合排序”:结构化数据走 SQL / 图查询或 text2sql,非结构化走 BM25(精确术语、编号)与向量检索(语义改写)双路,再用 RRF 或加权分数融合,最后交 reranker 精排,并把结构化结果以表格或三元组形式注入上下文,让模型看到的是对齐好的字段而不是散落文本。术语消歧要靠”上下文 + 领域信号”:构建术语词典与同义词表,把候选义项和定义一起喂给模型做上下文判定;检索侧对歧义术语做查询改写或展开(同时召回多个义项的文档),再按行业标签、文档来源、时效做元数据过滤;更稳的做法是在知识库里为每个术语维护规范条目(定义、适用业务、示例),把消歧结果显式写进 prompt,并设置置信度阈值,低于阈值时向用户追问而不是硬猜。