面灵AI→

小红书客户端研发岗面经合集:Agent 工程追问、Android 八股与播放器项目

轮次
多轮面试合集
时间
2026-10
来源
牛客网

《面试题目》

面经 01 · 小红书 客户端研发岗(2026-04-14)

  1. 工具调用率核心提高了 15%,依赖的是什么手段?
  2. 意图不太明确的时候,显式调用的是什么?
  3. 命中率是用什么指标来衡量的?有线上数据吗?
  4. 有去判断模型整体的准确率,以及 token 消耗量吗?
  5. 有考虑过通过大语言模型进行二轮评估吗?
  6. agno 框架和其他框架有什么区别?
  7. 有了解过 harness 工程吗?
  8. 整体上有没有做什么编排调度?
  9. 目前大多数 coding 场景的编排还是比较固定的(规划、生产、评估),有尝试完成这种编排吗?
  10. 你觉得你们公司的场景适合这么做吗?
  11. 上下文工程中长期记忆和短期记忆是怎么组织的?
  12. 怎么保证记忆?
  13. 算法:排序链表。
  14. 平衡二叉树和红黑树的区别是什么?

面经 02 · 小红书 客户端研发岗(2025-12-31)

  1. 效果怎么样?怎么提速?提速的效果如何?
  2. multi-agent 怎么做的?
  3. 你自己做过 MCP 吗?
  4. 上下文压缩怎么实现的?
  5. 为什么要做 LoRA?效果怎么评估的?
  6. RAG 优化怎么做的?
  7. Agent 的评估和打分怎么做的?
  8. JVM 垃圾回收?
  9. SpringBoot 的 AOP 原理是什么?你知道 Spring 出来之前,开发 Java 项目是怎么样的吗?
  10. 为什么要分布式锁?线程问题。
  11. 为什么会有线程问题?举例说明。
  12. 你说你会做上下文记忆优化,怎么做的?
  13. 思维题:按顺序处理、不够就回头,求最后剩下的两个数。

面经 03 · 小红书 客户端研发岗(2025-12-08)

  1. Activity 生命周期?
  2. A→B 的 Activity 生命周期变化?
  3. Handler 机制?
  4. 一个线程有几个 Handler / Looper?
  5. Intent 的数据结构?
  6. Intent 支持传输多大的数据?
  7. ThreadLocal?
  8. 线程池的启动和参数?
  9. ping 一个域名涉及的协议和流程?
  10. SparseArray?
  11. 六大设计原则?

面经 04 · 小红书 客户端研发岗(2025-10-31)

  1. 挑一个项目介绍。
  2. 项目追问:项目动机、具体有哪些功能、UI 设计怎么完成的、学到了什么不一样的内容。
  3. 八股:上面提到的四大组件。
  4. 开发过程中有没有用过内容提供者(ContentProvider)?
  5. RecyclerView 的生命周期,如何实现复用?
  6. 实际开发中有没有做过屏幕的适配?
  7. 如何实现页面的布局?
  8. dp 能详细讲讲吗?
  9. 八股:Handler 的原理。
  10. 八股:什么是内存泄漏,哪些场景会造成内存泄漏?
  11. 八股:安卓中怎么开启线程?
  12. 八股:介绍 UDP 和 TCP,它们原理上有什么区别?
  13. 八股:HTTP。
  14. 八股:POST 和 GET 的区别。
  15. 介绍你研究生主修的方向(联邦学习)。
  16. 联邦学习在客户端的应用,以及核心难点是什么?
  17. 解释 transformer。
  18. 手撕:荷兰国旗问题。

面经 05 · 小红书 客户端研发岗(2025-10-29)

(题目与面经 04 完全相同)

