真实面经题目 · 原创解析

RAG 为什么要引入父子索引,如何兼顾小粒度召回和大粒度上下文回填?

这题考的是 RAG 检索粒度设计:小 chunk 更容易被向量或关键词召回命中,但单独放进上下文时可能缺少标题、章节、定义、前提和表格上下文;父子索引用子块做高精度召回,用父文档或父章节做证据回填,从而兼顾召回命中率、答案可读性和上下文预算。

出现于:快手 · 后端开发

60 秒回答模板

我会把父子索引理解成解决检索粒度和生成粒度不一致的问题。离线建库时,把原始文档按层级组织成父节点和子节点:父节点可以是整篇文档、章节、FAQ 条目或一段完整业务说明,保存标题、来源、权限、版本、更新时间和摘要;子节点则是更小的段落、列表项、表格行组或代码片段,用来生成 embedding 和 BM25 字段索引。在线查询时,先用子节点做召回,因为子节点更短、更聚焦,语义向量不容易被无关内容稀释;命中子节点后,再根据 parentId 回填父章节、相邻子块、父摘要或结构化上下文,让模型拿到足够完整的证据。之后不能简单把所有父节点都塞进 prompt,而要重新做去重、重排和 token 预算控制:同一父节点下多个子命中可以合并,分数要综合子命中强度、父节点质量、章节完整度和 query 覆盖;过长父节点可以只取命中段落附近窗口加父标题和父摘要。父子索引还要处理权限继承、版本一致性、删除更新、引用追踪和评估指标。核心不是多建一张表,而是让检索阶段用小粒度提升命中,让生成阶段用大粒度保证证据完整。

考点 核心动机
难度 真实面经题
回答目标 让候选人能把父子索引讲成一套可落地的 RAG 检索粒度方案:子块负责命中,父块负责完整证据,回填后再做去重、重排、预算控制、权限版本一致性和指标评估。

深入解析

01

先识别粒度矛盾

RAG 里常见矛盾是:为了召回准确,chunk 希望短一点;为了生成可靠,证据又希望完整一点。长 chunk 会让 embedding 被多个主题混在一起,短 chunk 又可能只有一句结论,没有标题、前提、定义、步骤和例外条件。父子索引的价值就在于把召回粒度和上下文粒度解耦。

02

父节点承载结构和证据边界

父节点通常不是随便按固定长度切出来的一段,而是具有语义完整性的单元,例如一篇文档、一个章节、一个 FAQ、一个接口说明、一个故障处理步骤或一张表格说明。父节点保存标题层级、来源、更新时间、权限、版本、业务标签、摘要和原文范围,用来保证后续回填时知道证据来自哪里、是否可见、是否过期。

03

子节点负责高精度召回

子节点粒度更小,可以按段落、列表项、表格行组、代码块或语义句群切分。它适合做 embedding、BM25、实体字段和标题字段索引,因为短文本主题集中,更容易命中用户问题里的实体、术语、错误码和语义表达。每个子节点都要保存 parentId、在父节点中的位置、所属标题、权限继承和内容 hash。

04

在线链路是子召回到父回填

在线查询通常先召回子节点 topN,再把命中的 child 映射回 parent。回填方式可以有多种:只取命中子节点附近窗口、取同一小节的相邻子节点、取父节点摘要加命中原文、取完整父章节,或者在多子命中时合并成一个父级证据包。这样既不牺牲召回准确性,也避免模型看到孤立句子后误解。

05

回填后必须再去重和重排

父子索引不能把每个命中子块的父节点都无脑放入上下文。同一父节点下多个子节点命中时应合并,重复文档、镜像内容和相邻窗口要去重。重排时要同时看子节点相关性、父节点完整度、是否覆盖问题要点、是否包含可引用事实、来源质量和时效,必要时用 cross-encoder 或 LLM reranker 判断证据包是否真正能回答问题。

06

上下文预算决定回填策略

