面灵AI→

猎聘全栈面试:Agent 编排与分布式锁

时间
2026-10
来源
牛客网

《面试题目》

  1. 你接触过 AI 和 Agent 开发吗?
  2. 具体介绍一下你做过的 AI Agent 项目。
  3. 你的项目是基于 LangChain 还是 LangGraph?
  4. RAG 的检索是怎么实现的?
  5. 能详细介绍一下检索过程吗?
  6. 你们的召回准确率能达到多少?
  7. 项目中遇到过什么技术难点?做过哪些优化?
  8. 你对 LangGraph 的运行机制、Agent 编排和工具调用了解多少?
  9. 你们的上下文压缩是怎么实现的?
  10. Agent 的记忆怎么分层?约束是什么?哪些信息不能压缩?
  11. 你的异步结算链路、消费幂等、失败异常和并发抢占是怎么做的?
  12. SETNX 唯一性兜底是在哪一层做的?MySQL 还是 Redis?
  13. 数据库更新失败以后,Redis 预占的名额怎么恢复?
  14. 你了解 Redis 分布式锁吗?怎么实现?
  15. 分布式锁怎么处理重入、续期和释放?
  16. 怎么避免释放别人的分布式锁?
  17. 你了解 Kafka 吗?RabbitMQ 和 Kafka 有什么核心区别?
  18. RabbitMQ 和 Kafka 在消息顺序性方面有什么区别?
  19. RabbitMQ 和 Kafka 的延迟队列、死信机制有什么区别?
  20. Kafka 能进行消息追溯和重复消费吗?
  21. 你在实习中怎么使用 AI Coding?做过哪些优化?
  22. SDD 是什么框架?你使用过哪些工具?
  23. 团队有没有自研过 SDD 或类似的 AI Coding 框架?
  24. 团队有没有沉淀 Rules 或 Skills?
  25. 除了本地 AI Coding 工具,你们有云端平台吗?
  26. 你觉得 AI Coding 最难的环节是什么?
  27. 你接触过前端开发吗?实习中有没有做过前端?
  28. 你的全栈项目主要面向 C 端用户还是内部 B 端用户?
  29. React 或 Vue 的代码你能看懂吗?能维护现有代码吗?

《参考解析》

RAG 的检索要按「离线建库 + 在线召回 + 重排」三段讲,召回率必须给出口径。 离线段:文档解析与清洗(去页眉页脚、表格转结构化)、按语义而非固定长度切块(带重叠、保留标题层级作为元数据)、embedding 后入向量库并保留 doc_id/版本/权限字段。在线段:query 改写(多轮对话里的指代消解、口语化补全)、混合检索(向量召回 + BM25 关键词召回,两者用 RRF 或加权融合,纯向量在专有名词和编号上很弱)、元数据过滤(按租户、时间、权限先过滤再检索,避免越权召回)。重排段:用 cross-encoder 或大模型对候选集精排,取 Top-N 进上下文。召回率不能说「90% 多」这种没有分母的数,正确说法是先定义评测集(人工标注的 query-答案对,覆盖高频与长尾),再报 Recall@k 和 MRR/NDCG,并说清 k 取多少、基线是什么、优化前后差多少。追问常是「召回率不高怎么查」,答法是从四个方向定位:切块粒度是否把答案切断了、embedding 模型与领域是否匹配、是否缺关键词通道、过滤条件是否过严。

记忆分层与上下文压缩要落到「什么可以丢、什么绝不能丢」。 分层一般是三层:会话内的短期记忆(最近若干轮原文,保证连贯)、会话级的摘要记忆(超出窗口后压缩成要点)、跨会话的长期记忆(用户画像、偏好、历史结论,存结构化存储或向量库,按需检索注入)。压缩的常见做法:滑动窗口保留最近 N 轮、对早期轮次做摘要替换、把工具调用结果只保留结论而丢掉原始大 JSON、对长文档用检索代替全文注入。绝不能压缩或必须原样保留的:用户明确给出的约束与偏好(地域、预算、时间)、系统指令与安全边界、当前任务的目标与已确认的决策、以及工具调用的原始关键字段(ID、金额、状态)。一个常被追问的点是「压缩后怎么保证不出错」——做法是把「结构化状态」和「自然语言摘要」分开存:状态用字段存、可校验,摘要只用于语气和背景,不承载事实。

LangGraph 与 Agent 编排要讲清「状态机 + 显式状态」这个核心差异。 LangGraph 把流程建模成有向图:节点是动作(调用模型、调用工具、做判断),边(含条件边)决定下一步去哪,所有节点读写同一份 state,因此循环、分支、重试、人工介入都是显式的,失败可以从某个检查点恢复,也方便做可观测。LangChain 的链式结构适合线性流程,遇到需要回退和多次尝试的场景就很别扭。工具调用要能说清一轮的完整过程:模型输出工具名与参数 → 框架按 schema 校验 → 执行并把结果作为新消息塞回上下文 → 模型继续推理直到给出最终答案。追问点通常在:怎么防止死循环(步数上限、状态去重、重复调用检测)、工具失败怎么处理(结构化错误返回给模型让它换参重试,而不是抛异常中断)、以及怎么限制工具权限(写操作要人确认、按用户隔离凭据)。

