字节跳动(飞书)AI 全栈一面:Agent 项目与性能优化
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 介绍一下 Agent 项目的整体技术架构和工作原理
- Agent 没有遵循 Skill 中的限制和描述时,你是如何解决的?
- 是否尝试过把 Skill 规则写入更底层、优先级更高的规范,以提高遵从性?
- 介绍一下视频处理的完整链路
- 面对海量监控视频,如何避免内存高占用或 OOM?多个长视频是串行还是并行处理,处理时间大约多久?
- 分块上传如何保证成功?断点续传、一致性和清理逻辑是如何实现的?
- 视频文件物理存储在哪里?
- 视频合并失败时,清理逻辑是如何实现的?
- MySQL 索引使用什么数据结构?介绍一下它的组织方式和工作原理
- 一条 SQL 执行需要几百毫秒,你会如何分析和优化?
- SQL 已从 800 毫秒优化到 50 毫秒,但整体接口仍然很慢,如何继续定位和优化?
- 接口总耗时 80 毫秒、单次 SQL 只有 10 毫秒,如果还要继续优化,你会从哪些方面分析?项目中是否使用过缓存或并发手段?
- 两个大整数以字符串表示,最长可达 10 万位,实现大数加法(例如 1 加 99 等于 100)
- 了解堆吗?介绍一下最大堆和最小堆
- 讲讲 HashMap
- 讲讲一致性哈希
- 请做一个简单的自我介绍
《参考解析》
大数相加(最长 10 万位):不能转成整数类型,双指针从两个字符串尾部逐位相加并维护进位,时间 O(n)、空间 O(n)。Java 里推荐预分配 char[] 从后往前填,避免 StringBuilder 反转:
public String addStrings(String a, String b) {
int i = a.length() - 1, j = b.length() - 1, carry = 0;
char[] buf = new char[Math.max(a.length(), b.length()) + 1];
int k = buf.length - 1;
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry;
if (i >= 0) sum += a.charAt(i--) - '0';
if (j >= 0) sum += b.charAt(j--) - '0';
buf[k--] = (char) ('0' + sum % 10);
carry = sum / 10;
}
return new String(buf, k + 1, buf.length - k - 1);
}
边界要主动说:空串、前导零、结果进位多一位(99 + 1 = 100)、负数(这题限定非负,若要支持得先判符号再转减法)。Python 可以直接转 int,但面试要写不依赖大数类型的版本。
一条 SQL 几百毫秒怎么优化:先 EXPLAIN 看 type(出现 ALL 说明全表扫)、key(是否命中索引)、rows(预估扫描行数)、Extra(Using filesort / Using temporary 是危险信号)。常见手段按性价比排序:建或改索引(联合索引守最左前缀,能用覆盖索引就别回表);消除让索引失效的写法(列上套函数、隐式类型转换、like '%x'、or 混用);只 select 需要的列;深分页改成游标或延迟关联;大事务和批量更新拆小;热点数据加缓存。改完再 EXPLAIN 对比,别只看”跑得快了”。
SQL 从 800ms 降到 50ms,接口还是慢:说明瓶颈已经不在这一条 SQL 上。分三步定位:① 埋点或 Arthas trace 看接口内部各段耗时,确认是 DB 总时间还是别的环节;② 统计单次请求的 SQL 条数,N+1 问题(100 次 10ms)比单条慢 SQL 更常见,改成批量查询或 join;③ 往外看——连接池等待时间(HikariCP 的 connectionTimeout / 活跃连接数)、下游 RPC 与缓存 RT、响应体序列化与网络传输(大 JSON、未开 gzip)、GC 停顿和线程池排队、锁竞争。80ms 总耗时 / 10ms SQL 的场景里,剩下 70ms 往往是串行调用多个下游,可以并行化(CompletableFuture)、用本地缓存(Caffeine)挡一层,或把多次往返合并成一次批量调用;也可以把连接池预热、结果集裁剪。
Agent 不遵循 Skill 约束怎么办:按”约束强度”分层治理,而不是反复改 prompt。① 硬约束下沉——能从提示词挪到工具 schema 的(枚举、必填、参数范围)就挪过去,让模型在结构上无法违规,代码侧再做一次校验;② 结构化输出 + 校验失败自动重试(把校验错误回灌给模型让它自纠);③ 中间件拦截,工具调用前 pre-check,非法调用直接拒绝并把原因返回;④ 提示词分层(系统级规范 > 任务 > 用户),避免同一上下文里规则互相冲突,规则条数控制在模型注意力能覆盖的范围内;⑤ 给正反例 few-shot;⑥ 建评测集回归,量化遵从率而不是凭感觉。是否把规则写到更底层的规范里——本质上就是 ①,代价是灵活性下降,适合校验类硬规则,不适合需要模型判断的软规则。
视频链路与防 OOM:链路大致是客户端分片直传对象存储 → 触发转码 / 抽帧(ffmpeg 按关键帧或固定 fps)→ 视觉模型推理(批量)→ 结果按时间轴聚合(视觉标签 + ASR 字幕)→ 落库并生成摘要。防 OOM 的关键是全程流式与背压:不把整个文件读进内存,按分片或按时间窗切任务,生产者—消费者模型配有界队列,worker 并发数按内存预算算(单进程内存 × 并发 < 上限),中间结果落盘做外部排序,限制单进程同时打开的文件数和推理 batch。长视频一般不做大并发,2~4 路并行比较稳妥;耗时与时长、分辨率、抽帧率成正比,一小时视频抽帧加推理通常在分钟到十几分钟量级,回答时给个量级区间并说明受模型和并发影响,比编一个精确数字更可信。分块上传的可靠性靠:前端算分片 hash + 服务端记录已上传分片清单实现断点续传,合并前校验整体 MD5 / ETag,合并失败要把已落盘的分片和临时文件清理掉并标记任务状态可重试(原帖也追问了这一点)。
一致性哈希与 HashMap:一致性哈希把节点和 key 都映射到同一个 02^32 的环上,key 顺时针找到的第一个节点就是归属节点;扩容时只影响环上相邻区间的数据(加一个节点平均只迁移 1/N 的 key),不会像取模那样几乎全量失效。虚拟节点(每个物理节点映射 100200 个副本)用来解决数据倾斜,副本节点用来兜底可用性。HashMap 是数组 + 链表 + 红黑树:默认容量 16、负载因子 0.75、扩容翻倍,哈希扰动 (h = key.hashCode()) ^ (h >>> 16) 让高位参与运算,链表长度到 8 且数组长度 ≥ 64 才树化、退化阈值 6,JDK 8 扩容时按高位拆分,避免重新计算 hash。追问通常落在:为什么容量取 2 的幂(用位运算代替取模)、为什么线程不安全(并发 put 丢数据,JDK 7 头插还会成环)、ConcurrentHashMap 怎么并发(CAS 初始化 + synchronized 锁桶头 + 多线程协助扩容)。