面灵AI→

阿里云 AI Agent 算法一面面经全解析

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

一面

  1. KV Cache 是什么?为什么能加速大模型推理?追问:KV Cache 的代价是什么?长序列推理时显存怎么估算?
  2. Agent 上下文超限了怎么处理?追问:三种策略分别在什么场景下用?
  3. Agent 取消机制怎么做?这个机制你实际尝试过吗?
  4. MCP 是什么?Agent 通过 MCP 调用工具的流程是什么?追问:你觉得 MCP 能成为标准吗?
  5. RAG 中的父子索引是什么?解决了什么问题?追问:这样设计有什么代价?
  6. ReAct 模式和 Plan 模式有什么区别?分别适合什么场景?
  7. 工具返回结果的消息结构怎么设计?为什么需要单独的 Tool Message?
  8. 多个 Agent 同时修改同一个文件,如何避免冲突?
  9. Agent 陷入死循环或重复调用同一个工具,怎么兜底?
  10. 多 Agent 之间通信方式是什么?如何做任务分发和结果合并?
  11. 上下文压缩保留什么信息?如何判断哪些内容可以丢弃?追问:压缩任务交给小模型还是大模型?
  12. 调用大模型 API 失败,排查流程是什么?
  13. 分布式唯一 ID 怎么生成?如果要求自增怎么办?
  14. 缓存击穿、缓存穿透、缓存雪崩是什么?怎么解决?
  15. Redis 为什么能支撑高并发?追问:Redis 6.0 之后加了多线程 IO,还能叫单线程吗?
  16. ThreadLocal 为什么可能内存泄漏?追问:怎么避免?
  17. BeanFactory 和 ApplicationContext 有什么差异?
  18. 配置中心宕机或连不上,怎样保证应用不挂?
  19. TCP 为什么有四次挥手?一定需要四次挥手吗?
  20. 手撕:实现一个执行器,相同 key 的任务串行执行,不同 key 的任务并行执行。

