面灵AI→

字节 AI 全栈一面:Agent 资金流转、幂等与消息队列 ACK

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

《面试题目》

  1. 自我介绍。
  2. 讲一下第一个实习 Agent 项目的全流程,以及你负责的部分。
  3. 场景题:Agent 里的资金流转怎么设计?
  4. 检索增强(RAG)链路怎么搭的?
  5. 意图识别怎么做?
  6. Agent 各节点之间怎么做校验?
  7. 工具调用有哪些约束?
  8. 链路里涉及的算法参数如何设置?评测标准是什么?
  9. 幻觉问题如何治理?
  10. 面对用户的鲁棒性行为(乱输入、越权请求)怎么办?
  11. 幂等键是怎么设计的?
  12. 并发情况怎么解决?重复扣款和网络抖动如何解决?
  13. Agent 循环次数、工具调用次数、token 成本怎么控制?
  14. 怎么用 JSON 参数校验和 schema 约束工具入参?
  15. 知识库如何保证时效性?
  16. 什么情况下走人工兜底?
  17. 后端项目里 Redis 限流怎么做的?
  18. 如何保证扣费和任务处理的平衡?
  19. Lua 脚本在项目里具体做什么?
  20. Redis 的 key 是怎么设计的?
  21. 场景题:多级缓存、缓存一致性、并发冲突。
  22. 消息队列:消费者消费完成了但没有返回 ack,会怎么处理?
  23. 超时重传为什么可能让下游更慢?怎么解决?
  24. 进程和线程有什么区别,分别适合什么任务?
  25. 进程间通信方式有哪些?
  26. p99 延迟上涨、平均延迟不变,怎么排查?
  27. 手撕:数组中出现概率最大的前 k 个值。

《参考解析》

Agent 里的「资金流转」本质是把一次有副作用的操作做成可重放的事务。一次下单/扣费要拆成「生成业务单号 → 调用工具 → 记录结果」三段,幂等键用「用户 + 业务类型 + 业务唯一标识」拼出来,写进 Redis 或数据库唯一索引;同一个键重复进来直接返回第一次的结果,而不是再扣一次。网络抖动导致的重复请求、模型重试导致的重复工具调用,都靠这个键收敛。关键是要把「执行前落状态、执行后落结果」分开记,否则进程挂在中途就无从判断到底扣没扣。

消息队列「消费完成但没回 ack」不同中间件行为不一样。RabbitMQ 靠连接是否存活判断,连接断了消息重投,连接还在就继续等;RocketMQ 靠返回状态判断,没返回成功就重试,重试到上限进死信;Kafka 靠提交 offset 标记,没提交下次从头再来。共同点是消息不丢、但大概率重复消费,所以消费端必须幂等。业务上要配死信队列 + 告警,别让一条毒消息无限重投把队列堵死。

超时重传放大流量,是 p99 恶化的常见原因。下游变慢时,客户端超时重试会让同一批请求翻倍打到下游,下游更慢,形成正反馈。做法是:只对幂等接口重试、按错误类型区分(业务错误不重试,只有超时和连接失败重试)、指数退避加随机抖动避免重试风暴、设置重试预算和熔断,必要时在入口限流保住数据库。

多级缓存的核心是「每一层的失效策略」而不是多加一层。本地缓存扛热点、Redis 扛共享、数据库兜底,读路径先本地再 Redis 再回源;写路径先更数据库再删缓存(或延迟双删),并用版本号/逻辑过期避免并发回填把旧值写回去。缓存击穿用互斥锁或单飞,穿透用布隆过滤器或空值缓存,雪崩给过期时间加随机抖动。

p99 涨、均值不涨,说明是长尾而不是整体变慢。排查顺序是:先分实例、分接口、分依赖看 p99 落在哪一段,再看 GC 停顿、连接池排队、慢 SQL、锁等待、下游超时重试;均值被大量快请求稀释,长尾往往来自少数请求的排队或重试,所以要看分位数的分布而不是平均值。