面经 06 · 小红书 客户端研发岗(2025-10-26)

  1. 简历中的安卓项目用到了哪些核心技术,遇到了什么技术问题?
  2. Activity 和 Service 后台播放:Service 能保证一直不被杀掉吗?你用 Activity 做后台播放为什么没被杀掉?
  3. 随机播放你是怎么实现的?
  4. 动态主题适配怎么做的?颜色提取用了什么库?
  5. 项目中的一些卡点,比如一些第三方的算法和库,你是怎么想到要用的?
  6. 本地播放器改为在线播放器需要哪些工作?(引导、推荐算法、缓存)
  7. 客户端怎么和后端进行结合?
  8. 本地的 mp3 和从网络上获取的 mp3 歌曲,在播放层面上有什么区别?
  9. 为什么要用 MediaPlayer?你开发过程中遇到什么问题没有(缓冲时间长、播放卡顿)?
  10. 为什么不选择第三方播放器呢?
  11. 大模型怎么跟小红书的业务结合起来(业务角度),或者怎么跟安卓结合起来(技术角度)?
  12. 算法(说思路):一个二维矩阵,从左到右递增、从上到下递增,给定一个数 n,怎么以最小的时间复杂度检索?

面经 07 · 小红书 客户端研发岗(2025-09-17)

  1. 你的 App 在哪些应用商店上架?当前用户规模是多少?
  2. 这个 App 分了哪几个主要模块(功能)?
  3. 社交功能是什么样的?有没有即时通讯/聊天?
  4. 聊天功能是怎么实现的?消息发送/接收的流程是什么?
  5. 聊天消息怎么存储(server / DB / 消息队列等)?群组、好友列表、通讯录是怎么管理的?
  6. 消息推送你接入了哪些厂商(如 APNs、小米等)?到达率和典型延迟是多少?
  7. 为什么选择 Flutter 做跨平台开发?优点和限制是什么?
  8. 如果需要用到原生平台能力,如何在 Flutter 中与原生通信(如何实现、常用方案)?
  9. 说说你了解的 Flutter 渲染流程。

《参考解析》

Agent 项目的追问:怎么把「效果」说成数字

这一场几乎全程在拆一个 Agent 项目的效果口径,面试官问的是同一件事的四个面:工具调用率提升 15% 靠什么手段、命中率用什么指标衡量、模型准确率和 token 消耗量有没有统计、有没有做二轮评估。回答这类追问的通用结构是「指标定义 → 采集方式 → 基线对照 → 归因」。比如工具调用率,先定义清楚分子分母(是「可调用工具的轮次里实际发起调用的比例」,还是「工具调用成功数 / 应调用数」),再说明采集点(工具调用的入口统一打点,记录 trace、命中工具名、入参摘要、是否成功);提升 15% 的手段通常要归到三类——提示词与工具描述改写(名字与描述更具体、参数 schema 更少歧义)、流程改造(先做意图识别或槽位补全再决定调用,减少「该调不调」)、以及后处理兜底(模型没调时按规则补一次调用、或做一次恢复重试)。命中率则要明确是「检索命中」还是「工具选择正确」,前者看 recall@k 与人工标注,后者看有标注集的准确率;准确率与 token 消耗是必须一起报的一对指标,因为只报准确率不报成本,任何「加一轮评估、加一次重试」的方案都能把指标刷上去。

「有考虑过通过大语言模型进行二轮评估吗」是这一族问题的落点:二轮评估(用模型给结果打分、或做 self-refine / LLM-as-a-judge)的收益与代价要一起讲——收益是能覆盖规则判不了的语义质量,代价是延迟与成本翻倍、评分本身不稳定(同一输入多次打分不一致)、以及模型自评容易偏向自己的输出。工程上的做法是把评估分层:能用规则断言的(格式、字段、工具调用序列)先用规则,规则覆盖不了的用模型裁判并配上评分 rubric,再加人工抽检标定;上线时用「采样评估 + 离线评测集」而不是全量二轮,并且要求评估结果可回放(输入、模型版本、参数、工具返回都存档),否则指标涨了也说不清是哪一处改动的功劳。被追问「你们公司场景适合吗」,判断依据是任务是否有明确验收标准、出错代价与延迟预算——高代价低延迟容忍的场景值得加评估与重试,反之不如把评估放到离线。

