字节跳动抖音电商 Agent 开发一/二/三面
- 轮次
- 一面二面三面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
一面
- 请做一个简单的自我介绍。
- 你第一段实习在哪里,所在团队和主要工作是什么?
- 你在字节实习为什么没有转正?什么时候知道没有转正 HC?
- 这次求职主要倾向什么方向?
- 平时是主要使用 AI 工具,还是也学习大模型和 Agent 的基础知识?
- 舆情分析 Agent 是学校项目还是个人兴趣项目?项目定位是什么?
- MySQL 索引通常是什么结构?InnoDB 中应该怎样设计索引?
- 为什么数据库索引常用 B+ 树,而不是二叉搜索树、红黑树或 B 树?
- B+ 树与平衡二叉搜索树的查找复杂度有什么区别?
- 给定一个订单表和高频查询,这个表和查询的索引应该怎样设计?
- 联合索引为什么通常先放等值过滤列?什么是最左前缀原则?
- 查询要返回 id 和 amount 时,哪些列应该放进联合索引?怎样权衡覆盖索引与索引体积?
- InnoDB 的主键索引和非主键索引有什么区别?
- 查询包含 ORDER BY created_at 时,联合索引应该怎样设计?一定要写 DESC 吗?
- 建立两个独立索引后,底层是一棵 B+ 树还是两棵 B+ 树?两个独立索引怎样参与查询?为什么它们通常不能同时高效完成过滤和排序?
- 对于「按订单和状态过滤、按创建时间取最新若干条」的查询,最终的联合索引和查询过程是什么?
- 使用 LIMIT offset, size 扫描百万或上亿行数据有什么性能问题?怎样用游标分页优化深分页?游标为什么要包含唯一键?
- 如果一个订单有一亿条子订单,需要逐批调用 RPC 并更新状态,怎样设计可靠的离线处理任务?
- Redis 有没有类似数据库二级索引的能力?Redis 和 MySQL 各有什么优缺点,什么场景分别选择它们?
- MVCC 是做什么的?它怎样服务于事务隔离?
- Redis 有事务吗?它与 MySQL 事务有什么区别?
- Redis 命令大体串行执行,为什么业务上仍然会出现并发问题?Redis 6 引入 I/O 多线程后,是否变成多个线程并发修改同一个 Key?
- 为什么不能先 GET 判断锁不存在再执行 SET?SET NX 解决了什么问题?一个基本正确的 Redis 分布式锁应该怎样获取?
- 已经使用 SET NX,为什么释放锁还需要 Lua?为什么不能直接 DEL?如果业务没有执行完锁就过期了,怎样处理?
- Pipeline 和 Lua 分别解决什么问题?Pipeline 中的一批命令是原子的吗?
- Redis 内存达到上限时有哪些淘汰策略?除了 LRU,还了解哪些缓存替换算法?
- LRU 的核心原理和数据结构是什么?get、put 的复杂度是多少?
- 请用你熟悉的语言实现一个支持构造、get 和 put 的 LRU Cache。
二面
- 请简单做一下自我介绍。
- 是否收到面试前的 AI Coding 题?目前完成得怎么样?展示一下完成合同审核题的过程,你是怎样使用 AI 开发的?
- 你对 AI 生成的技术方案提了哪些修改?AI 给出的技术方案很长,人应该怎样 Review、抓重点和做取舍?
- 除了主要流程,Review 和开发时还关注哪些点?当前实现处于什么阶段?不接模型的 baseline 能做什么?
- 输入有歧义时,下面输出的关系是怎么得出的?有歧义就不建立关系吗?
- 原始信息不完整、不确定时,所谓「默认结果」应该如何处理?结论会不会也有问题?
- 开发时是否给 AI 明确说明过这些边界情况?是否确认过它的处理逻辑?
- 这道 AI Coding 题实际花了多长时间?如果时间更充足,你会怎样继续优化,并怎样和 AI 交互?
- 没有产品同学可以确认需求时,遇到模糊信息怎么办?如果产品同学也是新人,也不知道正确答案,怎么办?
- 两个条款名称很相似,怎样确认它们是不是同一个条款?能否直接制定一个判断逻辑?
- 最终报告怎样区分确定的关系和疑似关系?置信度应该怎样处理?
- 如果给你两天时间,最应该优先优化哪些模块?
- 是否需要引入大模型?哪些部分交给模型,哪些部分交给代码规则?判断一个任务适合模型还是规则,有什么通用标准?
- 怎样判断最终报告的质量,确认输出的关系是否正确?将报告与人工标准答案比较后,发现差异应如何迭代?
- 差异一定要人工逐个分析吗?这个过程怎样利用 AI?让 AI 分析差异、辅助修复,需要提供哪些信息?
- 只知道正确答案、不知道最佳实现策略时,怎样让 AI 帮忙?告诉 AI 自己设想的实现策略,还是提供输入和正确输出让它推导,哪种更好?
- 是否实践过把测试用例交给 AI,让它验证、发现偏差并自行迭代?
- 合同变成一两百页、超过 Agent 上下文窗口时怎么办?文档具体怎样切分?如何处理切分边界和块大小?
- 文档切分以后怎样得到最终答案?不同子 Agent 分别处理文档块之后,怎样汇总结果?
- 汇总后的结果又超出上下文怎么办?如何让 300 页合同的处理链路真正跑通?
- 压缩或丢弃信息时,怎样判断哪些信息无效?渐进式披露在这个合同场景中具体怎样落地?
- 如果业务强约束 AI 不能基于虚构信息得出关系和结论,工程上怎样设计?
- 用另一个 Agent 评估结果,但评估 Agent 本身也有幻觉,怎么办?判断结论是否基于事实,一定要经过 AI 吗?怎样把非 AI 校验纳入工程链路?
- 为什么没有继续在原团队实习并转正?实习期间怎样学习 AI、从哪里获取信息?
- 信息很多时优先关注什么?关注之后有什么实践动作?学到的新知识有没有用到实习开发中?
- 除了辅助开发,AI 有没有用到国际支付风控的业务场景中?
- 怎样理解 CLI 和 MCP Tool,它们有什么区别?什么场景使用 MCP,什么场景使用 CLI,什么场景需要 Skill?
- 同一项能力支持多种接入方式时怎样选择?自己的业务能力如何开放给 Agent?
- 如果重点追求性能和低延迟,CLI、MCP、Skill 应该怎样选型和设计?
三面
- 请做一下自我介绍。
- 你在实习团队拿到转正 Offer 了吗?为什么没有转正?
- 介绍一件实习期间能体现个人技术成长和能力的事情。
- 可以现场演示一下舆情分析 Agent 产品吗?这个项目是否已经上线并被真实用户使用?
- 项目的目录结构是什么,为什么这样设计?你认为项目中最关键的代码和技术方案是什么?
- 为什么自己实现这套流程,而不是直接使用成熟的开源 Agent 框架?
- 为什么要持久化任务、工具调用和执行过程数据?
- 算法题:实现「最短无序连续子数组」——给定一个无序数组,找到最短的连续覆盖区间,对区间内的元素递增排序,使得数组整体递增。
《参考解析》
索引为什么是 B+ 树:从磁盘 I/O 次数倒推
数据库索引的瓶颈不在比较次数,而在磁盘随机读的次数。红黑树、二叉搜索树都是二叉的,树高是 log₂N,千万级数据就是二三十层,最坏情况接近二三十次随机 I/O;B 树的每个节点可以存多个键,树高降到了 logₘN,但它的数据行同时挂在内部节点和叶子节点上,范围扫描要在层与层之间来回跳。B+ 树把这两点都解决了:内部节点只存键、不存数据,同样大小的页能塞进更多键,树高进一步降到三四层;所有数据行都在叶子节点,且叶子之间用链表串起来,BETWEEN、ORDER BY、LIMIT 这类范围操作只要定位到起点再顺着链表走即可。代价是等值查询必须一路走到叶子,但多走的一两次内存级比较远比省下的磁盘 I/O 便宜。InnoDB 的页默认 16KB,按这个设计三层 B+ 树大致能撑两千万行左右的表。
联合索引的列序:等值在前、范围在后,覆盖索引换回表
联合索引 (a, b, c) 的排序规则是先按 a 排、a 相同再按 b 排,所以只有「a 等值、b 等值、c 范围」这种前缀连续用上的写法才能把索引当有序结构来用——这就是最左前缀原则。因此设计列序的口诀是等值过滤列放前面、范围列放后面、排序列紧跟等值列。WHERE order_id = ? AND status = ? ORDER BY created_at DESC LIMIT 20 的理想索引是 (order_id, status, created_at),一次索引扫描就能同时完成过滤和排序,省掉 filesort。要不要把 amount 也塞进去做覆盖索引,是体积与回表的权衡:覆盖索引能免掉回表,但如果 amount 很大、或者查询还会要别的列,把宽列放进索引会让每个索引页能装的键变少、树变高、写放大也更大。ORDER BY 的 DESC 在 MySQL 8 里不必显式声明——索引可以正向也可以反向扫描,真正影响能否用上索引的是排序列有没有落在索引里、以及多列排序方向是否与索引一致,混合方向(一个 ASC 一个 DESC)在 8.0 之前就没法用索引排序,8.0 之后才有降序索引。
深分页与游标分页:为什么 offset 越翻越慢、游标为什么要带唯一键
LIMIT offset, size 的代价与 offset 成正比:服务端必须先把前 offset + size 行取出来再丢掉前 offset 行,LIMIT 1000000, 20 意味着要顺着索引扫一百万行、还要对每一行回表取数据。翻到后面越翻越慢,本质是在为「已经看过的数据」重复付费。游标分页(keyset pagination)换了个思路:记住上一页最后一条的排序键,下一页用 WHERE (created_at, id) < (?, ?) ORDER BY created_at DESC, id DESC LIMIT 20,直接从索引的定位点往后取 20 行,复杂度与页深无关。游标必须带唯一键,是因为排序列可能有大量重复值——只用 created_at 做游标时,同一毫秒内的多条记录在两次查询之间没有确定顺序,会出现漏行或重复行;把主键拼进游标与排序键,排序才是一个全序,翻页才稳定。它的代价是不能随机跳页,只能上一页/下一页,这正好也是信息流产品的交互形态。
Redis 分布式锁:SET NX PX 加锁、Lua 校验持有者后释放、过期靠续期兜底
先 GET 再 SET 的问题在于这两步之间不是原子的,两个客户端可以同时判断出「锁不存在」然后都去 SET,锁就失效了。SET key value NX PX 30000 把「判断不存在」和「写入」合成一个原子命令,是加锁的最小正确形态。释放锁必须用 Lua,是因为要保证「校验 value 是不是自己持有的那个 token」和「删除 key」两步原子:直接 DEL 会误删别人的锁——A 的业务超时、锁自动过期,B 拿到了锁,此时 A 执行完回来 DEL,删掉的是 B 的锁。Lua 脚本里先 GET 比对 token、一致才 DEL,Redis 单线程执行脚本,中间不会被插队。业务没执行完锁就过期,是所有方案都要面对的根问题:可以开一个看门狗线程按锁租期的三分之一续期、把租期按业务 P99 放大、或者给业务加可重入与超时中止;但根本上要接受分布式锁只能降低冲突概率,真正不能重复执行的操作还得靠下游的幂等键兜底。
Redis 命令串行执行,为什么业务上仍然会并发出错
「串行执行」说的是 Redis 内部对单条命令的处理不被打断,不等于「多条命令组成的一个业务动作」具有原子性。GET 判断不存在、再 SET 写入,这是两条命令、两个事件循环轮次,另一条连接完全可以在中间插进来。Redis 6 的 I/O 多线程只是把网络读写与协议解析分摊到多个线程,命令的执行仍然是单线程的(核心的命令执行路径没有放开),所以它既没有引入「多线程并发改同一个 Key」的新问题,也没有把多条命令变成原子操作。要跨越单命令的边界保原子,只有三条路:用 Lua 脚本把多步操作打包(脚本内不会被插入其他命令)、用 MULTI/EXEC 事务(注意它不提供回滚,只是批量执行,且不能在中间读结果做判断)、或者用 WATCH 做乐观锁重试。
LRU 的实现:哈希表定位 + 双向链表维护时序
要在 O(1) 内同时完成「按 key 查值」和「按访问时序淘汰」,必须一个哈希表配一个双向链表:哈希表的 value 指向链表节点,链表头是最新访问、链表尾是最久未用。get 命中时把节点摘下来插到头部,未命中返回空;put 若 key 存在则更新值并移到头部,不存在则新建节点插到头部、把 size++,超过容量就删掉尾节点并从哈希表里移除。两处容易写错:一是删除尾节点时必须同步从哈希表里删掉对应 key,否则 map 会泄漏;二是 put 已存在的 key 时不能重复插入新节点。更进一步的话题是「Redis 的 LRU 并不精确」——Redis 3.0 之后用的是近似 LRU,随机采样若干 key 淘汰其中最久未用的,为的是省下维护全局有序链表的指针开销和内存;练手可以从零实现精确 LRU,再把 LRU-K、LFU(Redis 4.0 的 allkeys-lfu)以及「扫描式淘汰」的取舍讲清楚。
长文档超出上下文:分层切分 + 并行子任务 + 有损汇总 + 渐进式披露
把一两百页合同塞进单个上下文,工程上拆成四步。第一步切分要按语义边界切而不是按固定字数切:先按条款/章节标题做一级切分,超长的条款再按段落重叠切(例如每块带 10% 重叠),重叠是为了不让一个跨块的实体被切成两半。第二步并行处理:每个块交给一个子任务抽取「实体 + 关系 + 出处」,输出结构化 JSON 而不是自然语言长文,因为结构化结果的体积比原文小一到两个数量级,这一步就是把文档压小的关键。第三步分层汇总:先合并同一实体的多块结果并去重,再让上一层只读合并后的摘要而不是原文,如果仍然超限就再建一层——树形归并的层数与文档规模成对数关系。第四步渐进式披露:主链路只带全局摘要和索引,需要细节时再按索引回查具体块,避免把全部细节常驻上下文。判断哪些信息可以丢,标准是「丢了以后还能不能支撑最终结论」:与结论无关的修饰、模板条款可以丢,涉及金额、期限、责任主体、例外条件的必须保留,并记下它在原文的位置以便复核。
模型与规则的边界:确定性的事交给代码,语义理解交给模型
划分标准可以落成几个可判定的问题。一是「答案是不是唯一且可枚举」:日期格式校验、金额加减、编号是否连续、字段是否为空,这类有确定判定过程的必须交给代码规则,用模型做只会引入不确定性还更慢。二是「输入是否规范化」:两个条款名称相似但不完全相同,靠字符串匹配无法判定,要靠语义理解,这就该交给模型,但要让模型输出「是同一实体/不是/不确定」三态而不是必须二选一。三是「错了的代价有多大」:涉及金额、法务责任、对外结论的关卡加代码校验(汇总是否等于明细之和、外键是否闭合、数值是否在量级区间内),模型只产出候选、由规则决定能否放行。四是「能否给出处」:模型给出的每条关系都要能回溯到原文位置,指不出出处的一律降级为「疑似」,交由人工确认——把置信度当一等公民,比调提示词更能提升可信度。至于质量评估,可行的做法是先用少量人工标好的标准答案做对照集,把差异按类型归类(漏抽、错抽、边界判错),再让模型辅助分析差异原因并给修复建议,但「用模型评模型」不能闭环,必须保留规则校验与人工抽检。
CLI、MCP Tool 与 Skill 的选型
三者的差别在调用路径和上下文开销上。CLI 是一次进程调用,启动与退出有固定开销,但输入输出走管道、可以流式、便于本地调试和复用现有命令行工具,适合批量、离线、需要和文件系统打交道的能力。MCP Tool 把能力以结构化 schema 暴露给模型,省掉「怎么调、参数是什么」的猜测,代价是每个工具的定义都要占用上下文、且走一层协议转发,工具数量一多上下文会被工具清单吃掉;追求低延迟和高吞吐时,一次问答里挂几十个工具反而不如给一个 CLI 入口加 --help。Skill 更接近「按需加载的知识与流程」,它不提供运行时能力,而是在合适的时机把领域步骤和注意事项注入进来——所以判断标准是:能力和执行属于 CLI/MCP,流程和判断属于 Skill。业务能力对外开放给 Agent 时,优先给稳定、幂等、可校验的接口,把鉴权与配额留在服务端,而不是靠提示词约束模型不要乱调。