蚂蚁集团大模型算法面经合集:并发基础与训练推理
- 轮次
- 多轮面试合集
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
面经 01 · 大模型算法岗(2026-03-21)
- 平时都是怎么学技术的?
- 线程和协程的区别?
- 了解过线程池吗,为什么要用到线程池?
- 知道哪几种线程池?
- 把一个任务扔进线程池会用什么方式处理?
- 知道哪几种拒绝策略,基于什么样的场景会去选择这样的拒绝策略?
- 多线程场景下,如何保证线程安全?
- Synchronized 和 ReentrantLock 的区别?
- 发生死锁的条件?
- 使用锁的时候有哪些注意事项,怎么样加锁会比较好一点?
- JVM 内存结构是怎样的?
- int 变量和 Object 变量会放在哪个区域?
- GC 算法各自适用的场景?
- ACID 中,原子性和一致性怎么实现的?
- 优化一个索引或者设计一个索引,需要考虑哪些?
- B+ 树结构,为什么要用 B+ 树,有什么优点?
- 什么是空间局部性?
- 系统架构大概是怎样的?
- 生产过程的数据怎么记录的?
- 为什么要用阿里的大模型?
- 分块策略怎么做的?
- 怎么评估检索效果?
- 如果问了一个知识库里没有的问题,系统怎么表现?
- 检索效果不好的话,如何排查和优化?
- QPS 特别高的话系统会有什么瓶颈?
- 项目中 Redis 的作用是什么?
- Redis 的 IO 模型?
- Redis 为什么快?
- 为什么用 Cache-Aside,为什么不用其他策略?
- 热点 key 该怎么处理?
- 分布式锁的维度有哪些?
- 抢购过程整体链路?
- 100 万人同时抢一个商品怎么解决?
- 用户同时在多个分片抢购怎么解决?
- 为什么用 MQ,有什么作用?
- 如何判断消费成功?
- 如何保证消息幂等性?
- 假如系统 10 分钟内一直告警,运维 agent 怎么去处理,有什么好的优化方案?
- 手撕:LRU 缓存。
面经 02 · 大模型算法岗(2026-03-16)
- 你们的「线索评分」机制是如何运作的?
- 如果出现「线索评分高,但实际转化率低」的情况,你怎么看待?如何提升 ROI?
- 场景题(共享屏幕看代码):如何设计 Prompt,让大模型严格按照指定的 JSON 格式输出,且不包含多余的废话?
- 如果大模型「幻觉」或者没有严格按格式输出(比如缺失字段、格式乱掉),系统层面如何做兜底和容错处理?
- 如果是做实体抽取或确定的逻辑分类任务,你会怎么调整 temperature、top_p 等参数?
- 你简历上提到系统平均耗时是 800ms,这个 800ms 在系统内部的链路分布是怎样的?
- 看你 23-24 年在华为待的时间比较短,离职原因是什么?
- 现在为什么考虑从百度看机会?
面经 03 · 大模型算法岗(2026-03-10)
- 商机智能体项目的背景与你的核心职责是什么?
- 你们是如何优化知识库检索(RAG)准确率的?
- 针对大模型响应慢的问题,系统耗时层面做了哪些优化?
- Go 语言原生的 Map 是并发安全的吗?在项目中如何解决并发读写问题?
- 平时开发中,MySQL 的慢查询你们是如何排查和优化的?
- 从上一家公司(百度)离职的原因是什么?目前的期望薪资如何?
面经 04 · 大模型算法岗(2026-02-28)
- MLA 为什么比 MHA 好?
- 权重吸收中间遇到的问题有哪些?
- KV Cache 的离线计算与非常用 KV Cache 的卸载加载是怎么做的?
- 还有什么 KV Cache 优化的相关 tricks?
面经 05 · 大模型算法岗(2026-02-27)
- 模型蒸馏的数据如何做的?如何清洗蒸馏得到的数据?
- 有没有使用强化学习做过数据仿真?
- 有没有了解过训练推理一致性这个领域?
- 模型量化如何做的?GPTQ、QAT 等等,并说明为什么选择了 w8a16 的量化?
- 写一下 PPO 算法的损失函数和 GAE 优势函数。
- 手撕:合并 K 个升序链表。
面经 06 · 大模型算法岗(2026-02-27)
- PPO 的原理?从维护的四个 model 讲,再详细讲一下训练流程和损失函数各个参数含义?
- 为什么有了 reward model 还需要 critic model?critic model 作用是什么?
- 交叉熵和 KL 散度的联系和区别?PPO 的 KL 散度可以改成交叉熵吗?分类任务可以用 KL 散度吗?
- GRPO 的 KL 散度和 PPO 的 KL 散度区别?K1、K2、K3 估计区别?
- rollout 数量、batchsize 数量和计算资源(卡的数量)有什么关系?线性?非线性?
- 真实采样数量一定等于 rollout 数量吗?
- 提到了拒绝采样,详细讲一下。
- 你是怎么设计 agent 的记忆系统?
- 长期记忆如何存储?如果历史记录量非常大,怎么优化查询效率?
《参考解析》
面经 01 · Java 并发与 JVM
1. 线程与协程的区别,以及线程池要解决什么:线程是操作系统调度的基本单位,有独立栈、共享进程地址空间,切换要陷入内核并保存现场;协程是用户态调度的轻量执行体,切换不经过内核、开销只有百纳秒级,但需要运行时支持,且一个阻塞式系统调用会卡住承载它的整个线程,所以协程适合 IO 等待密集、切换极频繁的场景,CPU 密集任务交给线程池更合适。线程池解决的是三件事:复用线程避免频繁创建销毁、限制并发数保护下游资源、统一管理队列与拒绝行为。ThreadPoolExecutor 的核心参数是核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略;执行流程是「核心线程未满就新建 → 核心满了进队列 → 队列满了再扩到最大线程数 → 再满就触发拒绝策略」。常见池型有固定大小、缓存型(SynchronousQueue)、单线程、定时调度,以及 JDK 21 之后的虚拟线程执行器。拒绝策略有 AbortPolicy(抛异常)、CallerRunsPolicy(调用方线程执行,形成反压)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢最老的);选择依据是「任务能不能丢、丢了谁感知」——核心链路用 Abort 或 CallerRuns 让上游感知,日志、埋点类可丢弃。
2. Synchronized 与 ReentrantLock、死锁与加锁规范:Synchronized 是 JVM 内置关键字,自动加解锁、异常也会释放,支持锁升级(偏向 → 轻量级 → 重量级),但不可中断、不能超时、不支持公平与多条件等待;ReentrantLock 是 AQS 实现,提供 lockInterruptibly、tryLock(timeout)、公平锁与多个 Condition,适合需要超时退避、可中断或精细唤醒的场景,代价是必须在 finally 里 unlock,写错就是死锁。死锁的四个必要条件:互斥、请求并保持、不可剥夺、循环等待——破坏任意一个即可,工程上最可行的是破坏循环等待(全局锁顺序)和请求并保持(一次性申请、tryLock 失败就释放重来)。加锁注意事项:锁粒度尽量小、锁内不做 IO 与远程调用;多把锁固定顺序;能用无锁结构(CAS、原子类、LongAdder)就不用锁;优先用并发容器而不是自己给普通集合加锁;必须成对释放且放在 finally;避免锁升级/降级混乱,也不要嵌套加锁;对锁做监控(等待时长、竞争次数),因为锁竞争往往是性能问题最隐蔽的来源。
3. JVM 内存结构、变量位置与 GC 算法选择:运行时数据区包括线程私有的程序计数器、虚拟机栈、本地方法栈,以及线程共享的堆与方法区(JDK 8 之后是元空间,使用本地内存),另有直接内存。栈帧里放局部变量表(基本类型值与对象引用)、操作数栈、动态链接和返回地址。所以 int 这类局部基本类型变量直接存在栈帧的局部变量表里(对象字段则随对象在堆上),Object 变量本身是引用存在栈上、实例存在堆上;字符串常量与类元信息在元空间,字符串字面量在堆里的字符串常量池。GC 算法按场景选:复制算法适合对象存活率低的新生代(Eden + 两个 Survivor,配合对象年龄晋升);标记-清除会产生碎片但适合老年代 CMS 这类低延迟场景;标记-整理适合老年代整体回收(Serial Old、Parallel Old);G1 把堆分成 Region 做增量回收、按回收收益排序,适合大堆且要求可控停顿;ZGC/Shenandoah 用染色指针与读屏障做并发整理,把停顿压到毫秒级,适合超大堆与低延迟要求。
4. 索引设计与 B+ 树、空间局部性:设计索引先问查询模式:等值、范围、排序、分组分别是哪些列的组合,再按「最左前缀」原则设计联合索引,把区分度高且用于等值的列放前面、范围列放最后(范围之后的列无法再用索引定位)。要考虑的还有:回表代价(能覆盖索引就覆盖,减少随机 IO)、索引列不参与函数与隐式类型转换、区分度太低(性别、状态)的列单独建索引没意义、写放大与磁盘占用(每个索引都要维护)、以及前缀索引与索引下推。B+ 树的优点是扇出大、高度低(3~4 层覆盖千万级数据)、非叶子只存键所以页内能放很多键、叶子结点用链表串联支持范围扫描与排序、所有查询路径长度一致所以性能稳定。空间局部性是这一切的底层原因:CPU 一次读入缓存行、磁盘一次读入整页,访问相邻地址时命中率极高;B+ 树把「一次页读取」对应到「几百个键的比较」,而红黑树每层都是一次指针跳转、更可能是随机 IO,数组与顺序扫描之所以快也是同一条道理。
5. ACID 的原子性与一致性落地:原子性靠 undo log:事务修改前先把旧值记录到 undo log,回滚时反向执行恢复;同时配合 redo log 保证「日志先落盘、数据页后刷」,崩溃恢复时用 redo 重放已提交、用 undo 回滚未提交。一致性是目的而不是机制,它由原子性、隔离性、持久性加上业务约束(唯一键、外键、CHECK、应用层校验)共同保证;换句话说,数据库能保证的是「不会出现半个事务」,但不能保证业务语义正确,比如转账双方金额之和不变要靠事务边界与约束来兜。隔离性靠锁与 MVCC:写写靠行锁,读写靠 MVCC 快照,四种隔离级别逐级解决脏读、不可重复读、幻读(RR 下 InnoDB 用间隙锁与 next-key lock 处理当前读的幻读)。持久性靠 redo log 与刷盘策略,innodb_flush_log_at_trx_commit=1 时提交即落盘,代价是每次提交一次 fsync。理解这四条的关键是把日志体系(undo/redo/binlog)与两阶段提交串起来。
6. Redis 为什么快、IO 模型与 Cache-Aside:Redis 快的原因有四层:纯内存操作没有磁盘 IO;单线程命令执行避免了锁与上下文切换(Redis 6 之后网络 IO 多线程,命令执行仍是单线程);IO 多路复用(epoll)加事件驱动,能用一个线程处理大量连接;再加上高度优化的数据结构(SDS、跳表、listpack、渐进式 rehash)与避免大 key 阻塞的编码转换设计。代价是慢命令会阻塞全局,所以要禁用 keys、用 scan 代替、控制大 key 与热 key,并理解持久化(RDB fork 与 AOF fsync)带来的抖动。缓存策略选 Cache-Aside 是因为它最贴合业务:读时先查缓存再查库并回填,写时更新库再删除缓存,缓存不承担写路径的一致性责任、实现简单、缓存故障时系统还能降级直读数据库;Read-Through/Write-Through 需要缓存层代理 DB、Write-Behind 有丢数据风险、双写容易产生不一致。热点 key 的处理方法是本地缓存挡一层、把 key 打散成多副本(加随机后缀)分摊到多个节点、读写分离到从节点、必要时用 Redis Cluster 的多分片或在网关层做请求合并。
7. 秒杀全链路与多分片并发:整条链路是:静态资源 CDN 化 → 网关层限流与黑名单 → 前置校验(活动状态、用户资格、库存预检)→ 扣减库存(Redis Lua 原子扣减或分段库存)→ 生成订单(异步落库)→ 支付与超时回补。100 万人抢一个商品的核心是不能让请求打到数据库:Redis 里用 Lua 脚本把「判断库存 + 扣减 + 记录用户购买标记」做成一次原子操作,返回失败的请求直接丢弃;库存可拆成 N 个分片(每片独立计数)以降低单 key 热点,用户按 ID 哈希路由到固定分片,避免同一用户跨片重复购买。用户同时在多个分片抢购时,先在入口做用户维度的幂等占位(Redis setnx 用户 ID 或布隆过滤器),再用一张「用户 - 商品」唯一约束的订单表兜底,异步落库时的唯一键冲突就是最后一道防线;同时通过 MQ 削峰、批量落库、超卖回补与对账保证最终一致。分布式锁的维度要按竞争资源划分:商品维度锁库存分片、用户维度锁购买资格、订单维度锁状态迁移,粒度越细争用越小,锁只保护临界区、绝不在锁内做远程调用。
8. 消息队列的作用、消费确认与消息幂等:MQ 解决四件事:异步解耦(下单不必等发短信)、削峰填谷(把瞬时流量拉平给下游)、广播与事件驱动(一次事件多方消费)、以及失败重试与最终一致(配合本地消息表 / 事务消息)。判断消费成功要区分「业务处理成功」与「消息确认成功」:RocketMQ 用消费者显式返回 CONSUME_SUCCESS 或 RECONSUME_LATER,消费失败会按退避等级重试,超过次数进死信队列;Kafka 靠 offset 提交,建议业务处理完成后再提交 offset(至少一次),并把幂等做在业务侧。因此消息幂等的标准做法是给每条消息一个业务唯一键(消息 ID 或业务单号),消费前用 Redis setnx 或数据库唯一索引占位,处理完成后记录结果;失败重试返回同一结果。要注意「至少一次」语义下必然有重复,所以消费端必须幂等;同时避免消费端长时间阻塞导致 rebalance 抖动,重试要有上限并配死信告警。
9. RAG 分块与检索效果评估:分块策略要按文档结构定:Markdown/HTML 按标题层级切、PDF 先做版面还原再按语义段落切、表格与代码块单独成块,块大小通常 300~800 字,块间留 10%~20% 重叠防止切断上下文;给每块补上所属标题路径与摘要,显著提升向量质量。评估检索要看四个指标:命中率(正确块是否在召回集里)、MRR/NDCG(正确块的排序位置)、覆盖率(问题类型是否被知识库覆盖)、以及端到端引用正确率与忠实度;没有标注就先用 LLM 造评测集再人工抽检,固定一份回归集,每次改分块或换模型都跑一遍。检索效果不好的排查顺序是:数据是否真的入库、块是否被切碎或过大、query 是否被正确改写、召回是否命中(看原始 topK)、重排是否把正确答案排下去、最后才是模型有没有用上上下文。用户问知识库外的问题时,正确表现是明确说「知识库中没有相关信息」并给出可行建议或转人工,而不是拼凑一个看似合理的答案——这靠检索分数阈值、prompt 约束与低置信度兜底共同实现。
面经 02 / 03 · 大模型应用与工程
10. 线索评分机制与 ROI 提升:线索评分本质是把「哪些线索值得优先跟」变成一个可排序的分数。常见做法是规则与模型结合:规则层用确定的强特征(企业规模、行业匹配、是否主动咨询、是否留了手机号)给基础分,模型层用历史转化的监督信号训练打分模型(LR/GBDT 可解释性好,深度模型或 LLM 抽取非结构化信息),两者融合后分层(A/B/C)并驱动分配与跟进策略。评分高但转化低,通常是三类问题:标签口径与业务目标不一致(优化的是「有回应」而不是「成交」)、样本偏差(高分线索被优先跟进,反馈数据里缺少低分的真实转化情况)、以及特征时效性差(线索意图随时间衰减但特征没更新)。提升 ROI 的做法:把目标标签换成真正关心的指标(成交额、毛利、有效商机),按时间窗口滑动重训,做 A/B 实验比较「按分排序跟进」与「随机跟进」的转化差异,并把评分与动作绑定(高分立即外呼、中分走培育、低分走自动化),同时用增量实验监控特征漂移。要能说清模型的收益口径,而不只是准确率。
11. 强制 JSON 输出与幻觉兜底:Prompt 设计上,先给唯一且明确的任务定义,再给字段清单(名称、类型、是否必填、取值范围与枚举)、一个完整示例和一个反例;明确「只输出 JSON、不要解释、不要 Markdown 代码块之外的文字」,并把输出 schema 直接写进 system 消息。更强的做法是用模型原生能力而不是靠 prompt:JSON mode、function calling 或结构化输出约束(如按 schema 约束解码),它们的可靠性远高于纯文本约束。工程上要三层防线:第一层输出后强校验(JSON Schema),失败把结构化错误信息回填让模型自修正,重试 2~3 次;第二层容错解析,正则抽取 JSON 片段、修复尾逗号、补齐截断的括号;第三层业务兜底,关键字段缺失或置信度低就转人工或走规则默认值,绝不让残缺数据进入下游写操作。参数上,实体抽取与固定分类任务应把 temperature 调到 0 或接近 0、top_p 也压低,减少随机性;同时限制最大输出长度避免跑题,并对同一输入做多次采样投票来提升稳定性。上线要监控结构化成功率与字段缺失率,按模型版本做回归。
12. 端到端 800ms 的耗时拆解与优化:拆解要先能分段打点:网关与鉴权、业务逻辑与数据库查询、向量检索与重排、Prompt 拼装与 token 数、模型首 token 延迟(TTFT)与生成总时长、后处理与落库;把「固定开销」和「随长度增长的开销」分开看。优化手段按收益排序:流式输出让用户先看到内容(感知延迟大幅下降)、把能并行化的步骤并行(多路检索、缓存查询、用户画像获取)、缓存与预计算(热门问题的答案或检索结果、embedding 缓存、prompt 前缀缓存)、检索侧降本(减少召回条数、先过滤元数据再向量检索、用小模型做 rerank)、模型侧分级(简单问题走小模型或规则,复杂问题才走大模型,缩短 max_tokens,关闭不必要的思考模式)、以及数据库侧消灭 N+1 查询和慢查询。做优化前后都要用同一份压测集对比 P50/P95/P99,而不是只看均值——均值 800ms 往往掩盖着 5% 的请求耗时 5 秒。
面经 04 / 05 / 06 · 模型结构与后训练
13. MLA 与 KV Cache 优化:MLA(Multi-head Latent Attention)比 MHA 省的是推理时的 KV Cache:MHA 每个 head 都要缓存完整的 K、V,缓存量随头数与序列长度线性增长,长上下文下显存很快被打满;MLA 把 K、V 压缩到一个低维的 latent 向量,只缓存这个 latent,需要时再通过上投影还原出各头的 K、V,因此缓存量可以减少一个数量级,同时保留了多头表达能力和与 RoPE 兼容的位置编码处理。权重吸收是把上投影矩阵在推理时提前吸收进相邻的线性层(与 W_q、W_o 合并),这样不必真的还原出完整 K、V,减少计算与显存搬运;实现上会遇到矩阵维度不匹配、精度损失与算子融合的问题,需要按框架支持情况调整并行切分方式。KV Cache 的常见 tricks 还包括:分页管理(PagedAttention)消除显存碎片、共享前缀缓存(系统提示与 few-shot 只算一次)、量化缓存(INT8/FP8 存 KV)、滑动窗口或 StreamingLLM 丢弃远古 token、按注意力分数淘汰(H2O 等)、把不常用的 KV 卸载到 CPU/磁盘再按需加载,以及 GQA/MQA 这类用更少的 KV 头降低带宽的方案。
14. PPO/GRPO:损失、critic、KL 估计与资源关系:PPO 维护四个模型:actor(策略,被训练)、critic(价值网络,估计状态价值)、reward model(给回答打分,可为规则或人类偏好训练)、reference model(冻结的参考策略,用于 KL 约束)。训练流程是:用当前 actor 对 prompt 做 rollout 采样 → reward model 打分 → critic 估计优势(GAE:用 TD 残差按折扣因子与 λ 做指数加权累积,兼顾偏差与方差)→ 用裁剪的目标函数更新 actor,损失为概率比乘优势后取 min(clip 限制更新幅度)再减去 KL 惩罚项,同时用均方误差更新 critic。有了 reward model 还需要 critic,是因为 reward 只在序列末尾给一个标量,无法告诉模型「中间哪一步做得好」,critic 提供的价值基线用来算优势,能显著降低梯度方差。GRPO 去掉 critic,改用同一 prompt 下多次采样的组内均值作为基线,省掉一个模型与对应的显存开销,代价是采样数量要够。KL 估计上 PPO 常用 k1(log 比值的直接估计,有偏但方差小)、k3(无偏高方差)、k2(介于两者),GRPO 也沿用同一族估计,选哪个是偏差方差权衡。交叉熵衡量与目标分布的差异、KL 衡量两个分布的整体差异且不对称,KL 在 one-hot 目标下等价于交叉熵减常数,所以分类任务常用交叉熵;把 PPO 的 KL 换成对参考分布的交叉熵在 one-hot 情形下近似可行,但会丢掉「参考分布非 one-hot」时的信息。资源关系上,rollout 数量与 batchsize 决定每次更新的样本量与显存占用,卡数增加使总吞吐近似线性、但单卡 batch 变小会让梯度噪声变大,样本生成(推理)与训练往往要分阶段调度,所以整体不是纯线性——真实采样数也不一定等于 rollout 数,采样后过滤、截断、拒绝采样都会减少实际进入训练的有效样本。
15. 量化、蒸馏与训练推理一致性:量化方案分两类:PTQ(训练后量化,如 GPTQ 用逐层误差补偿、AWQ 按激活重要性保护关键通道)实现成本低;QAT(量化感知训练)在训练时插入伪量化节点、让模型适应量化误差,效果更好但要重训。选择 w8a16(权重 8bit、激活 16bit)是工程折中:权重占显存大头,压到 8bit 直接把显存与带宽降一半,而激活保持 FP16 避免动态范围与离群值带来的精度崩塌;如果连激活也压到 8bit,需要更复杂的离群值处理(SmoothQuant、per-channel scale),在长上下文与 MoE 场景收益不稳定,所以多数线上推理先用 w8a16 拿到稳定的显存与吞吐收益。蒸馏数据的关键是「老师怎么产生、数据怎么筛」:先用强模型批量生成,再用规则过滤(格式、长度、敏感词、事实校验)、用奖励模型或模型自评打分,并做去重与多样性控制(按 embedding 聚类、避免同一模板刷量),最后混入一定比例的真实数据防止分布漂移;清洗阶段要保留 provenance 便于溯源。训练推理一致性指的是训练用 FP16/BF16 而线上用 INT8/INT4、或 kernel 实现不同导致同输入不同输出,表现为评估集指标正常但线上行为漂移;治理方式是固定 kernel 与算子版本、在训练侧做量化模拟、用同一份输入对齐离线与线上的 logits 与生成结果,并把一致性做成上线前的回归用例。