《参考解析》

  1. KV Cache 的原理与显存估算:自回归解码里,每一层历史 token 的 K、V 只取决于它前面的上下文,不会因为新生成的 token 改变,缓存下来就能把「每步重算整段历史」变成「只算当前 token 的 QKV,再和缓存的 K 做一次注意力」。要分清阶段:prefill 那次平方级注意力一分没省,KV Cache 省的是 decode 的重复投影,所以别笼统说「整个生成从平方降到线性」。代价是显存随层数 × 并发 × 上下文长度 × KV head 数 × head dim × 精度字节线性涨,长上下文下瓶颈会从算力转到显存容量与带宽;GQA/MQA 直接砍 KV head 数是收益最大的一招,PagedAttention、前缀复用、KV 量化、滑窗则分别解决碎片、重复前缀、精度和长度问题。报容量数字时别只报理论值,块内碎片、并发峰值、Beam Search 复制和安全余量都要算进去。

  2. Agent 上下文超限:目标不是删旧消息,而是在固定 token 预算里保住「完成当前任务还需要的东西」,所以先给系统指令、工具 schema、当前目标、近期对话、检索证据和输出各留独立预算,权限与硬约束不能被历史挤掉。接着分三档处理历史:近期窗口按 token 和任务依赖切,而不是机械按轮数切;更早的历史压成带版本、时间和来源的结构化任务状态(目标、已确认事实、未完成子任务、约束、决策),新旧冲突时记录「旧值 / 新值 / 依据」而不是静默覆盖;再久远的进外部可检索存储,注意向量相似度只说明「像」,不说明「当前有效」,还得叠新鲜度、来源和权限。真正撑爆上下文的多半是工具输出,压缩应该在工具层做——分页、字段裁剪、聚合、错误摘要、Artifact 引用,只把决策需要的片段塞回模型。选型按场景:短对话用窗口加几个槽位就够,长周期任务要状态加阶段摘要,跨会话个性化才上带权限隔离的长期记忆。

  3. MCP 的调用链路:MCP 是 Model Context Protocol,解决的是 Host、Client、Server 之间怎么发现能力、协商版本、传请求、返回内容和管理连接生命周期,它不替模型决定「该不该调工具」。角色上,Host 是承载 Agent 的应用(IDE、桌面客户端、业务服务),负责权限、上下文和用户交互;Client 跑在 Host 内,通常和一个 Server 建一条逻辑连接;Server 把工具、资源、提示模板暴露出来,背后接本地文件、数据库或 SaaS。一次典型调用是:initialize 握手协商版本与能力,tools/list 拿到按权限过滤过的 schema 交给模型,模型产出结构化调用意图,Host 校验工具名、参数、用户权限和风险级别,Client 用 JSON-RPC 2.0 发 tools/call(本地走 stdio、远程走 Streamable HTTP),Server 执行后返回结构化结果,Host 做体积限制、脱敏和「不可信数据」标记再交回模型进入下一轮。判断「能否成为标准」时可以说:它已经是工具接入层最有影响力的开放协议之一,但统一的是接口不是语义,远程 Server 还带来授权、token 转发和 Prompt Injection 风险,低延迟强事务的内部调用该走 gRPC 还是照走。

  4. RAG 父子索引:它把「拿什么去匹配」和「拿什么去回答」拆成两件事。块太大时一个向量混进多个主题,查询信号被稀释、专有术语也匹配不准;块太小时语义是纯了,但标题、定义、前提和表格上下文被切断,模型拿到手答不准。做法是用较小子块建向量或关键词索引(每个子块带 parent_id、位置、版本、权限),查询先混合召回再做 rerank,命中后映射回父块或相邻窗口,按父文档去重合并、控制总 token 后交给生成模型,同时保留命中位置用于引用。代价要主动讲:子块到父块的映射和版本一致性要维护,多个子块命中同一父块不去重会重复占上下文,父块放大 token 与延迟还可能把没权限的兄弟内容一起带出,所以权限至少要落实到可返回的父级;子块的 rerank 分数不能直接当父块分数,要设计 Max 或 Top-N 聚合;文档更新时父子必须原子换版,否则会出现旧索引指向新正文。父不一定是整篇文档,一个章节、一张带说明的表格、一个函数都可以,尺寸要按文档结构和下游评测调,别硬套 256/1024。

  5. 死循环与重复调用的兜底:先明确「最多 15 步、同一工具每分钟 5 次」是某个业务的配置而不是普适答案,关键是同时限制资源消耗和识别「没有进展」。硬预算要覆盖总步数、token、工具调用次数、总耗时、金额和并发度,任一项耗尽就进受控终止,而不是继续问模型「再试一次」。对 tool_name + 规范化参数 + 关键环境版本做指纹,连续重复且环境没变时不再执行副作用工具,直接回缓存结果或明确告诉它重复了。进展要用可观测信号判断——已解决子目标数、错误类型变化、文件哈希、数据库状态、测试通过数、检索到的新证据——别依赖「最近几轮 Thought 语义相似」,表述会变但行为可能照样在打转。重试只给瞬时错误,配指数退避和抖动;参数错误、权限错误、确定性业务拒绝不该重试;某工具持续失败就熔断、切备用或转人工。退出前保存 checkpoint、已完成事项、失败原因和下一步建议,让断点可恢复。副作用工具必须有幂等键和前置校验,否则「兜底」也拦不住重复发信或重复扣款。

  6. 手撕:同 key 串行、不同 key 并行的执行器:核心不变量只有一条——同一个 key 在任意时刻最多一个任务在跑;不同 key 之间互不阻塞,交给共享线程池并行。实现上给每个 key 建一条 lane(队列 + running/closed 状态),入队和「要不要触发调度」必须在同一个短临界区里原子完成,任务本身丢给线程池执行,绝不在持锁状态下跑业务代码;一个任务结束后再在锁内取下一个,队列取空就把 running 置回 false,并用 remove(key, lane) 这种带值比较的删除避免误删新 lane。最常见的坑是「先发现队列为空、再删除 map 条目」这两步之间被并发 submit 插进来,新任务落进一个马上要被删掉的旧 lane、再也没人消费——所以删除要带 CAS 语义,enqueue 发现 lane 已关闭时重试拿新 lane。剩下的都是工程补充项:每个 key 的队列上限和背压、任务 Deadline 与取消、线程池拒绝后把排队任务统一标记失败、前一个任务抛异常后后续是否继续、热点 key 的公平性,以及长期不释放的 key 怎么回收。