DCD(Domain-Collection-Document):面向异构语料的层级化 RAG 控制架构

  • 关联论文:2604.07590
  • 作者:flyP
  • 更新:2026-07-20

一句话结论

DCD 提出"领域—集合—文档"三层级知识组织 + 多阶段路由:在不修改底层 LLM 的前提下,把异构语料切成可路由的层级结构,先路由到 Domain、再路由到 Collection、最后才落到 Document,让 RAG 的检索和生成范围在每一步都被显式约束,同时配合 smart chunking、hybrid retrieval 和生成 guardrail,在多步骤查询与异构企业语料上明显提升事实性、答案相关性与鲁棒性。

解决什么真问题

RAG 在企业落地时经常出现的两类失败,都源于"知识表示太扁平":

  1. 异构语料 + 多步查询下 Naive RAG 退化。当你的语料是"HR 制度 + 产品手册 + 工单记录 + 财务报表 + 监管文档"这种多领域混合时,用户的查询又往往是"先定位到 HR 的某条政策,再交叉引用工单里的案例"——朴素 RAG 会一次性把全部 chunk 拉进来算相似度,召回里既包含无关的财务噪声,也漏掉了真正相关的工单上下文。
  2. 缺乏显式 workflow。Naive RAG 的 query → retrieve → generate 是一条直线,没有"先选领域 → 再选子库 → 再选文档"的显式路径。一旦 query 是多跳、多意图的,直线 pipeline 就会在第一个错误环节传播。

DCD 直击这两点:用层级化结构组织知识 + 用多阶段路由显式控制每一步范围,让 RAG 的搜索空间按"业务语义"逐级收窄,而不是一上来就让 embedding 在全集上做近似最近邻。

论文还有一层隐性意图:不动 LLM。所有改动都发生在检索/路由侧,意味着这套设计可以挂在任何商用 LLM(OpenAI / Claude / 本地 vLLM)之上,零模型微调。

核心方法

1. 三层级知识组织:Domain → Collection → Document

Corpus
 └─ Domain (领域)
     ├─ Collection (集合)
     │   ├─ Document
     │   ├─ Document
     │   └─ ...
     ├─ Collection
     └─ ...
  • Domain:最高层,对应一个业务域,如 "HR"、"Finance"、"Product Support"、"Legal"。
  • Collection:中间层,对应同一 Domain 内的子主题,如 HR Domain 下的 "Leave Policy"、"Onboarding"、"Compensation"。
  • Document:叶子层,即单个 PDF / 单个 Markdown / 单个工单 / 单个章节。

层级化的好处不是"分类学的整齐",而是让检索的搜索空间按业务语义逐级收窄:从全集 → Domain → Collection → Document,每一跳的候选集都比上一跳小一个量级,embedding 的负担、LLM 的负担、guardrail 的负担都随之下降。

2. 多阶段路由(Multi-stage routing)

每一级路由都不靠硬规则,而是让 LLM 用结构化输出(structured output / function calling)从上一级的结果里挑下一级:

# 阶段 1: 选 Domain
selected_domain = route(q, domain_list, llm_structured)

# 阶段 2: 选 Collection
selected_collection = route(q, selected_domain.collections, llm_structured)

# 阶段 3: hybrid retrieval 选 Document
hits = hybrid_retrieve(q, selected_collection.documents, k=K)

# 阶段 4: smart chunking + rerank
chunks = smart_chunk(hits) → rerank → top-N

关键设计是 LLM 结构化输出做路由:LLM 不自由回答,而是按 schema 输出 JSON { "domain": "...", "collection": "..." }。这样:

  • 路由结果是可审计的:每一步都可以打印出来 review;
  • 路由不会跑偏:LLM 不会在阶段 1 给出"我要在 Finance 里找 HR 政策"这种自相矛盾的输出;
  • 路由可以插拔:换 LLM / 换 schema 不影响整套架构。

这一步把"Naive RAG 的直线 pipeline"升级成了"显式 workflow",恰好回应了上面提到的真问题。

3. Smart chunking(智能切块)

DCD 在切块环节不是简单按 token 切,而是结合 Document 类型做"结构感知"切块:

  • 合同 / 表格类:按条款(clause)切,避免把"第 N 条"和它的解释分到不同 chunk。
  • 长文档 / 手册类:按章节 + 段落 + token 上限做多级切分。
  • 工单 / FAQ 类:把"问题 + 答案 + 元数据(时间/客服/产品线)"作为一个 chunk 单元。