记忆与上下文工程:长期、短期与压缩

「长期记忆和短期记忆怎么组织」要把两种东西分清:短期是本次会话的工作上下文(最近几轮消息、当前任务状态、刚调用的工具结果),长期是跨会话沉淀的用户偏好与事实(角色、项目背景、历史结论)。常见的组织方式是三层:常驻区(系统指令与硬约束,始终在 prompt 里)、滑动窗口(最近 N 轮保原文,超出后按 token 预算截断)、检索区(长期记忆按相关度召回 Top-K 拼进上下文,而不是全量塞入)。长期记忆通常落一张表(用户 id、记忆条目、来源、置信度、更新时间、过期策略),写入要靠异步抽取(对话结束后让模型抽结构化条目,或按规则抓取),读取要按当前任务检索并做去重与时效过滤——Mem0 这类方案做的事就是「抽取—存储—召回」这条链路,用向量库加结构化存储,再配一套冲突消解(新事实覆盖旧事实、低置信度不写入)。关系型上下文则用图或显式关系字段存「谁和谁是什么关系」,避免把关系信息塞进自然语言描述里每次重新推理。

「上下文压缩怎么实现」的答案分三档:一是截断与摘要(把久远的对话用模型压成结构化摘要,保留结论、决策与未完成事项,丢掉寒暄与重复内容);二是按需检索(不全量保留历史,需要时再从存储里召回相关片段);三是结构化替代(把长散文换成 schema、状态文件、待办清单,让同样的信息占更少的 token)。要注意三个坑:摘要会丢细节,重要约束必须放在常驻区而不是靠摘要继承;压缩后要保留「决策与理由」这类不能重新推导的信息;以及压缩本身要计成本与延迟(每轮都调用模型压缩会把成本抬上去,通常按 token 阈值触发)。回答「怎么保证记忆」时要落到可验证的点:写入时去重与置信度、读取时按时间与来源排序、过期与纠错链路(用户说「不是这样的」要能改记忆),以及评测(拿固定问题集验证长期记忆的召回准确率)。

框架与协议选型:agno、harness、MCP 与 multi-agent

「agno 框架和其他框架有什么区别」这类比较题,不要背各框架的功能清单,而是给一排比较维度,然后按维度说自己项目的取舍:抽象层级(是给你一个 agent 类加工具,还是给整套工作流与状态机)、多智能体支持(是否内置 team / handoff、还是自己编排)、工具与协议兼容(是否原生支持 MCP、能否接现有函数与 OpenAPI)、可观测性(trace、回放、成本统计是否开箱可用)、以及部署与语言生态。回答时点明「我们选它的原因是 X,代价是 Y」比列举特性更可信。harness 工程被问到时,可以理解为「把模型能力包起来的那层运行框架」——负责工具注册与调用、上下文拼装与压缩、循环与终止条件、重试与降级、以及日志与评估,也就是「除了模型权重以外的全部工程」。自我评估要诚实:如果项目只是调了几次 API、没有做循环与状态管理,就直说当前处在哪一层,然后说清如果要往编排方向走会补什么。

multi-agent 的做法要讲「分工 + 通信 + 收敛」三件事:分工按角色(规划者、执行者、检查者)或按子任务与数据切片;通信靠共享状态(一个任务表或黑板)或显式的消息传递,要点是消息里带结构化字段(任务 id、上游结论、期望产物、约束)而不是自然语言闲聊;收敛要有总步数与预算上限、失败重试上限、以及判定完成的规则,否则会出现互相等待或反复返工。代价必须主动说:token 成本近似按 agent 数翻倍、延迟叠加、上下文同步本身很容易变成新的 bug 来源,所以多智能体的第一条判据是「单 agent 加工具能不能做」。MCP 的定位是协议层:把工具与资源用统一的方式暴露给任意客户端,解决工具与宿主耦合的问题,自己实现过就要能说清 server 暴露了哪些 tool / resource、参数 schema 怎么定义、鉴权怎么带、错误怎么回传。