如果父节点很短,可以直接回填完整父节点;如果父节点很长,就应该采用父标题、父摘要、命中段落、前后窗口和必要表格片段的组合。预算不足时优先保留能直接回答问题的子块、父级标题路径、关键定义和限制条件;预算充足时再补背景说明和相邻段落。好的实现通常按 token 预算动态选择 K,而不是固定返回几个父节点。

07

权限和版本要从父到子一致

父节点和子节点必须共享一致的权限、租户、语言、版本和删除状态。不能出现子节点可召回但父节点无权回填,或者父节点更新后子节点还是旧 embedding 的情况。实践中要用文档版本、chunk hash、索引批次和软删除标记保证召回、回填、引用展示都指向同一份内容快照。

08

引用追踪要落到子证据

给用户展示时可以引用父文档或章节,但系统内部要记录具体命中的 childId、parentId、命中位置、父节点版本和原文片段。这样才能排查模型是否真的使用了证据,也能在 badcase 中判断问题出在子召回、父回填、重排还是生成。

09

评估要拆成两层指标

父子索引的评估不能只看最终答案。子层要看 Recall@K、MRR、专名命中率和误召回;父层要看回填后的证据完整率、上下文冗余率、引用准确率、答案忠实度、token 成本和延迟。若子节点命中但父回填过长挤掉其他证据,最终效果仍然可能下降。

易错点

  • 只说父子索引能提升召回,但没有解释小粒度召回和大粒度上下文之间的矛盾。
  • 把父节点和子节点都按固定字符数切分,父节点没有语义完整性,回填后仍然不可读。
  • 命中子节点后直接回填整篇长文档,导致 token 预算被一个来源占满。
  • 同一父节点下多个子块命中时不合并,prompt 里出现大量重复段落。
  • 只建向量索引,不保存 parentId、标题路径、版本、权限和原文位置,无法做可靠回填。
  • 父节点更新后不重建或失效子节点 embedding,召回和引用指向不同版本。
  • 权限只挂在父节点或只挂在子节点,导致召回可见性和回填可见性不一致。
  • 评估只看最终回答主观质量,不拆分子召回、父回填、重排和生成各环节。

面试官追问

父节点应该是整篇文档还是章节?

取决于文档结构和上下文预算。短 FAQ 或规范条目可以把整条作为父节点;长文档更适合用章节或小节作为父节点。原则是父节点放进上下文后仍然能保持语义完整,同时不会过长到挤掉其他证据。

命中多个子节点属于同一个父节点时怎么打分?

可以把最高子节点分、命中子节点数量、命中覆盖的章节位置、关键词覆盖和父节点质量综合起来。多个子命中通常说明父节点更可能相关,但也要防止同一父节点重复占满上下文,所以要合并成一个证据包再参与重排。

父子索引和相邻 chunk 扩展有什么区别?

相邻扩展通常是在命中 chunk 前后取窗口,主要解决局部上下文缺失;父子索引有明确的层级结构和父级元数据,可以回填章节摘要、标题路径、完整父节点和权限版本信息,适合结构化文档和长文档。

父节点太长怎么办?

不要直接整段回填。可以使用父标题加父摘要、命中子节点附近窗口、关键表格片段、相邻步骤和必要限制条件;如果仍然超预算,再做证据压缩或让 reranker 选覆盖问题最多的片段。

父子索引会不会降低召回速度?

子节点数量增加会带来索引和召回成本,但在线主召回仍然发生在子索引上,父节点通常通过 parentId 批量读取。真正的成本在于回填后重排和上下文拼装,因此需要缓存父节点元数据、控制 topN 和做好去重。

如何判断父子索引真的有效?

可以对比普通 chunk 检索和父子检索的子召回率、证据完整率、引用准确率、答案忠实度、平均 token 消耗和延迟。特别要看那些单个短 chunk 命中但回答缺少前提的 badcase 是否减少。