面灵AI→

字节跳动后端一面面经(Agent 与 MCP 方向)

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

《面试题目》

  1. 先讲一下你在实习做的第一个项目,整体的框架是怎样的,用来做什么的?
  2. 你负责的部分 Agent 是怎么实现的,有没有底层的设计优化?
  3. 你怎么看待 JEV,和 BERT 有什么区别?你认为 JEV 对你的 Agent 有什么优化点?
  4. RPC 和 MCP 的接口区别是什么?
  5. 开发 MCP 最重要的是什么?
  6. MCP 定义出参入参,实际过程中有没有出现调用 MCP 入参和出参出错的?怎么防止?
  7. Skill、workflow、Agent 三者的联系和区别是什么?
  8. 你的项目中有没有用到 RSI,怎么做的?
  9. 你的项目为什么要用一个 Agent,而不是直接写一个 Skill 嵌入到里面?
  10. 你们的评审准确率等一些指标是怎么计算的?
  11. 聊一下你的后端项目,简单介绍一下
  12. Redis 通过 Lua 预扣减为什么还要乐观锁兜底?
  13. 如果假设投递消息是 exactly once、不丢不重复,还有必要乐观锁兜底吗?
  14. 强一致和最终一致性
  15. Redis 是强一致还是最终一致性?
  16. Redis 怎么做主从同步的?
  17. master 向 slave 传递数据,这里的数据都是什么?

手撕(共 20 分钟)

  1. K 个一组的链表反转
  2. 两数相加 II(不能用额外空间、输出链表)

《参考解析》

  1. RPC 与 MCP 的区别:RPC 是「调用远端的一个方法」,接口是给程序员看的——服务、方法、参数类型都写死在双方约定的 IDL 里,改接口要改两端。MCP 是「把能力暴露给模型」,接口首先是给模型看的——重点在工具的名称、描述和 JSON Schema,模型靠自然语言描述来决定要不要调、怎么填参数。所以 RPC 的设计目标是类型安全和性能,MCP 的设计目标是可被正确选择。这个差别直接决定了两边的工程重点不同:RPC 追求契约稳定,MCP 追求描述清晰、参数可校验、失败可自修正。

  2. 开发 MCP 最重要的是什么:把「什么时候该用这个工具」写清楚,而不是把实现写得多漂亮。具体三件事——描述里包含触发条件和适用边界(以及不该用的场景),避免多个工具之间语义重叠导致模型选错;参数用 schema 约束类型和取值范围,并在服务端做校验,不要相信模型一定会填对;返回结果要结构化且信息量够,错误信息要能让模型看懂并自我修正(区分「参数填错了可以重试」和「这个操作本来就不允许」)。此外工具如有副作用必须幂等,否则模型重试会重复写入。

  3. 入参出参出错怎么防:入参侧用 schema 校验 + 服务端二次校验,把校验失败的原文回灌给模型让它改,同时设重试上限;枚举值在描述里列全,别让模型自由发挥。出参侧统一包装(成功/失败、错误码、可读消息),避免把异常堆栈直接丢回去;大结果要截断并说明被截断了,否则模型会以为数据就这么多。再往上,把高频出错的 case 固化成评测样例,改一次描述跑一遍。

  4. Skill、workflow、Agent 的联系与区别:Skill 是一段被固化下来的能力(输入输出明确、执行路径基本确定),Workflow 是按固定顺序编排多个步骤的流程,两者都是确定性的;Agent 是让模型自己决定下一步做什么,是非确定性的。三者的关系不是替代而是包含——Agent 会调用 Skill 和 Workflow 作为手脚,能用确定性流程解决的就不该交给 Agent,因为 Agent 更贵、更慢、更不稳定。判据是:路径事先能写清楚的走 Workflow/Skill,路径依赖中间结果、需要临场判断的才上 Agent。

  5. 为什么用 Agent 而不是写个 Skill:Skill 的前提是「步骤固定、分支可穷举」。如果实际任务里下一步做什么取决于上一步拿到的内容(比如先看文档结构才决定读哪一节、评估结果不合格就要换方案),穷举所有分支会变成一棵爆炸的组合树,维护成本比给模型自主权更高。这时 Agent 的价值是把「决策」交给模型。反过来,如果这条路径其实很稳定,那用 Agent 只是把一个 Workflow 包了一层不确定性,是错误的选型。

  6. 评审准确率这类指标怎么算:要有评测集、有判定标准、有分母定义。三个必须说清的点:样本从哪来(真实流量采样还是构造)、单条怎么判对错(人工标注 / 规则 / 另一个模型当裁判,各自偏差不同)、分母是什么(是所有请求,还是模型给出了结论的那些——把「拒答」排除在分母外会虚高)。工程上要做成可回归的:每次改 prompt 或换模型都跑同一套集,看指标变化,而不是只报一个当下数字。

  7. Redis 用 Lua 预扣减后为什么还要乐观锁兜底:Lua 保证的是单次 Redis 操作的原子性,不是整个业务流程的原子性。典型场景是「先扣库存、再落库/发消息」——Lua 扣减成功之后,如果后续步骤失败(进程崩了、网络断了、DB 写不进去),库存已经扣掉但业务没完成,这就需要补偿;反过来,如果业务侧有并发更新同一份数据的情况(比如读出来算一下再写回),Lua 管不到这一步,得靠版本号做 CAS。所以两者解决的是不同层次的并发:Lua 解决 Redis 内部的竞态,乐观锁解决「跨越 Redis 与其它系统」的竞态。

  8. 消息 exactly once 下还要不要乐观锁:要看 exactly once 是在哪一层保证的。如果消息系统真的保证「不丢不重」,那「重复消费导致重复扣减」这一类问题消失了,但并发问题还在——两个不同请求同时修改同一条业务数据,exactly once 完全不管这个,仍然需要乐观锁或数据库行锁。另外「exactly once」在工程上通常指端到端的幂等语义,实现上依然依赖消费端去重,所以消费侧写库时保留幂等键仍然是必要的。

  9. Redis 的主从同步与强一致性:Redis 默认是最终一致——主节点处理写请求后异步把命令转发给从节点,主从之间存在延迟窗口;主节点挂了如果还没同步完,从节点提升后可能丢数据。异步复制的第一阶段是全量同步(主节点 fork 出子进程生成 RDB 快照发给从节点,期间的新写命令缓存进 replication buffer,快照发完再补发缓冲);之后进入命令传播阶段,主节点把每条写命令转发给从节点。传递的数据就是这些写命令流(以及全量阶段的 RDB 快照和 offset/runid 这类同步元信息),不是磁盘文件也不是查询结果。要更强的保证可以用 WAIT 命令等指定数量的从节点确认,但代价是延迟和可用性。