RAG 与微调:优化点与评估

RAG 优化的顺序是「先查链路再调模型」,标准排查清单可以按四段给:切分与索引(块大小是否匹配问题粒度、是否带标题与层级、是否做了重叠)、召回(向量召回配关键词/BM25 的混合检索,必要时加查询改写与多路召回)、精排(rerank 模型或规则打分,把 Top-K 收敛到真正进入 prompt 的几条)、生成(prompt 里要求引用原文片段、给「资料不足就明说」的出口、控制上下文长度避免中间信息被忽略)。被追问具体效果,要给「召回率、答案正确率、幻觉率、平均延迟与 token」这几项的前后对比,并说明评估集怎么来(从真实问题里抽样 + 人工标注标准答案)。数据过期要靠元数据治理:每条知识带来源、版本、生效与失效时间,检索时按时间过滤,更新用文档哈希做增量重建,过期内容归档而不是直接删。

LoRA 的取舍要讲清「为什么不用全量微调」:参数量与显存代价、训练数据量需求、以及多任务多版本共存的部署成本。LoRA 冻结原权重、只训练低秩增量矩阵,训练显存和产物体积都小得多,可以按业务挂载不同的 adapter,代价是容量受限(对全新领域知识的学习不如全量微调,也不适合把大量事实塞进权重——那本来就该交给 RAG)以及推理时多一层合并或额外计算。效果评估不能只看训练 loss,要拿业务评估集对比基线的准确率、格式遵循率与拒绝率,并做「数据泄漏检查」(评估集不能出现在训练集里);如果评估指标只有困惑度,等于没评估。常见的追问是「LoRA 学到的是风格还是知识」——可以用同一套题分别对基座与 adapter 提问来验证,风格类任务通常提升明显,事实类任务提升有限。

Java 后端八股:AOP、GC 与并发

Spring 的 AOP 原理是「动态代理 + 拦截器链」:容器在创建 bean 时判断是否需要增强,有接口的默认用 JDK 动态代理(基于接口生成实现类),没有接口用 CGLIB 生成子类覆写方法,调用时先过 MethodInterceptor 链(事务、日志、权限注解都由它实现)。要注意自调用失效(同类内部方法调用不走代理)、final 方法与 private 方法不能被增强、以及代理对象的类型与注解读取的差异。追问「Spring 之前怎么开发 Java 项目」是考演进史:没有 IoC 就手工 new 与工厂、没有 AOP 就把日志与事务代码抄在每个方法里或用 EJB 的重量级容器;可以说清 Spring 解决的核心问题是「对象创建与依赖关系的控制反转」加上「横切关注点的抽离」,同时坦率承认 XML 时代的配置负担,以及后来 SpringBoot 用自动配置与起步依赖把它收掉了。JVM 垃圾回收要按「分代 + 算法 + 收集器」三层答:新生代复制(Eden 与两个 Survivor 8:1:1)、老年代标记清除或标记整理;收集器从 Serial/Parallel 到 CMS,再到 G1(按 Region 增量回收、可预测停顿)与 ZGC/Shenandoah(并发整理、毫秒级停顿);常用参数(堆大小、新生代比例、GC 日志、停顿目标)与排查手段(jstat、GC 日志、堆 dump)比背名词更有用。

分布式锁的必要性来自「多实例并发访问同一份资源」:单机的 synchronized 只在一个 JVM 内互斥,多副本部署后必须靠外部系统仲裁,常用 Redis(SET NX PX + 唯一 value + Lua 释放)或 ZooKeeper/etcd。被追问「为什么会有线程问题」要举具体例子而不是概念:i++ 的读改写不是原子操作会丢更新、双检锁的对象发布需要 volatile 防止指令重排、多个共享字段的组合不变式在交错执行下会被破坏、并发容器迭代时的弱一致性、线程池里任务之间共享可变状态等。回答结构可以是「现象(数据错)→ 根因(原子性/可见性/有序性)→ 复现(并发压测)→ 解法(锁、原子类、不可变、线程封闭、消息传递)」。

