虾皮研发工程师一二面:五路召回、加权 RRF 与消息队列压测
- 轮次
- 一面+二面+HR面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
一面
- 请做一下自我介绍。
- 完成 RAG 功能时,你给 AI 提供了什么目标、代码和上下文?
- 你如何把 RAG 开发拆成多个任务,并判断每一步已经完成?
- AI 第一次产出了哪些错误或不合理设计,你是怎样发现并修正的?
- 最终用了哪些测试、评测和数据证明 RAG 项目有效?
- “日均 2.7 万入团用户”的统计时间窗口、去重方式和用户口径是什么?
- “每位新人 0.5 个关注”“75% 回关”等数字来自实验观测还是估算?
- 实验上线后除了曝光、点击和关注转化率,还应观察哪些体验和稳定性护栏?
- 消息队列实际测过的单机吞吐、延迟、并发连接数、队列数、消费者数和消息大小分别是多少?
- Broker 重启后的恢复耗时是多少?
- 如何通过故障注入验证消息重复投递率和丢失率?
- 如果相关数据还没有测量,你会优先建立哪些基线、压测方法和验收标准?
- 以一个技术问题为例,完整说明 RAG 从用户输入到答案输出的在线查询链路。
- 为什么需要五路召回,而不是只做向量召回或向量加全文召回?
- 同一 chunk 被多路召回命中时如何合并和去重?
- RRF 的公式是什么,各通道权重如何设置?
- 融合分数相同时,如何保证排序稳定和结果确定?
- Reranker 超时时返回哪个阶段的结果,怎样标记降级?
- RAG 中哪段核心代码是你独立实现的?请举一个删除、修复或调整权重的真实例子。
- 40 条评测问题怎样分层,31 项测试分别覆盖什么?
- 如何验证检索服务从约 1800 行重构到约 500 行后行为等价?
- 如何构造困难样例、对抗样例和失败样例?
- 在两个 AI Agent 实操方向中选择一个,如何先写清问题定义、技术设计、接口、约束和测试范围?
- 如何让 AI 实现五路混合检索融合模块,并验证 RRF、去重、标题加分、文档配额和 reranker 超时降级?
- 请解释 AI 生成的方案和代码,并找出确定性、性能、超时传播或数据结构方面的问题。
- 不使用递归,手写二叉树的中序遍历。
- 你有什么问题想问面试官?
二面
- 请做一下自我介绍。
- 四个月实习中,投入最大、技术成长最多的项目是什么?
- 简历中的开源消息队列是否由你独立完成?
- Broker、Topic、ACK、持久化和通信等核心模块是否都是你做的?
- 消息队列实际测过的单机吞吐、消费延迟、消费者数量和重启耗时分别是多少?
- RAG 项目是否也是你个人完整实现的?
- 选择加权 RRF 时,主要考虑了哪些指标和融合因素?
- 40 条评测集的 Recall@K 和引用正确率是多少,有没有大致数据?
- HNSW 索引是 Agent 帮你实现的,还是使用了开源库?
- 你还了解哪些向量索引方案,什么时候选择 HNSW,为什么不用其他方案?
- 与 AI 联合开发 RAG 时,是否使用过 SDD、Spec 或其他开发范式?
- 面对包含入库、检索和测评的大项目,你怎样向 Agent 描述并拆解任务?
- AI 生成数百甚至上千行代码时,除了单元测试,你怎样确认它符合预期且交付正确?
- 请使用 std::nth_element 返回数组中的第 N 大元素。
- 这段实现的时间复杂度和空间复杂度是什么?
- 对按 . 分段的字符串,如何处理普通字段、* 匹配一段和 # 匹配零段或多段?请先说明算法、复杂度和边界用例,再完成编码与测试。
- 团队负责什么业务,新人入职后通常会做什么?
- AI 除了写代码,还能怎样提升广告研发效率?AI 时代工程师应保留什么能力?
- 搜广推研发当前最大的技术挑战是什么?
《参考解析》
RAG 检索链路:为什么五路召回、加权 RRF 与 reranker 降级
这场虾皮研发岗的节奏是 9.10 一面、9.21 二面、10.9 HR 面,一面不走常规问答,而是让 AI 按简历生成整套卷子、自己在卷面上作答——题量四十多道,几乎全围着简历里的两个项目(RAG 与自研消息队列)转,泛泛而谈会当场露馅。
为什么不是只做向量召回或「向量 + 全文」,关键在于两类召回的失效方式不同:向量召回吃语义改写,却会漏掉编号、专有名词和精确字段;全文(BM25 一类)正好相反,字面命中强但不理解同义替换。补到五路,通常是在这两路之外再加标题与字段加权召回、结构化过滤(时间、类目、文档配额)和更细粒度的段落级召回。收益是召回率上限变高,代价是候选集变大、融合与 rerank 的延迟和成本上升——路数不是越多越好,要按评测集上的边际增益决定。
RRF 解决的是各通道分数量纲不可比的问题(cosine 相似度和 BM25 分根本不在一个尺度上),所以它只用排名:score(d) = Σ wᵢ / (k + rankᵢ(d)),k 一般取 60 左右做平滑,wᵢ 是通道权重。权重别凭感觉给,做法是在验证集上做消融,看每个通道单独拿掉后 Recall@K 掉多少,再按贡献分配。同一 chunk 被多路命中时用稳定 id(文档 id + chunk 序号)合并累加分;同一文档多个 chunk 命中要先按文档聚合成一个候选再排序,否则一篇长文档会霸榜。
排序确定性是最容易被忽略的追问:RRF 分数撞车很常见,尤其候选集小时,如果依赖 sort 的稳定性或哈希遍历顺序,同一个 query 两次请求可能给出不同结果,评测就无法复现。工程做法是补一个全序的二级键,比如文档 id 或入库时间。reranker 超时则要提前设计降级:超时预算用尽就返回融合阶段的结果,并在响应里带上降级标记(降级阶段、超时阈值、是否 degraded),让上游和评测能把它与正常结果区分开,同时保证超时能在调用链上传播、不把整条查询链路拖死。
评测与数据口径:Recall@K、评测集分层与 AI 协作验收
40 条评测集要讲清分层:按查询类型分(事实型、多跳、长尾、对抗/无答案),每一层的指标意义不同;31 项测试一般覆盖检索命中、引用正确、拒答、边界与失败用例。指标别只报一个 Recall@K——召回率看上限,MRR/NDCG 看排序质量,引用正确率(答案是否有据、能否溯到原文)看生成侧,再补拒答率和 P95 延迟。40 条这个量级方差很大,单项差几分不一定是真改进,所以固定随机性(温度 0、固定索引与排序二级键)并分层看,比盯总分可靠。
1800 行重构到 500 行怎么证明行为等价:拿同一批输入跑新旧两版,逐条比对返回的文档集合、排序和融合分,差异都要能解释;纯函数部分留黄金用例快照,线上可以用影子流量对照。困难样例、对抗样例和失败样例不是随机凑的——困难样例从线上低分或无答案的 query 里挖,对抗样例改写成同义、缩写、错别字、跨语言,失败样例是已知会把链路带偏的输入,三者都要进回归集。
简历上的数字会被逐个对账,这部分值得单独准备:“日均 2.7 万入团用户”要说清时间窗口、去重方式(同一个人重复入团算几次)和用户口径;“每位新人 0.5 个关注”“75% 回关”要说明是实验观测还是估算,估算就得给区间和算法。被问「如果还没测过,你会先建哪些基线」时,答出可执行的顺序(先定义指标与口径 → 再做离线评测 → 最后线上小流量),比临时补一个数字更得分。二面反复追问「HNSW 是 Agent 写的还是开源库」「AI 生成上千行代码除了单测怎么确认正确」,本质是在查同一件事:你对交付物有没有真实的掌控力——AI 可以写,但选型、验收标准和证据必须由你负责。
消息队列项目:压测基线、故障注入与恢复耗时
自研消息队列这类项目,面试官问的都是可测量的量:单机吞吐、P50/P99 延迟、并发连接数、topic/queue 数、消费者数、消息大小,以及 Broker 重启后的恢复耗时。这几项凑不齐,基本会被判定为没真压过——顺带要能说清压测的硬件配置、消息大小梯度和客户端实现。吞吐和延迟必须成对报,只给吞吐不给延迟等于没给结论。
恢复耗时取决于持久化设计:顺序追加的 commitlog 重放、索引重建、刷盘策略(同步刷还是页缓存异步刷)各占多少时间,并且要和数据量对应起来报,比如 10 GB 数据恢复到可服务是几十秒还是几分钟。故障注入验证重复与丢失的标准做法,是注入 broker 被 kill、网络分区、磁盘写满、消费者长时间不 ack 触发 rebalance,再在生产端和消费端按消息序号对账,统计重复率与丢失率。多数 MQ 的语义是 at-least-once,所以重复只能靠消费端幂等(唯一消息 id 加去重表)压到业务无感,而丢失必须为零——这两句答出来,比报一个漂亮的吞吐数字更能说明你懂自己的系统。
手撕与开放题:中序遍历、nth_element、通配符匹配
不用递归的中序遍历就是显式栈模板:从根一路向左压栈,弹出即访问,然后转向右子树;时间 O(n)、空间 O(h)。可以顺口补一句 Morris 遍历能用 O(1) 空间做到,但过程中会临时改写树结构,不适合并发读的场景。
std::nth_element 的要点是它只保证第 N 位就位、两侧不保证有序,平均 O(n)(libstdc++ 是 introselect,递归深度超限回退堆选择,标准不保证最坏复杂度),所以它适合找第 k 大或 top-k,而不是完整排序;参数是 (first, nth, last),返回后 [first, nth) 的元素都不大于 *nth,求第 N 大要么传 greater 比较,要么对下标做对称换算。复杂度追问直接答平均 O(n)、额外空间 O(log n)(递归栈)即可。
通配符分段匹配(. 分段、* 匹配一段、# 匹配零段或多段)是 glob 类问题,朴素回溯在多个 # 的情况下会指数爆炸,稳妥写法是 DP:dp[i][j] 表示前 i 个模式段能否匹配前 j 个字符串段,* 只允许从 dp[i-1][j-1] 转移,# 则可以从 dp[i-1][j-1]、dp[i][j-1](吞一段)和 dp[i-1][j](吞零段)转移;边界要专门处理空串、连续 #、模式以 # 结尾。面试官要求先说算法、复杂度和边界用例再动手,是在看工程习惯,别跳过。
开放题按真实理解回答即可:AI 提升广告研发效率不止写代码,还包括日志与指标排查、实验配置生成、取数与日报、代码 review 辅助和 case 归因,工程师要守住的是问题定义、判断与验收能力。被问到「搜广推当前最大的技术挑战」时,如果不在这个方向,坦白说明并给出自己的理解(冷启动与样本稀疏、多目标与长期价值、实时性与特征一致性、算力与延迟预算等)比硬编一个答案安全。原帖写到 10.9 HR 面结束、面试官说十月底发带薪意向,没有交代最终结果。