面灵AI→

蚂蚁集团大模型算法岗面经合集:RAG 与 Agent 工程

时间
2026-09
来源
牛客网

《面试题目》

面经 01 · 大模型算法岗(2025-09-12)

  1. 用户的意图识别怎么解决的,准确率能到多少?
  2. RAG 的流程是什么?
  3. RAG 的优化手段有哪些?
  4. Function Call 的原理是什么?
  5. 你们的项目大概有多少人使用了?
  6. 介绍论文。

面经 02 · 大模型算法岗(2025-04-27)

  1. Java 开启多线程的方式有哪些?
  2. 线程池的参数和线程提交步骤?
  3. Agent 相关:RAG、Memory、Function Call 和 MCP 等常见概念。
  4. 场景题:设计转账过程,涉及分布式锁、MySQL 间隙锁、事务。

面经 03 · 大模型算法岗(2025-04-17)

  1. 用过哪些大模型?本地有部署过大模型吗?
  2. 了解 Agent / 智能体吗?
  3. 了解 RAG(检索增强)吗?
  4. 学校里有 AI 相关的经验吗?
  5. 简历里的 CV 论文(残差优化模型)具体讲一下。
  6. 你觉得两个项目哪个比较复杂?简单讲述一下这个项目难在哪、为什么、怎么解决的?
  7. 秒杀业务里 Redis 存了什么东西?为什么要存放用户 ID 和优惠券 ID?有没有其他优化方案?
  8. 这样不会出现第一次在 Redis 中还没操作完又有新请求的情况吗?
  9. 线程池有哪些关键参数?
  10. HTTPS 是如何保证数据安全的?
  11. 工程和算法两个部门分别具体在干什么?工程涉及到算法的部分有多少?
  12. 怎么学习大模型?

面经 04 · 多模态大模型算法岗(2025-04-16)

  1. 项目介绍:遇到的困难、项目背景、怎么与同学或导师合作?
  2. 项目中自己做的事、负责的部分?
  3. 怎么分配任务?
  4. 为什么选择上一家公司,不选择大厂?
  5. 实习最大的收获、成长?
  6. 了不了解咱们这边的业务?
  7. 面试过程中比较感兴趣的业务场景?

面经 05 · 大模型算法岗(2025-04-03)

  1. 拷打项目。
  2. 实习编码过程中遇到了哪些技术问题,如何解决的?
  3. 线上环境如何确保代码是没有异常的,有异常如何处理?

《参考解析》

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 进回归集——能被复现的问题才算真正修好了。