面灵AI→

美团 Agent 开发一面:RAG 被追到第七层,卡在没有标题的 Markdown 上

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 自我介绍。
  2. 为什么想做这个智能运维 Agent?
  3. 描述一下整个系统怎么运转的,中间件选型的依据是什么。
  4. 为什么用 RAG?现在上下文窗口这么大了,为什么不直接把原始文档全丢给 Agent 让它自己查?
  5. 十篇以上文档,或者让它读一个很大的代码仓库,用 RAG 和让它自己 grep,不考虑 token 消耗的情况下,各有什么优劣?
  6. 从原始文档到最终答案,切块、索引、存储、检索、排序,哪几步对效果影响最大?
  7. 如果你的 Markdown 没有标题,怎么切?
  8. 混合检索里,关键词检索有什么优缺点?向量检索有什么缺点?
  9. chunk 一两百字,query 就三五个字,怎么把短 query 和长 chunk 放到同一个空间比?你对比过长 query 和三字 query 的向量召回差异吗?
  10. 除了压缩,长 query 还怎么处理?
  11. 父子索引解决什么问题?父块多大、子块多大,为什么这么定?
  12. 知识库持续更新,旧的向量怎么失效?
  13. 召回结果互相矛盾的时候,谁说了算?
  14. 工具调用返回结构怎么设计?
  15. 上下文超窗口,滑动窗口和动态摘要各自的坑。
  16. 你这个系统上线了吗,日均多少请求?

《参考解析》

RAG 与「全丢给模型」不是二选一,真正的分界是「检索成本」和「证据可控性」。 上下文窗口再大也有三个硬约束:一是成本随 token 线性涨,长文档每次请求都重读,长期跑不起;二是有效上下文会衰减,关键信息埋在几万 token 中间时召回率明显下降;三是不可追溯,模型说错时你拿不出「它看了哪一段」。RAG 的价值不只是省 token,而是把「找证据」变成可评测、可更新的独立环节。但 RAG 也不是默认正确:文档总量小、更新慢、问题需要跨全文推理时,直接把全文放进上下文或让 Agent 用 grep/ripgrep 主动查,反而更准。合理的架构是分层——小库直读、大库先检索、需要精确定位时给 Agent 文件搜索工具,而不是把所有东西都塞进向量库。

RAG 和 grep 的优劣,取决于「问题是否知道该找什么」。 grep 适合精确符号:函数名、报错码、配置项、日志片段,结果确定、可解释、不会漏掉字面匹配,缺点是要求 query 里有关键词,且大仓库全量扫会慢、需要好的索引和忽略规则。RAG 适合语义问题:「这个服务为什么在高峰期超时」「这类故障通常怎么处理」,能把不同文档里的相关段落聚到一起,缺点是切块会截断上下文、向量召回对数字/专有名词不敏感、召回结果难以证明完整。工程上常见组合是「先用符号/元数据把范围缩小,再在候选集里做语义检索」,以及把 grep 结果作为高置信证据参与重排。

没有标题的 Markdown,不能只按固定长度硬切。 可用的信号有:空行形成的自然段、列表层级、代码块围栏、表格边界、加粗小标题、数字编号、重复的模板句式。一个可落地的顺序是:先保护代码块和表格不被切断 → 按空行/段落聚合成语义段 → 段落过长再按句子切,并保留少量重叠 → 每块补上「来源文件 + 章节路径 + 前后块摘要」作为上下文头。没有标题时,可以让模型对每段生成一句主题标签,或按文档的目录页、文件名、URL 层级反推结构。关键不是切得多均匀,而是每一块单独拿出来仍然能回答一个问题。

短 query 对长 chunk,本质是「信息密度不对等」。 三五个字的 query 往往缺少限定条件,向量容易落到泛化语义上;一两百字的 chunk 则混着主题、细节和废话,池化后的向量被稀释。解决办法有几种:给 chunk 生成「假设性问题」或摘要再嵌入(HyDE 的反向用法),让索引侧更接近 query 的形态;query 侧做扩写,把简称补全、把口语改成领域词;用多向量/ColBERT 这类细粒度匹配,而不是单一池化向量;或者先 BM25 把候选缩到几十条,再用 cross-encoder 精排。父子索引也是同一思路:用小块做召回保证命中,用父块做生成保证上下文完整。父块大小取决于文档结构和模型窗口,常见父块 10002000 字、子块 200400 字,比例不是固定的四比一,而是按「子块能否独立命中、父块能否覆盖答案」调出来的。

知识库更新和矛盾召回,考的是「证据的版本与优先级」。 旧向量失效不能只靠定期全量重建,工程上给每个 chunk 带 doc_id + version + updated_at + source,更新时按 doc_id 删除旧版本再写入,检索时过滤掉过期版本;增量索引要有墓碑标记和后台 compaction,避免删除后又被旧索引召回。召回结果冲突时,不能交给模型「自己判断」:先按来源权威性(官方文档 > 内部 wiki > 个人笔记)、时间新旧、版本号排序,把冲突显式呈现给模型并要求它引用依据;如果业务上必须有唯一答案,就在检索层做去重与裁决,把「哪个是最新」变成确定性规则,而不是生成时的自由发挥。

工具返回结构要「短、稳、可引用」。 建议统一成 {ok, data, error_code, error_message, trace_id, truncated}:成功时 data 只放模型决策需要的字段,长列表分页并标注总数;失败时给机器可读的错误码和一句话原因,不要把整段堆栈塞进上下文;所有结果带来源标识,方便最终答案引用。工具描述里写清参数含义、边界和幂等性,返回里保留 trace_id 便于线上排障。上下文超窗时,滑动窗口的坑是丢掉早期约束(用户最初说的条件),动态摘要的坑是摘要本身有损且会累积漂移;更稳的是「结构化状态 + 摘要」:把任务目标、已确认事实、待办单独存成状态,窗口只带最近几轮原文和状态快照。