蚂蚁集团大模型算法岗面经
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
RL 训练与 Agent 系统
- PPO 的原理?从维护的四个 model 讲,再详细讲一下训练流程和损失函数各个参数含义?
- 为什么有了 reward model 还需要 critic model?critic model 作用是什么?
- 交叉熵和 KL 散度的联系和区别?PPO 的 KL 散度可以改成交叉熵吗?分类任务可以用 KL 散度吗?
- GRPO 的 KL 散度和 PPO 的 KL 散度区别?K1 K2 K3 估计区别?
- rollout 数量、batchsize 数量和计算资源(卡的数量)有什么关系?线性?非线性?
- 真实采样数量一定等于 rollout 数量吗?
- 提到了拒绝采样,详细讲一下。
- 你是怎么设计 agent 的记忆系统?
- 长期记忆如何存储?如果历史记录量非常大,怎么优化查询效率?
- 你们有没有用到类似 AutoGen 或 LangChain 的框架?为什么选这个框架?
- vLLM 框架是怎么做推理加速的?
存储中间件与工程基础
- Redis 数据结构 zset 底层是怎么实现的?
- Zset 为什么用跳表,MySQL 为什么用 B+ 树,HashMap 为什么用红黑树?
- Bitmap 底层、误判、稀疏怎么解决?
- 布隆过滤器怎么实现?
- 为什么需要 Agent,和大模型有什么不同?
- CLIP 是什么?
- function calling 和 MCP 有了解吗?
- 让你提供一个 MCP 服务怎么做?
- RocketMQ 事务消息怎么实现的?
- MySQL 和 Redis 同步会有什么问题?
- 觉得自己和同龄人比有什么优缺点?
- 知道什么向量相似度计算方法?
- 实习中最有挑战的事是什么?
- Redis 网络模型?
- 看过 RocketMQ 哪些源码?
- 实习中怎么解决数据一致性问题?
- 实现一个 RPC 接口应该注意什么?
语言基础与工程实践
- Python 的数据类型有哪些?
- 几种下载方式:uv、pip、conda 有什么区别?
- 包冲突怎么解决,除了用虚拟环境以外呢,用 uv 怎么解决的?
- 怎么调用大模型的,你最近调用的大模型是什么,用它的什么功能呢?
- StringBuffer 和 StringBuilder 区别?
- String.length() 和 String.getBytes().length 的区别?
- 你现在使用的 JDK 比起 8 来做出了什么进步?
- 前端用过什么?
- 温度(Temperature)是什么?
- 幻读现象怎么解决?
- 怎么调试呢?你的项目是怎么使用的呢?
- 怎么训练呢?比如你涉及了什么算法?
- 结合 word 转图片再用大模型提取成 JSON,你们为什么要选用大模型呢?对比传统模型的优点在哪儿呢?
- 如果 word 中添加附件怎么处理?
- 最近有用大模型做过什么项目没,请你说说。
- 你怎么看待加班这一现象?
RAG、Agent 与模型落地
- RAG 中如何处理非结构化知识与结构化知识的混合检索?
- 在金融业务中,像「杠杆」、「对冲」等许多术语都存在歧义性,你如何在 RAG 中实现术语的准确消歧?
- 你认为 RAG 是大模型能力的一种补偿手段还是主流范式?未来还会存在吗?
- 如何控制 Agent 生成的内容在业务上规避风险,比如合规和隐私之类的?
- Agent 执行链中失败重试会导致长尾耗时,你如何优化策略以控制 SLA?
- LoRA 微调时哪些层是可以不冻的?为什么有时候逐层解冻效果更好?
- 如何部署一个高并发低延迟的大模型 API 服务?
- 你觉得通用大模型和垂类小模型之间最终会形成怎样的分工?哪个更适合企业落地?
RL 算法对比
- 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,并设置置信度阈值,低于阈值时向用户追问而不是硬猜。