蚂蚁集团大模型算法岗面经合集:RAG 与 Agent 工程
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
面经 01 · 大模型算法岗(2025-09-12)
- 用户的意图识别怎么解决的,准确率能到多少?
- RAG 的流程是什么?
- RAG 的优化手段有哪些?
- Function Call 的原理是什么?
- 你们的项目大概有多少人使用了?
- 介绍论文。
面经 02 · 大模型算法岗(2025-04-27)
- Java 开启多线程的方式有哪些?
- 线程池的参数和线程提交步骤?
- Agent 相关:RAG、Memory、Function Call 和 MCP 等常见概念。
- 场景题:设计转账过程,涉及分布式锁、MySQL 间隙锁、事务。
面经 03 · 大模型算法岗(2025-04-17)
- 用过哪些大模型?本地有部署过大模型吗?
- 了解 Agent / 智能体吗?
- 了解 RAG(检索增强)吗?
- 学校里有 AI 相关的经验吗?
- 简历里的 CV 论文(残差优化模型)具体讲一下。
- 你觉得两个项目哪个比较复杂?简单讲述一下这个项目难在哪、为什么、怎么解决的?
- 秒杀业务里 Redis 存了什么东西?为什么要存放用户 ID 和优惠券 ID?有没有其他优化方案?
- 这样不会出现第一次在 Redis 中还没操作完又有新请求的情况吗?
- 线程池有哪些关键参数?
- HTTPS 是如何保证数据安全的?
- 工程和算法两个部门分别具体在干什么?工程涉及到算法的部分有多少?
- 怎么学习大模型?
面经 04 · 多模态大模型算法岗(2025-04-16)
- 项目介绍:遇到的困难、项目背景、怎么与同学或导师合作?
- 项目中自己做的事、负责的部分?
- 怎么分配任务?
- 为什么选择上一家公司,不选择大厂?
- 实习最大的收获、成长?
- 了不了解咱们这边的业务?
- 面试过程中比较感兴趣的业务场景?
面经 05 · 大模型算法岗(2025-04-03)
- 拷打项目。
- 实习编码过程中遇到了哪些技术问题,如何解决的?
- 线上环境如何确保代码是没有异常的,有异常如何处理?
《参考解析》
1. 意图识别的方案与准确率口径:成熟的方案是分层而不是单模型:最外层用规则与词典做高置信度快速命中与兜底,中间层用小模型(微调过的 BERT 类)做多分类,最内层用 LLM 处理长尾与需要上下文推理的复杂表达。用 LLM 时不要靠 prompt 让它自由输出,而是用 function calling 或 JSON Schema 把输出约束到固定的枚举集合里,配 few-shot 和清晰的类别定义(写清边界与反例),类别多时先粗分类再细分类。准确率不能只报一个数字,必须带口径:在多大规模的测试集上、分布是否与线上一致、是否包含域外(OOS)样本,同时给出大类准确率、细类准确率、Top-2 召回和拒识率。工程上通常把置信度阈值和澄清反问接起来——低于阈值时不硬猜,而是追问一句,这比强行分类错到下游更划算;上线后按周看混淆矩阵,用线上日志回流补数据。想再抬点时,手段是补边界样本、加入多轮上下文改写、用大模型离线打标再蒸馏到小模型上降本。
2. RAG 的完整流程:分成离线建索引与在线检索生成两段。离线链路是文档接入(PDF、HTML、数据库、工单)→ 解析与清洗(版面还原、表格单独处理、去掉页眉页脚与目录)→ 分块(固定长度加重叠、按标题层级切、或语义切分)→ 向量化 → 写入向量库并同时建关键词索引。在线链路是查询改写(多轮场景先做指代消解,必要时扩写成多个子查询或做 HyDE 用假想答案去检索)→ 混合检索(向量召回加 BM25 关键词召回,各取 top-k)→ 重排(用 cross-encoder 精排,这一步对最终质量影响很大)→ 上下文组装(按 token 预算裁剪、去重、保留来源编号)→ 生成(明确要求只依据给定材料作答、材料不足时直说不知道)→ 后处理(补引用、敏感词过滤)。这套链路必须配评估:检索侧看 Recall@k 与 MRR,生成侧看忠实度、答案相关性和正确率,并且把线上 badcase 回流成固定评测集,否则每次调参都是盲调。
3. RAG 的优化手段清单:检索侧是收益最大的一环——做向量加关键词的混合检索,加查询改写与多路召回,引入 rerank 精排,做元数据过滤(时间、部门、权限),对小粒度块检索、把父级大块喂给模型(父子块),或给命中的句子补前后相邻窗口;领域差异大时用难负样本微调 embedding。分块侧要按语义或标题结构切、保留标题路径信息、表格与代码单独处理,块太大噪声多、太小则语义不完整。生成侧用 prompt 强制引用来源、要求先列证据再下结论、few-shot 示范「材料不足时怎么回答」,温度调低以减少发挥。工程侧做 query 与 embedding 缓存、检索并行化、超时降级、索引增量更新,多租户场景必须做行级权限过滤,否则会越权检索到别人的文档。最后是最容易被忽视的一条:建一套带「期望证据」的评测集,每次只改一个变量并跑回归,避免调好一个坏一个。
4. Function Call 的原理:它不是模型真的去执行函数,而是把「调用意图」结构化输出出来。模型在训练时见过大量「工具定义 + 调用格式」的样本,推理时把每个工具的 name、description 和参数的 JSON Schema 作为一段特殊内容拼进上下文,模型据此生成结构化的调用请求(闭源模型通常走独立的 tool_calls 字段,开源模型靠对话模板约束出 JSON)。真正的执行在应用侧:解析模型输出 → 用 JSON Schema 校验参数 → 调用真实函数 → 把结果作为工具角色的消息回填 → 再次请求模型进入下一轮,直到模型给出最终答案或达到步数上限。几个工程要点:调用链是「模型生成 + 外部执行」两段,模型无法绕过应用直接执行,所以参数永远当不可信输入校验;工具描述的质量直接决定选对率,要写清什么时候该用、什么时候不该用、每个参数的含义与枚举取值,而不是只写功能;工具数量涨到几十个后要按场景裁剪或做工具检索,否则选错率明显上升;必须设最大步数、总 token 和超时限制;并行工具调用与流式增量 JSON 解析是最容易出 bug 的两处实现细节。
5. Agent 里的 RAG、Memory、Function Call 与 MCP:四个概念各解决一个问题。RAG 解决「知识不在参数里」,把外部知识检索进上下文,让回答有据可依、可更新、可引用。Memory 解决「跨轮次甚至跨会话的信息保留」:短期记忆就是对话窗口,超限时做摘要压缩或按 token 预算裁剪;长期记忆是向量库加结构化画像,关键是要定义什么时候写入、怎么召回、新信息如何覆盖旧信息、过期信息如何失效,否则召回一堆无关记忆反而干扰当前任务。Function Call 解决「模型不能直接做事」,把工具能力以 schema 的形式交给模型,由它决定调用哪个、参数怎么填。MCP 解决的是「工具接入的标准化」:用 JSON-RPC 统一暴露 tools、resources、prompts 三类能力,一次实现就能被所有支持 MCP 的客户端复用,取代每个框架各写一套工具适配层。把这四件事拼起来才是 Agent 的主循环:规划 → 调用工具 → 观察结果 → 重新规划,而工程上真正的难点是终止条件、失败恢复、成本与延迟控制,以及可观测性。
6. Java 多线程创建方式与线程池参数、提交步骤:创建线程有继承 Thread、实现 Runnable、实现 Callable 配合 FutureTask、交给线程池、以及用 CompletableFuture/ForkJoinPool 做任务编排这几种,实践中只用线程池,JDK 21 之后还可选虚拟线程。ThreadPoolExecutor 的七个参数是核心线程数、最大线程数、空闲存活时间与单位、任务队列、线程工厂、拒绝策略。提交一个任务的处理顺序是:运行线程数小于核心数就直接新建核心线程执行;否则尝试把任务放进队列;队列满且线程数小于最大线程数就新建非核心线程;再满就交给拒绝策略(抛异常、调用者线程自己执行、静默丢弃、丢弃最老的)。最容易答错的三个点:队列类型决定了参数是否生效——用无界队列时最大线程数形同虚设,只有核心线程干活,任务无限堆积最终 OOM;想做过载保护就用有界队列配「调用者执行」策略形成背压;核心线程默认不会回收,要回收得开 allowCoreThreadTimeOut。线程数按任务类型估算:CPU 密集约为核数加一,IO 密集按「等待时间与计算时间之比」放大。工程上还要用线程工厂给线程命名便于排查、监控活跃线程数与队列长度和拒绝次数,并且不要用 Executors 那几个便捷工厂——无界队列与无限线程正是线上事故的常见来源。
7. 转账场景的设计:分布式锁、间隙锁与事务:先把需求拆清:不能超扣、不能多加、可追溯、可重试。单库内的两账户转账其实不需要分布式事务,把「扣减」和「入账」放进同一个本地事务即可保证原子性,注意别在事务里做 RPC 或发消息,否则长事务长时间持锁。防超扣不要「先查余额再改」,而要用带条件的更新 update account set balance = balance - ? where user_id = ? and balance >= ?,靠唯一索引上的行锁加影响行数判断,既原子又避免并发覆盖。多账户操作要按账户 ID 排序后依次加锁,否则 A 转 B 与 B 转 A 并发时会互相等待形成死锁。间隙锁要讲清它出现的条件与副作用:可重复读隔离级别下,范围条件加 for update 会加 Next-Key Lock(记录锁加间隙锁)来防幻读,代价是会锁住不存在的区间、阻塞其他事务插入;等值查询走唯一索引时只加记录锁,锁范围最小,所以能等值就别写范围条件。跨服务时用分布式锁把同一账户的并发串行化,SETNX 必须配唯一 value 和过期时间、释放时用 Lua 校验 value 防止误删别人的锁,同时要意识到锁过期而业务未完成、Redis 主从切换丢锁这些风险——所以更稳的做法是把幂等键(转账单号唯一索引)和状态机做成最终防线,锁只当优化。一致性与补偿用本地消息表、TCC 或 Saga 加对账兜底;验证手段是并发压测、在事务中途注入异常检查是否全部回滚、以及跑账务平衡校验确认总额守恒。
8. 秒杀场景:Redis 存了什么、为什么这样存、竞态怎么解:Redis 里通常放四类数据:库存计数(用 DECR 做原子扣减)、已参与用户集合(Set 或 Bitmap,实现一人一单)、活动与券的元信息缓存、以及限购计数与风控名单。存用户 ID 的目的是做去重与限购:SADD 返回 1 表示首次参与,天然幂等,重复请求直接被挡在最前面,同时便于核对「已抢到的人」和做风控。存优惠券 ID 是因为一次活动往往有多个券种或批次,库存和去重必须按维度隔离,键设计成 stock:{couponId}、bought:{couponId} 才能按券核对余量、监控异常并支持单品补库存;如果只用一个全局键,不同券之间会互相污染。至于「第一次还没操作完又来新请求」,要区分两个层面:单个 Redis 命令本身是单线程原子执行的,所以单独的 DECR 不会超卖;真正的竞态出在「判断」与「扣减」两步之间——if (stock > 0) DECR 这种先查后减在并发下一定会超卖。正确做法是用 Lua 脚本把「查是否已抢过 + 判断库存 + 扣减 + 记录用户」打包成一次原子执行,或者直接用 DECR 的返回值判断(小于 0 说明已抢完再回补)。即便如此,异步落库仍可能失败,所以数据库侧必须保留 (userId, couponId) 唯一索引和乐观锁扣库存作为最终一致性的兜底,靠消息重试与对账修复差异,并补上热点 key 分桶、本地缓存预热、限流与验证码削峰这些前置手段。
9. HTTPS 如何保证数据安全:它靠 TLS 同时提供四件事:机密性、身份认证、完整性和一定程度的防重放。机密性是握手阶段先用非对称算法协商出会话密钥(现代做法是 ECDHE,具备前向安全,服务器私钥日后泄露也解不开历史流量),之后的数据全部用对称加密传输(AES-GCM 或 ChaCha20-Poly1305),因为对称加密比非对称快几个数量级。身份认证靠证书链:服务器出示由 CA 签名的证书,客户端用内置根证书逐级验签,并校验证书里的域名是否与访问目标匹配、是否在有效期内、是否被吊销,这样才能抵御中间人冒充。完整性由 AEAD 的认证标签保证,任何一位被篡改都会解密失败——TCP 自带的校验和太弱,不足以承担这个职责。防重放靠握手随机数与记录层序列号,重复的报文会被丢弃。握手大致流程是客户端发 ClientHello(随机数、支持的套件)→ 服务端回 ServerHello、证书与密钥交换参数 → 双方各自计算出共享秘密并派生会话密钥 → Finished 报文校验握手未被篡改 → 开始加密通信;TLS 1.3 把往返压到一次(会话恢复可做到 0-RTT),并移除了 RSA 密钥交换与一批弱套件。实践上还要配合 HSTS 防协议降级、证书自动续期、OCSP Stapling 提升吊销检查性能。
10. 用过哪些大模型、本地怎么部署:回答要能横向对比而不是罗列名字:闭源的 GPT、Claude、Gemini 系列能力强、工具调用与多模态成熟,但按量计费且数据出境;开源的 Llama、Qwen、DeepSeek、GLM 等可私有化部署、许可相对宽松。比较维度是参数规模、上下文长度、是否支持 function calling 与多模态、中文能力、推理成本与商用许可。本地部署的关键是算清显存:模型权重约等于参数量乘以精度字节数(FP16 约 2 字节、INT8 约 1 字节、INT4 约 0.5 字节),再加上 KV Cache,后者约为 2 × 层数 × KV 头数 × head_dim × 序列长度 × 批大小 × 精度字节数,长上下文场景下 KV Cache 往往比想象中更吃显存,所以要用 GQA/MQA、分页管理和量化来压。部署框架按场景选:个人试验用 Ollama 或 llama.cpp(GGUF 量化、CPU 也能跑),生产高并发用 vLLM 或 SGLang,靠 PagedAttention 和连续批处理把吞吐拉起来,再叠加前缀缓存、投机解码与张量并行。选型的判断标准是数据能不能出内网、并发与上下文长度要求、以及有没有必要为原型阶段的高成本买单。
11. 工程与算法部门的职责划分,以及怎么学大模型:算法侧管的是「数据与权重」:数据构造与清洗、模型选型与微调(SFT、LoRA、DPO)、评测集与指标设计、badcase 归因、检索与提示策略调优、蒸馏与量化。工程侧管的是「系统与链路」:推理服务部署与弹性扩容、批处理与显存调度、网关与流式输出、会话与上下文管理、工具与插件接入、限流降级、可观测性与成本核算。实际项目里这条线经常被跨过——检索策略、prompt、评测集往往由工程同学一起改,因为落地效果取决于整条链路而不是单点模型,所以被问到「工程涉及算法多少」时,务实的答法是按项目阶段说:探索期算法主导、工程配合做数据管道与评测;上线期工程主导、算法负责效果回归。学习路径建议按「概念 → 动手 → 论文 → 工程」推进:先把 Transformer、预训练与微调、RAG、Agent 这条主线建立成概念地图;再跑通一个开源模型的本地推理、做一个带评测集的 RAG demo;然后读关键论文(Attention Is All You Need、LoRA、RAG、ReAct、DPO)理解每个方法解决什么问题;最后钻营推理框架、量化与评测这些工程细节。只读不练是学大模型最常见的失效方式。
12. 线上如何保证代码不出问题、出了问题怎么办:分三道防线。上线前靠单元与集成测试、静态检查、代码评审、灰度发布(按用户或流量比例放量)和线上流量回放。运行时靠一组标准的韧性手段:每个外部依赖都设超时;只对幂等操作重试并配合退避与次数上限;错误率超阈值时熔断快速失败;入口限流保护自身与下游;关键依赖做线程池或信号量隔离,避免一个慢依赖把整个服务拖死;核心链路准备降级方案(返回缓存或兜底话术)。可观测性要齐三样:带 trace id 的结构化日志能串起全链路、指标覆盖 QPS/错误率/P95 与 P99/单请求成本、告警按 SLO 燃烧率而不是单点阈值触发,否则要么漏报要么天天误报。异常处理上,先分类再动作:业务异常是可预期的,返回明确错误码即可;系统异常要兜底并告警;不可重试的错误绝不能反复重试放大故障。所有 catch 都必须记录上下文(请求 ID、入参摘要、下游返回),禁止空 catch 把异常吞掉;资源用 try-with-resources 或 RAII 保证释放;涉及写操作的接口用幂等键保证重试安全。最后是事后能力:保留现场、配置与模型版本都能一键回滚、每次故障的 badcase 进回归集——能被复现的问题才算真正修好了。