Android 八股:组件生命周期、Handler 与内存泄漏

Activity 生命周期那条线要能把「A→B 的跳转」逐个数出来:A 的 onPause → B 的 onCreate/onStart/onResume → A 的 onStop(如果 B 不透明,A 可能只到 onPause);返回时 B 的 onPause → A 的 onRestart/onStart/onResume → B 的 onStop/onDestroy。四大组件(Activity、Service、BroadcastReceiver、ContentProvider)各自职责要说清,ContentProvider 用于跨进程共享数据(联系人、媒体库这类场景),现在更多被适配层或 DataStore/Room 取代,但面试官问「有没有用过」是想看你有没有跨进程数据的经验。Service 的追问要点是「保证不被杀」这件事本身做不到:前台服务(带通知)与 START_STICKY、以及对电池优化的申请只是降低被杀概率,正确的架构是把长任务交给前台服务或 WorkManager/JobScheduler、把播放器做成独立进程或媒体会话,Activity 里的播放之所以没被杀,往往是因为它有前台通知、系统把它归到「正在播放媒体」这类豁免里,而不是因为 Activity 本身有什么特权。Handler 机制要讲四件套和两个队列:Handler 负责发送与处理、MessageQueue 按时间排序(when)、Looper 从队列取消息、Message 用享元池复用;一个线程只有一个 Looper、一个 MessageQueue,因此主线程只有一个 Looper,但可以有任意多个 Handler(每个 Handler 绑的是同一个 Looper,靠 target 区分)。追问常落在「为什么主线程死循环不卡」(管道的 epoll 阻塞式等待,没消息时线程挂起,不占 CPU)、ThreadLocal 怎么把 Looper 与线程绑定(每个 Looper 存在 ThreadLocal 里)、以及 HandlerThread 与 IdleHandler 的用途。

内存泄漏要按「生命周期更长的对象持有更短命的引用」这一句根因答,再列场景:非静态内部类/匿名类隐式持有 Activity(Handler 延时消息、AsyncTask、Runnable 都要改成静态 + 弱引用)、单例持有 Context(要用 ApplicationContext)、监听器与回调没反注册、Bitmap/Cursor/Stream 没关闭、Handler 消息队列里的消息未移除。检查手段是 LeakCanary 自动报引用链,线上则靠 Debug.dumpHprofData 抓包后用 Memory Analyzer 找 GC Roots 到泄漏点的最短引用链。SparseArray 用两个数组(key 升序 + value)替代 HashMap<Integer, Object>,省掉了装箱与 Entry 对象,适合 key 为 int 且数据量不大(几百到几千)的场景,代价是查询 O(log n) 而非 O(1)。dp 与屏幕适配那条线:dp 是密度无关像素,px = dp * density,所以同样的 dp 在不同 dpi 上物理尺寸接近但像素数不同;dpi 与窄/宽屏、刘海、折叠屏的适配要讲 dp 加自适应布局(ConstraintLayout、百分比、sw600dp 资源限定符)加最小宽度限定符,而不是强行改 density。RecyclerView 的复用要讲 onCreateViewHolder(创建)/onBindViewHolder(绑定)/onViewRecycled(回收)与四级缓存(屏幕内、离屏、自定义缓存、池)、以及 DiffUtil 做增量刷新;内容是「把 ViewHolder 当成可复用的容器,绑定时不要持有位置假设」——在 ViewHolder 里存 position 或用 setTag 记得位置的老写法,滚动后必然错位。

播放器与跨端项目:Flutter、MediaPlayer 与聊天系统