异步结算链路的答案要把「幂等、抢占、补偿」三件事拆开说。 幂等:消费端以业务唯一键(订单号 + 事件类型)建唯一索引或写幂等表,重复消息命中即直接返回成功,而不是靠「消息不会重复」的假设。并发抢占:名额扣减走 Redis 的原子操作(Lua 脚本或 DECR)做第一层拦截,最终以数据库唯一约束或 UPDATE ... WHERE remain > 0 的行锁为准,Redis 只做拦截、不做真相。失败补偿:数据库更新失败时,Redis 里预占的名额必须回滚,靠的是「预占记录带状态与过期时间 + 定时对账任务」——把预占写成带 TTL 的 key 并记录流水,失败路径立刻显式释放,投递到 MQ 失败进重试队列,更极端的情况由对账任务按流水与数据库实际结果对齐(数据库已扣就补记,未扣就释放)。要主动承认这套设计的取舍:允许极短时间内存在「Redis 说没了、数据库其实还有」的不一致,用对账窗口收敛,而不追求强一致——这是大多数名额/库存场景的正确答案。

「SETNX 在哪一层」这类问题的标准答法是:Redis 做拦截层、数据库做兜底层,两层都要有,缺一不可。 只有 Redis 会在主从切换或进程重启时丢锁/丢键,造成超卖;只有数据库则会在高并发下把压力全打在行锁上。所以 Redis 的唯一性 key 是性能手段,数据库的唯一索引或乐观锁是正确性手段,两层用同一把业务键对齐,冲突时以数据库结果为准并回滚 Redis 侧。这个「拦截 + 兜底 + 对账」的三段式几乎能覆盖所有库存、名额、券码类问题。

Redis 分布式锁要按「加锁、续期、释放」三处讲透。 加锁用 SET key value NX PX ttl 一条命令(分开 SETNX 再 EXPIRE 会留下永不过期的锁),value 用带客户端标识的随机串。续期用看门狗(Redisson 的 watchdog 默认续到 30 秒)或自己在业务内定期续,续期的前提是「锁还是自己的」——先校验 value 再续。释放必须用 Lua 脚本做「比较 value 再删除」的原子操作,否则会出现「A 的业务超时后锁过期、B 拿到锁,A 执行完把 B 的锁删了」这个经典事故。重入用 Hash 结构存(key → {客户端 ID: 重入次数}),计数减到 0 才真正删除。必须主动说清单机 Redis 锁的局限:依赖单点,主从异步复制下锁可能丢失,强一致场景要用 Redlock 或直接退化为「数据库唯一约束 + 短事务」,很多团队的最后结论就是「能用数据库约束解决的不要上分布式锁」。

Kafka 与 RabbitMQ 的对比要围绕「消息顺序、投递语义、延迟与死信」展开。 顺序性:Kafka 只保证同一 partition 内有序,所以按业务键(订单号)做分区键才能保证同一实体的消息有序,消费者并发度受 partition 数限制;RabbitMQ 在单队列单消费者下有序,多消费者并发消费同一队列就无序,要保持顺序得靠单个队列加单消费者或者一致性哈希交换机。死信与延迟:RabbitMQ 原生有死信交换机(DLX)与 TTL + DLX 实现的延迟队列(也可用延迟插件),机制清晰、消息级 TTL 支持;Kafka 没有消息级的 TTL 和死信队列,延迟要靠业务层做时间轮、或者单开一个按时间分区的 topic 再回灌,重试通常做成多个重试 topic 逐级退避。重复消费与追溯:Kafka 靠 offset 保留 + 消费者组重放实现「消息追溯」,天然是至少一次投递,重复消费是常态,所以幂等必须由消费端保证;RabbitMQ 的 ack 后消息即删,追溯靠死信队列或业务侧落库。选型结论也要给:日志/流式/高吞吐与重放场景选 Kafka,复杂路由、优先级队列、单条延迟消息、需要精细 ack 的业务选 RabbitMQ。

AI Coding 这块要答「我改了什么流程」,而不是「我用它写代码」。 可讲的具体优化:把任务拆成「先出方案与接口 → 再分段实现 → 每段跑测试」的节奏,控制单次上下文规模;把项目规范、目录约定、常用命令、已知坑写成 Rules/Skills 常驻上下文,减少重复纠偏;用 spec 或文档先固化输入输出与验收标准(SDD 类框架的核心就是这个:先写规格,再让模型按规格实现并自检),把「对不对」从人读代码变成可执行的断言;代码生成后强制跑 lint、类型检查、单测,把不通过的结果直接回灌给模型修;对生成结果做 diff 审查,只接受最小改动。团队沉淀这块,本地有 Rules/Skills、云端有平台,都可以如实说。问「最难环节是什么」,值得给一个有观点的答案:不是生成代码,而是「验证与上下文供给」——模型写得再快,没有可执行的验收标准和准确的上下文,就会把 review 成本推高;其次是跨文件重构时的一致性。这题答出自己的判断比答得全更重要。

前端与全栈能力问题本质是「你能不能独立把一条链路拉通」。 答「能看懂、能改现有代码、能查文档」是安全的,但更好的答法是给出具体范围:能写 React 函数组件与 hooks、能处理路由与状态管理、能看懂构建配置、能定位接口联调问题;Vue 能读能改,但主动说明「如果是从零搭项目,我选自己更熟的那套」。如果确实只在实习里碰过少量前端,如实说清「做过哪几个页面、遇到的具体问题怎么解决的」,比笼统地夸大有用。C 端还是 B 端的问题要答出差异意识:C 端重性能与体验(首屏、SEO、埋点、兼容性),B 端重权限、表单与流程的复杂度,说清自己在项目里真正处理过哪一类。