Smart chunking 的核心收益是 chunk 内部的语义自洽性——这直接抬高了 retrieval 的 recall 和后续 generation 的 faithfulness,因为 LLM 看到的"证据"是完整的,而不是被截断的半句话。

4. Hybrid retrieval(混合检索)

DCD 不只用向量检索,而是 BM25 / 关键词检索 + 向量检索 的 hybrid:

  • 向量检索:处理语义相近但用词不同的 query;
  • BM25 / 关键词检索:处理精确名词、型号、编号、日期这类"必须命中字面"的 query;
  • 融合:用 reciprocal rank fusion (RRF) 或线性加权把两路结果合并。

为什么必须 hybrid?因为企业 query 里大量是"2024 年 Q3 的报销政策里关于差旅住宿上限那条"——"2024 年 Q3"是字面,"差旅住宿上限"是语义;只靠任何一路都会漏。

5. Guardrails(验证与生成护栏)

DCD 在生成前后各加一道护栏:

  • Pre-generation guardrail:检查 retrieved chunks 是否真的覆盖 query 的关键实体/数字,避免 LLM 在"无证据可引"的情况下强行编。
  • Post-generation guardrail:检查 answer 里出现的关键事实能否在 retrieved chunks 里找到对应来源;找不到的要么拒答、要么标注"无法验证"。

这两道护栏把 RAG 最常见的"幻觉"显式拦下来,对企业级应用(特别是合规、客服、监管场景)几乎是必需。

6. 整体 workflow 伪代码

def dcd_answer(query: str, llm, corpus: DCDCorpus) -> Answer:
    # Step 1: 路由到 Domain
    domain = llm.route(
        query,
        options=[d.name for d in corpus.domains],
        schema={"domain": str}
    )
    domain_obj = corpus.get_domain(domain)

    # Step 2: 路由到 Collection
    collection = llm.route(
        query,
        options=[c.name for c in domain_obj.collections],
        schema={"collection": str}
    )
    collection_obj = domain_obj.get_collection(collection)

    # Step 3: Hybrid retrieval 在 Collection 内
    bm25_hits = bm25_retrieve(query, collection_obj.docs, k=50)
    vec_hits = vector_retrieve(query, collection_obj.docs, k=50)
    fused = rrf(bm25_hits, vec_hits, top_k=20)

    # Step 4: Smart chunk + rerank
    chunks = smart_chunk(fused)
    chunks = rerank(query, chunks, top_n=8)

    # Step 5: Pre-generation guardrail
    if not guardrail_pre(query, chunks):
        return Answer(status="insufficient_evidence")

    # Step 6: Generation
    answer = llm.generate(query=query, context=chunks)

    # Step 7: Post-generation guardrail
    if not guardrail_post(answer, chunks):
        return Answer(status="unverified", body=answer, chunks=chunks)

    return Answer(status="ok", body=answer, chunks=chunks)

关键实验与数据

  • 评估集:论文使用合成评估数据集(synthetic evaluation dataset),并把数据集公开在 HuggingFace(redmadrobot-rnd/dcd)。
  • 评估维度:robustness(鲁棒性)、factual accuracy(事实准确性)、answer relevance(答案相关性)。
  • 结论方向:在多步查询与异构语料上相比 Naive RAG,robustness / factual accuracy / answer relevance 三项都显著提升(具体百分比论文未在 abstract 公开,需读正文表格)。
  • 论文长度:14 页 + 4 图 + 2 链接(HF 数据集 + GitHub 代码)。
  • 作者 / 机构:Valerii Kovalskiy(来自 Redmadrobot R&D 等团队;提交历史显示 2026-04-08 v1,2026-06-11 v2)。

注意:abstract 未公开完整 benchmark 数字,本节按论文 abstract 表述转述方向,不编造具体百分比。

亮点与局限

亮点

  • 真问题 + 真实落地:异构企业语料上的 RAG 退化是 2026 年最普遍的工程痛点,DCD 给出了对症方案。
  • 不动 LLM:所有改动在检索/路由侧,兼容任何 LLM,企业接入成本低。
  • 可审计的 workflow:每一步路由都是结构化输出,可打印、可回放、合规友好。
  • 三件套齐全:smart chunking + hybrid retrieval + 双侧 guardrail,组合拳覆盖了 RAG 几乎所有已知失败模式。
  • 公开数据集 + 代码:HF 数据集 + GitHub 仓库都公开,便于复现与扩展。
  • 业务语义驱动:Domain/Collection 的划分直接对应业务部门/产品线,落地到企业知识管理时心智负担小。

