字节跳动 AI Agent 开发面经合集:动态 UI 渲染、MCP 与 Skill 设计
- 轮次
- 面试合集
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
面经 01
面试岗位:AI 全栈开发工程师 | 面试时间:2026 年 10 月 13 日
- 自我介绍。
- 实习:讲一下项目有哪些模块,分别负责什么,怎么交互的?
- 你负责哪个模块?
- 这个数据转发工作经历了哪些模块?
- 讲讲你说的前台和后台分别是承担什么工作?
- 前台有哪些主要的页面和业务?
- 设计模式是什么?
- 技术选型怎么做的?
- DDS 是什么?
- 为什么用 DDS?
- 怎么保证数据的可靠性?
- 怎么取舍 DDS 和 HTTP 的数据通信时延?
- 现在系统的时延状态是怎样的?
- 这个 DDS 是怎么设计的?
- 无人机集群规模是怎样的?
- 如果两个飞机数据被篡改会发生什么?
- 如果飞手抢夺控制软件会怎么处理当前任务?
- 无人机哪些数据要同步更新?
- 举个例子?
- 频率大概是怎样的?
- 在这个频率下有没有遇到前端卡顿的问题?
- 怎么解决的?
- 具体是怎么实现的呢?
- 前端:说说 URL 到页面显示经历的过程?
- 看一下这段代码(一段 JS 事件循环的代码),说说它的输出是什么?
- 说一下为什么是这样的?
- Promise 里面持续 resolve 循环生成微任务会发生什么?
- 后端:数据库应该用的多吧?
- 说说 MySQL 的四种事务隔离级别以及如何实现的?
- RC 上的是什么锁?
- MySQL 索引用的数据结构是什么?
- 为什么用 B+ 树不用红黑树?
- 只是结构特性决定的吗?
- AIcoding:平时主要用 AI 做什么?
- 怎么做的?
- 写了什么 skill?
- TDD 驱动是怎么进行的?
- 手撕:求字符串 s 到其子序列 t 的实现(删除操作)种类数量,提示用动态规划做。
面经 02
面试岗位:AI Agent开发岗 | 面试时间:2026 年 10 月 13 日
- 学校课程多不多?
- 能实习多久?
- 你课程上的怎么样?
- 那个操作系统、算法什么这些都上了吗?
- 讲一下你在最近的这段实习做了什么。
- 你说一下这个,比如说在你的这个工作台里,我输入一句 prompt,然后一直到它能够实现、生成这些东西,这个整条链路是怎么转起来的?
- 这块有了解吗?
- 你刚刚描述的这一段的整个链路其实是一个比较顺的链路,对吧?
- 那整个 Agent 在读用户的 prompt 以及你的 skill 的过程当中,它一轮就能完成这个订单整个的生成吗?
- 还是说它这个 Agent 会有多轮的这种思考或者自动添加的过程,这些是谁来做的?或者怎么做的?
- Agent 的这种循环,或者 Agent 这个 loop 的一些架构有了解过吗?
- 你刚才说的整个路径当中其实有一个关键的点,就是前端后续的一些操作都是基于后端给你的返回的 JSON 去做的,对吧?
- 这个 JSON 的生成成功率是怎么样的?
- 就是你怎么约束说一定能进到你后续的这个链路里?
- 你拿到这个 JSON 然后去渲染这个 widget,有没有自己做过比如说技术方案的一些调研,看过 ChatGPT 或者 Claude Code 他们在做类似的 UI 动态渲染的时候用的都是什么方案?
- 然后你们用的是什么方案?
- 就是比如看一下其他的产品,国内的比如说豆包、Kimi,或者国外的 ChatGPT、Claude Code,他们在做这种动态 UI 的渲染的时候,用的是什么样的方案?
- 然后你们为什么是说采用你们现在这套方案?这个有做过对比吗?
- 那你刚才说到,你们这个 widget 或者是这个东西,它是有交互的是吧?可以去点确定或者编辑、删除这些。那这个过程当中再和后端通信的时候,这块是怎么做的?
- 你修改了之后,这个用户的行为,包括他定的这些东西,你得存到后端的数据库里面不是吗?
- 就是跟后端服务的通信有没有吗?还是说就是纯前端的一个改动?
- 这个组件是动态生成的对吧?
- 那你动态生成的时候,你怎么绑定某一个按钮,可以怎么请求后端的哪个接口?
- 我是想了解这个事情,你这个方案是怎么设计的?
- 那你的这个渲染的 UI 的页面,又可以在用户侧、端侧去做一些交互,对吧?你交互完了之后又要去和后端的服务去做一个通信,是吧?UI 怎么和后端的服务去做一个映射或者是捆绑呢?
- 那不同的按钮或者不同的操作行为,它肯定最后是有不同的这个请求的,对吧?都对应一个后端接口,对吧?只不过是改一些入参什么的?
- 有听说过 Dynamic UI 或者是 A2UI 的这种方案吗?
- 你们这个是流式渲染是怎么做的?就是 Markdown 渲染是吧?
- 结构化的那个 JSON,就是你们整个的相当于一个小卡片,渲染出来是流式出来的,还是说所有的东西都准备好了之后一下出来?
- 增量流式输出会出现哪些问题?
- 你刚才说那个你们那个工具调用有一些 MCP 是吧?然后前面还有一些 skills,你这个对 MCP、skills 这些工具的了解大概是什么程度?
- 有了解过吗?
- 有自己去实现过一些 MCP Server,或者是自己写过 Skills 吗?
- 那你说一下一个 Skill 有哪些组成部分?
- 如果让你写一个 Skill,你会怎么写?
- 你说的这所有的内容是写在一个文件里的是吧?
- 你觉得一个项目里面的话最多能加载多少个 skill 呢?
- 你在做这个前端开发的时候,最后你们的这个项目部署上线,我面向用户去用,假如说我作为一个用户去访问你们的这个站点的话,你们这个站点给我一个 URL,一直到我在浏览器里面把这个 URL 打开,这个过程当中经历了哪些环节?
- 其实这是个比较常规的一个链路对吧?
- 就是从 DNS 的解析到整个的页面去渲染出来,那现在比如说,从你刚才说的这个所有的节点里面,DNS 解析、TCP 连接、HTTP 请求、包括服务端的返回静态资源、然后拼接这个页面,每一个环节想去让你去做一下优化,你会想哪些方案去做这个链路上的优化?
- 就现在比如说这个站点性能有问题,想让你在你刚才所描述的所有的环节里,每一个节点都找一个优化方案,你会怎么去测?
- 你听说过闭包这个概念吗?
- 我现在想让你写一个闭包函数,这个函数就叫做 A,然后这个 A 每次执行的时候可以做一个计数器的效果,比如说我 A 调用一次就返回一,第二次返回二,第三次返回三(P1)。
- 你平时在做这个前端开发的时候,会请求后端的接口对吧?不需要写测试用例,你直接说一下思路。
面经 03
面试岗位:AI 全栈开发工程师 | 面试时间:2026 年 10 月 12 日
- 有在大型公司、正规企业实习过吗?
Agent(50%-70%)
- Agent 是用的框架吗?
- 除了步数耗尽以外,还有什么方法防止 Agent Loop 死循环?
- ReAct、Workflow、Replan 他们分别在什么场景下适用?
- MCP 和 function call 有什么区别?
- Agent 的 state、context、memory、MCP,分别是干什么的?
- Codex 他的上下文压缩机制,有什么优势?
- 已经有了一个 session,怎么跨会话?或者说怎么在新的会话当中使用之前那个会话的信息?
- Skill、MCP、CLI、plugin 分别适用在哪些场景下?
- 如果有 100 个 skill 和 100 个 MCP,应该如何去处理?
- 怎么让 Agent 知道精确地匹配到你写的 MCP?
- SKILL 由哪些组成部分?
- 从 URL 到用户浏览器,是一个怎么样的过程?
- 这些过程哪些地方你可以去优化?
- DNS 优化有哪些?
《参考解析》
JSON 驱动的动态 UI 渲染与交互回传
这套方案的本质是「模型产出受约束的结构,前端按结构渲染组件」。链路:模型返回带类型标识和参数的 JSON(组件名 + 属性 + 数据 + 动作声明),前端有一份组件注册表,按类型映射到真实组件;JSON 用 schema 或结构化输出约束,解析失败要有降级(提示重试、回退成纯文本),并把解析成功率、失败样本记进日志。交互回传是这套方案的难点:按钮的「动作」不能由模型自由编一个 URL,而应声明成后端预定义的动作标识,前端用映射表把动作 id 换成真实接口与参数,入参再按 schema 校验;用户编辑后的状态先落本地,再按动作提交到后端并落库,同时把用户改过的值回灌进会话上下文,保证模型下一轮看到的是最新状态。要能对比业界路线:OpenAI 的交互式组件依赖客户端预置组件与结构化输出,A2UI 这类方案把「组件描述 + 动作绑定」标准化,好处是前端不必为每个新组件发版、安全边界收敛在注册表内,代价是表达能力受注册表限制。面试官追问「UI 怎么和后端映射」,正确的回答是映射表与动作 id 由后端契约决定,模型只负责选择,不负责拼接口。
流式渲染与增量输出的坑
流式输出把「一次完整结果」拆成「边生成边渲染」,体验好但引入四类问题:结构不完整(JSON 流到一半不是合法 JSON,不能直接 JSON.parse,要用增量解析器或先按 Markdown 增量渲染再在结束帧替换成组件)、渲染抖动(卡片高度反复变化、闪烁,要靠占位骨架与最小高度约束)、顺序与依赖(后到的字段依赖前面的字段,要按声明顺序渲染并处理字段缺失)、以及错误可恢复性(流中断时要有明确的失败态与重试入口,而不是留下半张卡片)。工程上把「文本通道」和「结构通道」分开:Markdown 走增量渲染,结构化卡片走「先收完整帧再挂载」,并给每个可交互组件一个稳定 key,避免流式重渲染时把用户已经输入的内容冲掉。指标上要监控首字延迟、结构帧到达率、解析失败率与中断率,否则只看「流式很流畅」是自欺。
MCP、Skill、CLI、plugin 的边界与加载策略
四者解决的是不同问题:MCP 是工具与资源的接入协议,适合需要独立进程、统一鉴权、跨应用复用的能力(数据库查询、第三方 API);Skill 是写给模型的说明文档与资源包,适合承载业务规则、流程步骤和多文件参考资料,本质上靠上下文生效;CLI 是最原始的形态,能力确定、可组合、可被脚本与人共用,适合本地确定性操作(构建、部署、代码生成);plugin 是宿主应用的扩展点,由宿主加载并定义生命周期,适合与宿主深度耦合的能力(编辑器、IDE、浏览器扩展)。选型判据:能力是否需要跨应用复用、是否有凭据与权限边界、是否需要确定性执行。Skill 的组成一般包含元数据(名称与「何时使用」的描述)、正文指令(步骤、约束、输出格式)、可选脚本与参考资料,写法上把触发条件写在最前面、步骤可执行、边界写清。加载数量是硬约束:每个 Skill 的描述都进系统提示,几十个描述的 token 开销与选择噪声就很大,正确做法是分级——常驻少量高层索引,按需展开正文,用检索或显式路由选出候选;100 个 MCP 同理,靠命名空间、按会话授权和懒加载,只把当前任务相关的工具暴露给模型。「怎么让 Agent 精确匹配到 MCP」的答案是描述与命名要写清适用与不适用场景,并把工具选择纳入评测,而不是靠模型自己猜。
Agent Loop 防死循环与 ReAct、Workflow、Replan 的适用场景
防死循环不能只靠步数上限,要多层兜底:①重复检测,对「工具名 + 关键参数」做哈希,连续 N 次相同调用就中断;②无进展检测,比对每轮的状态或产物是否变化,长时间无变化即停止;③预算约束,同时卡步数、token 与墙钟时间;④错误熔断,同一个工具连续失败就换路或直接上报;⑤状态校验,要求每轮必须产生可验证的进展(新增信息、状态迁移),否则回退;⑥人工介入点,在关键写操作前暂停等确认。选型上:ReAct 适合信息获取路径不确定、需要根据反馈调整的开放任务,代价是轮次多、延迟与成本不可控;Workflow 适合步骤可枚举、要求可复现与可审计的流程;Replan 适合计划容易失效的场景(外部状态变化、长任务中途环境改变),代价是额外一次规划调用与上下文膨胀。回答时把判据说清楚(路径能否穷举、失败代价、实时反馈需求),比背定义更有说服力。
从 URL 到页面的全链路与逐节点优化
链路按顺序:DNS 解析 → TCP 握手(HTTPS 再加 TLS)→ 发 HTTP 请求 → 服务端处理并返回 HTML → 浏览器解析 HTML、构建 DOM 与 CSSOM → 遇到资源发起请求(JS 阻塞、CSS 阻塞)→ 执行 JS → 布局与绘制 → 合成上屏。逐节点优化:DNS 用 DNS 预解析、减少域名数、加长 TTL 与就近解析;连接层用 CDN 边缘节点、HTTP/2 多路复用、TLS 1.3 与会话复用、连接预热;请求层做资源合并与按需加载、缓存(强缓存与协商缓存、ETag)、压缩(gzip/brotli)、接口合并与并行;服务端做静态资源直出、SSR 或流式渲染、结果缓存;渲染层把阻塞脚本改成 defer/async、关键 CSS 内联、图片懒加载与预加载、避免布局抖动、用骨架屏提升体感。怎么测是这题的加分项:用 Lighthouse 与 WebPageTest 拿 Lab 数据、用 RUM 与 PerformanceObserver 拿真实用户的 LCP、INP、CLS,把优化前后按同一环境对比;再对照 Nginx 日志与服务端耗时把「网络慢」和「后端慢」分开,避免拍脑袋优化。批量图片请求的场景则答:合并成雪碧图或按需懒加载、用同一域名的 HTTP/2 连接复用减少建连、加缓存与 CDN 命中率、用响应式图片按屏幕尺寸取合适分辨率、通过预加载可视区图片拉齐首屏。
手撕:闭包计数器与子序列计数
闭包计数器用一个外层函数持有私有变量,内层函数每次调用自增并返回:function A() { let n = 0; return function () { return ++n; } }(题干是「A 每调用一次返回一、二、三」,等价写法是 const A = (() => { let n = 0; return () => ++n; })())。要点是讲清闭包为什么能保住状态(内层函数持有对外层作用域变量的引用,外层执行完栈帧销毁但变量被引用而保留)、var 与 let 在循环里的差异、以及内存角度(闭包会让变量常驻,不再需要时置空引用避免泄漏)。「字符串 s 到子序列 t 的删除方案数」是标准的二维 DP:dp[i][j] 表示 s 的前 i 个字符中凑出 t 的前 j 个字符的方案数,边界 dp[i][0] = 1;当 s[i-1] == t[j-1] 时 dp[i][j] = dp[i-1][j-1] + dp[i-1][j](用这个字符或不用),否则 dp[i][j] = dp[i-1][j]。先讲清状态定义与转移含义再写代码,并主动提边界(t 为空、s 比 t 短则结果为 0)与空间优化(只保留上一行),数据量大时注意取模。