美团 Agent 开发一面:RAG 被追到第七层,卡在没有标题的 Markdown 上
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 为什么想做这个智能运维 Agent?
- 描述一下整个系统怎么运转的,中间件选型的依据是什么。
- 为什么用 RAG?现在上下文窗口这么大了,为什么不直接把原始文档全丢给 Agent 让它自己查?
- 十篇以上文档,或者让它读一个很大的代码仓库,用 RAG 和让它自己 grep,不考虑 token 消耗的情况下,各有什么优劣?
- 从原始文档到最终答案,切块、索引、存储、检索、排序,哪几步对效果影响最大?
- 如果你的 Markdown 没有标题,怎么切?
- 混合检索里,关键词检索有什么优缺点?向量检索有什么缺点?
- chunk 一两百字,query 就三五个字,怎么把短 query 和长 chunk 放到同一个空间比?你对比过长 query 和三字 query 的向量召回差异吗?
- 除了压缩,长 query 还怎么处理?
- 父子索引解决什么问题?父块多大、子块多大,为什么这么定?
- 知识库持续更新,旧的向量怎么失效?
- 召回结果互相矛盾的时候,谁说了算?
- 工具调用返回结构怎么设计?
- 上下文超窗口,滑动窗口和动态摘要各自的坑。
- 你这个系统上线了吗,日均多少请求?
《参考解析》
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 便于线上排障。上下文超窗时,滑动窗口的坑是丢掉早期约束(用户最初说的条件),动态摘要的坑是摘要本身有损且会累积漂移;更稳的是「结构化状态 + 摘要」:把任务目标、已确认事实、待办单独存成状态,窗口只带最近几轮原文和状态快照。