局限

  • 依赖 LLM 做路由:路由阶段要调用 LLM,带来了额外延迟和成本;对延迟敏感场景(< 200ms)需要权衡。
  • 路由错误会传播:如果阶段 1 选错 Domain,下游全错;论文未给出"路由失败回退到 Naive RAG"的兜底方案(原文未明确)。
  • Domain/Collection 划分需要人工设计:不像 embedding 可以自动聚类,Domain / Collection 的边界由业务决定,企业首次部署要做一次"知识建模"工作。
  • 合成数据集评估:合成数据集便于对比但可能与真实生产 query 分布有差距,真实效果需要客户侧 PoC 验证。
  • 未深度评测多语言:语料以英文为主,多语种/小语种表现未在 abstract 涉及。
  • guardrail 是规则型还是模型型未明确:abstract 没具体展开 guardrail 的实现形式(关键词 overlap?LLM 自评?),需要读正文。

对工程落地的启发

  1. 企业 RAG 不要直接上 Naive:异构语料场景下,先按业务做 Domain/Collection 切分,再上检索——这是收益最大、成本最低的工程动作。
  2. 路由要"显式 + 结构化":让 LLM 用 JSON schema 路由,不要让它自由回答选哪个 Domain。可审计、可重放、合规友好。
  3. Hybrid retrieval 是默认:BM25 + 向量 + RRF 是 2026 年企业 RAG 的事实标准,纯向量检索在精确名词场景下不可靠。
  4. Chunking 要结构感知:合同按条款、手册按章节、工单按"问题+答案+元数据"——不要无脑按 token 切。
  5. Guardrail 不可省:pre-gen 检查证据是否齐全、post-gen 检查事实能否溯源,是把"幻觉率"压下来的关键。
  6. 数据集公开值得抄:把合成数据集开源到 HF,是让社区复现 + 反哺的最快路径,比单纯 release 代码更友好。
  7. 不动 LLM 是企业友好设计:如果你在做 RAG 产品,DCD 这种"无需微调"的方案对接速度通常远快于"必须 fine-tune"的方案。

与同方向工作的关系

  • vs Self-RAG / CRAG / FLARE(自适应 RAG):这些是"模型内决策要不要 retrieve"的工作,DCD 是"决策去哪个层级 retrieve"——两者正交,可以叠加(Self-RAG 决定"要不要查",DCD 决定"查哪一层哪一块")。
  • vs GraphRAG / LightRAG(知识图谱增强 RAG):GraphRAG 用图结构做全局推理,DCD 用层级结构做范围收窄;二者思路不同,但都能缓解 Naive RAG 在异构语料上的退化。可以视 DCD 为"轻量版 GraphRAG"——无需建图、无需实体抽取,开销低一个量级。
  • vs Multi-agent RAG(AgentWorkflow / AutoGen 类):多 agent 流派强调"agent 之间协作",DCD 强调"层级路由 + 显式 workflow",二者关注粒度不同。DCD 的"structured routing"其实可以视作一个轻量 agent 模式。
  • vs RAG 路由专题(RouteRAG、RouterDC 等):路由 RAG 流派,DCD 的差异在于把路由做成"业务层级"而不是"语义聚类",可解释性更强。
  • vs Naive RAG / Advanced RAG / Modular RAG 框架:DCD 落在 Modular RAG 阵营,贡献是把"Modular"具体化成"Domain-Collection-Document"的可复制模式。

适合谁读

  • 企业知识库 / 内部问答系统架构师:在做多业务部门、异构文档、跨系统的统一 RAG。
  • RAG 工程师:想从 Naive RAG 升级到 Advanced/Modular RAG,又不想一上来就上知识图谱。
  • 合规 / 审计团队:需要 RAG 的每一步都可追溯、可审计——DCD 的结构化路由天然友好。
  • 客服 / 工单系统团队:异构语料 + 多步 query 是客服场景的标准形态,DCD 的设计直接对症。
  • RAG 研究者:关心"结构化检索 vs 自由检索"的设计取舍,DCD 是"结构化派"的代表。
  • 不适合:单一领域 / 单一文档库的简单场景——DCD 的层级开销在这种场景下不划算,朴素 RAG 足够。