音乐播放器那组问题的答法是把「能力」和「状态」分开讲。Activity 与 Service 的分工:Service 承载播放控制与媒体会话、Activity 只是 UI,前后台切换靠前台服务加通知栏控制;随机播放的实现要给出可复现的方案(维护播放列表与已播集合、用「随机但不重复」的洗牌模式、或对权重做无放回抽样),并说明「随机」在业务上通常是「随机且不立即重复上一个」。本地 mp3 与在线 mp3 的区别要从数据源答:本地是文件或 content:// URI,可以直接随机定位、无缓冲;在线是 HTTP 流,要处理缓冲、断点续传、码率与格式协商、以及 seek 时需要 range 请求;MediaPlayer 的 prepareAsync 与状态机(Idle/Initialized/Prepared/Started/Paused/Completed/Error)是面试官爱追问的点,卡顿与缓冲的常规治理是预缓冲、双实例切换(下一个播放器提前 prepare)、以及失败重试与降级到低码率。为什么不直接用第三方播放器(ExoPlayer/ijkplayer)这类问题要正面回答取舍:自研可控但编解码格式与弱网处理要自己踩坑,第三方成熟但要评估体积、协议支持与维护状态;面试官想听的是「你评估过,理由是什么」。

Flutter 那组按「选型—通信—渲染」三层答。选型理由:一套代码覆盖双端、UI 一致性高、自绘引擎让动效表现可控、热重载提升开发效率;限制是没有原生控件的原生手感、插件质量参差、包体积变大、平台能力最终还是要写原生。与原生通信的方式有三类:MethodChannel 做方法调用与回调(最常用,双向、可传结构化数据)、EventChannel 做原生到 Dart 的事件流(推送、传感器)、PlatformView 把原生控件嵌进 Flutter 树(性能与层级有代价,尽量少用);通信要注意线程(channel 调用必须在主线程或指定 TaskQueue)、大数据量传输的性能与序列化开销。渲染流程要讲三层:Dart 侧把 Widget 树变换成 Element 树与 RenderObject 树(Widget 是配置、Element 是生命周期与复用单元、RenderObject 负责布局与绘制),RenderObject 产出 Layer 树交给 Skia/Impeller 光栅化,再由 GPU 合成上屏;帧调度由 Vsync 驱动(每帧 16.6ms 内完成 build/layout/paint),掉帧通常来自 build 阶段耗时(大 Widget 重建)或 paint 阶段重绘范围过大(RepaintBoundary 切分)。聊天模块的问题(消息发送/接收流程、存储、群组与好友列表、推送到达率)要按端到端链路讲:客户端本地库先写(乐观 UI)→ 长连接或通道发到服务端 → 服务端落库、生成会话序号 → 推给接收方 → 对方本地入库并按序渲染;离线消息靠拉取与推送补洞,去重靠消息 id,顺序靠会话内递增序号而不是时间戳。推送到达率必须诚实:厂商推送(APNs、FCM、小米、华为、OPPO、vivo)在进程被杀后是主要通道,到达率受机型与用户设置影响很大,通常以「送达率、展示率、点击率」分层统计,并设计拉取兜底。

算法与思维题

排序链表的常规解法是自顶向下归并(快慢指针找中点、递归排序两半再合并),O(n log n) 时间、O(log n) 栈空间;要能说出「自底向上归并可以做到 O(1) 额外空间」,以及为什么不用快排(链表随机访问差)。平衡二叉树与红黑树的区别按「平衡强度与更新代价」答:AVL 要求左右子树高度差不超过 1,查找更平衡但插入删除的旋转更频繁;红黑树只要求最长路径不超过最短路径的两倍(通过颜色约束保证),插删调整次数少,所以 STL 的 map/set 和 Linux 内核用它做有序容器。二维矩阵查找利用「右上角(或左下角)元素是行里最大、列里最小」的性质,每次比较都能排除一整行或一整列,因此 O(m+n);要主动说明它和「逐行二分 O(m log n)」的差异,以及为什么不能直接对整个矩阵二分(行内递增不保证跨行有序)。荷兰国旗问题用三指针(low、mid、high)一趟扫描,把 < pivot 换到左边、> pivot 换到右边,注意 mid 与 high 交换后不能前进(换过来的元素还没检查)。排序链表与荷兰国旗都可能被追问边界:空链表、单节点、重复键、pivot 取不到,写完主动报一下这些用例比等面试官问更稳。