延伸阅读

  • Self-RAG / CRAG / FLARE:自适应 RAG——决定"要不要 retrieve";与 DCD 的"决定 retrieve 哪里"正交。
  • GraphRAG / LightRAG:图结构增强 RAG——DCD 是"轻量替代",无需建图、无需实体抽取。
  • RouteRAG / RouterDC:路由类 RAG——DCD 把"路由"具体化成业务层级,可解释性更强。
  • Modular RAG Survey:将 DCD 放在 Modular RAG 框架下看,是"层级化 Module"的代表。
  • 研究主题卡:与 ragengineeringagent 主题联动,建议同步收录到这三个主题页。

复现清单(供其他研究者)

  1. 数据:从 HF redmadrobot-rnd/dcd 拉取论文公开的合成评估集,或按同样 schema 自建企业版。
  2. 代码:从 GitHub redmadrobot-rnd/dcd 拉取参考实现,先跑通 Naive RAG baseline。
  3. 层级建模:按业务划分 Domain / Collection(如 HR Domain / Leave Policy Collection),首次部署至少花 1 周做"知识建模"。
  4. 路由 LLM:使用支持 structured output / function calling 的 LLM(GPT-4o、Claude 3.5、本地 vLLM + guided JSON 均可)。
  5. Hybrid retrieval:BM25(推荐 Elasticsearch / OpenSearch)+ 向量(Qdrant / Milvus)+ RRF 融合。
  6. Guardrail 实现:pre-gen 用"关键实体覆盖率"规则;post-gen 用"事实-证据 span overlap"规则(实现细节论文未明确,建议自定)。
  7. 对照实验:Naive RAG、Advanced RAG(无层级)、DCD 三组对照,在同一合成评估集上比 robustness / factual accuracy / answer relevance。

工程落地与核查(Jay)

1. 事实核查

核查项 状态 说明
HF 数据集 redmadrobot-rnd/dcd ✅ 摘要有提 需核验当前是否仍可访问(部分 HF 数据集会因作者申请而下线)
GitHub redmadrobot-rnd/dcd ⚠️ 摘要有提 需核验 repo 是否确实存在(非作者自述即公开的 guarantee)
"robustness / factual accuracy / answer relevance 三项都显著提升" ⚠️ 方向性陈述 原文摘要未给具体数字,"显著"是作者定性判断,非统计显著性标注
Guardrail 实现形式 ❌ 原文无 摘要未明确 guardrail 是规则型(关键词 overlap)还是模型型(LLM self-evaluation),解读中未擅自填充 ✓
路由失败兜底方案 ❌ 原文无 摘要未提回退策略,工程落地需自设计
多语言评测覆盖 ❌ 原文无 摘要未涉及,解读中未填充 ✓
Domain/Collection 边界自动化 ❌ 原文无 需人工设计,解读中已标注 ✓

2. 实际系统落地路径

最小可跑 DCD 流水线(生产级伪代码)

from typing import Optional
from dataclasses import dataclass
import json

@dataclass
class Domain:
    name: str
    collections: list["Collection"]

@dataclass
class Collection:
    name: str
    docs: list[str]   # doc IDs

class DCDRAG:
    def __init__(self, llm_router, vector_store, bm25_index,
                 reranker, pre_guardrail_fn, post_guardrail_fn):
        self.llm = llm_router
        self.vector = vector_store
        self.bm25 = bm25_index
        self.reranker = reranker
        self.pre_guard = pre_guardrail_fn
        self.post_guard = post_guardrail_fn
        self.corpus: dict[str, Domain] = {}

    def index(self, corpus: list[dict]):
        """构建 Domain → Collection → Doc 三级索引"""
        for domain_data in corpus:
            domain = Domain(name=domain_data["domain"], collections=[])
            for coll_data in domain_data["collections"]:
                collection = Collection(name=coll_data["name"], docs=coll_data["doc_ids"])
                domain.collections.append(collection)
            self.corpus[domain.name] = domain

    def answer(self, query: str, top_k: int = 8) -> dict:
        # Stage 1: Domain routing(LLM structured output)
        domain_choice = self.llm.structured_output(
            prompt=f"Given query: {query}\nChoose the most relevant domain.",
            options=list(self.corpus.keys()),
            schema={"domain": str}
        )
        domain_obj = self.corpus.get(domain_choice["domain"])
        if not domain_obj:
            return {"status": "domain_not_found", "body": None}

        # Stage 2: Collection routing
        coll_choice = self.llm.structured_output(
            prompt=f"Given query: {query}\nIn domain {domain_obj.name}, choose the best collection.",
            options=[c.name for c in domain_obj.collections],
            schema={"collection": str}
        )
        selected_coll = next((c for c in domain_obj.collections
                              if c.name == coll_choice["collection"]), None)
        if not selected_coll:
            return {"status": "collection_not_found", "body": None}

        # Stage 3: Hybrid retrieval (BM25 + vector + RRF)
        doc_ids = selected_coll.docs
        bm25_hits = self.bm25.search(query, doc_ids, k=50)
        vec_hits = self.vector.search(query, doc_ids, k=50)
        fused = self.rrf_fusion(bm25_hits, vec_hits, top_k=20)

        # Stage 4: Smart chunk + rerank
        chunks = self.smart_chunk(fused)
        reranked = self.reranker.rerank(query, chunks, top_n=top_k)

        # Stage 5: Pre-generation guardrail
        if not self.pre_guard(query, reranked):
            return {"status": "insufficient_evidence", "body": None}

        # Stage 6: Generate
        answer = self.llm.generate(query=query, context=reranked)

        # Stage 7: Post-generation guardrail
        if not self.post_guard(answer, reranked):
            return {"status": "unverified", "body": answer, "chunks": reranked}

        return {"status": "ok", "body": answer, "chunks": reranked}

    def rrf_fusion(self, bm25_hits, vec_hits, k=60, top_k=20):
        """Reciprocal Rank Fusion"""
        scores = {}
        for rank, (doc_id, _) in enumerate(bm25_hits):
            scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
        for rank, (doc_id, _) in enumerate(vec_hits):
            scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
        return sorted(scores.items(), key=lambda x: -x[1])[:top_k]

3. 坑位清单

坑 1:Domain/Collection 划分是人工成本最大的环节 - 这不是纯技术问题,是业务建模问题;一个 HR 场景下"离职手续"是独立 Collection 还是挂在 Leave Policy 下,需要与业务方反复对齐 - 建议:用现有文档元数据(部门标签、目录结构)作为 Domain/Collection 的初始种子,先半自动化聚类,再人工 review,而不是从头设计

坑 2:路由 LLM 调用的延迟与成本 - 每条 query 额外触发 2 次 LLM 调用(Domain + Collection 路由);以 GPT-4o 为例约增加 50-150ms 延迟 + $0.001/query - 对高 QPS 系统(> 1000 QPM),路由层可能成为瓶颈;建议:对高频 query 的 Domain/Collection 先做 embedding-based 粗排,再用 LLM 精排

坑 3:Guardrail 实现是最大的不确定性来源 - 论文未明确 guardrail 是规则型还是模型型;两种实现路径的风险不同: - 规则型(关键词/实体 overlap):低延迟,但漏判率高,对同义表述无效 - 模型型(LLM self-evaluation):高准确,但增加 1 次 LLM 调用,且可能过度拒答 - 建议:生产系统两段都用规则型作为 fast path,模型型作为 confirm path

坑 4:路由错误的全链路传播 - 阶段 1 选错 Domain → 阶段 2 → 检索 → 生成全部跑偏;且这种错误在最终 answer 里不一定显式可见 - 缓解:加一个置信度阈值;低于阈值的路由结果触发 fallback 到全文检索模式

坑 5:合成评估集与生产分布的差距 - 合成数据集的优势是 ground truth 清晰,劣势是 query 分布可能过于理想化 - 建议:在生产环境用真实 query 做一次 audit,统计"路由到错误 Domain/Collection"的比例,据此调整 Domain/Collection 划分

坑 6:多语言文档的处理 - 论文以英文为主;中英混合或纯中文语料下,BM25 + embedding 的 hybrid fusion 效果可能显著下降(中文分词质量影响 BM25,embedding 模型的多语言能力影响向量检索) - 建议:中文场景替换成支持中文的 embedding 模型(如 BGE-large-zh),BM25 替换成 Jieba+BM25

4. 工程核查速查

✅  arXiv: 2604.07590 (v2 2026-06-11)
✅  HF 数据集: redmadrobot-rnd/dcd (摘要提,需核验)
⚠️  GitHub repo: 需核验是否真实存在且有代码(摘要提,非 guarantee)
⚠️  基准数字: 摘要无具体数字,解读未填充 ✓
⚠️  Guardrail 类型: 原文无,解读未填充 ✓
⚠️  路由回退方案: 原文无,需自设计
✅  路由 schema (JSON): 解读中已展示 ✓
✅  Hybrid retrieval RRF 融合: 解读中已覆盖 ✓