RAG 的知识库摄取与 Chunking 怎么做:从原始文档到可检索证据单元

RAG 的知识库摄取与 Chunking 怎么做:从原始文档到可检索证据单元

在这里插入图片描述

系列:生产级 LLM 应用方法论 06
日期:2026-06-20
适合读者:正在建设企业知识库、文档问答、客服助手、合规问答、研究助理或内部 Agent 的工程师、产品负责人和技术负责人。

摘要

第五篇讲了 RAG 的引用与归因:答案里的关键结论必须能追溯到证据。

这一篇回到更底层的问题:这些证据单元从哪里来?

很多 RAG 项目失败,不是因为模型不够强,也不是因为向量数据库不好,而是知识库摄取阶段已经把材料切坏了。PDF 页眉页脚混进正文,表格行列关系丢失,旧版本文档没有废止,权限信息没有进入 metadata,chunk 太长导致召回不准,chunk 太短导致上下文断裂。后面再怎么调 prompt、reranker 和引用格式,都只能在坏证据上修补。

所以知识库摄取不是“把文件上传到向量库”。它是一条生产流水线:盘点来源、解析文档、规范化文本、保留结构、切分证据、补元数据、去重和版本管理、写入索引、跑检索评测、持续回流失败样本

本文给出一套可落地的摄取与 chunking 方法,包含证据单元 JSONL 契约、chunk 策略选择、参数建议、表格和代码处理、权限和版本字段、最小结构化 chunker 示例,以及上线前自检清单。

目录

1. 为什么摄取质量决定 RAG 上限

RAG 的后半段看起来更“智能”:检索、重排、生成、引用校验。但真正决定系统上限的,经常是最前面的摄取。

如果摄取阶段把文档处理成这样:

退款政策
页脚:内部资料,仅供参考
表 2
人工审核 跨境支付 大额退款
1-5 个工作日
页眉:退款政策

模型后面很难稳定回答:

跨境订单是否需要人工审核?退款多久到账?

因为原始关系已经丢了。表格的行列、标题层级、版本、页码、权限、更新时间都没有进入证据单元。

RAG 摄取质量差,会带来六类问题。

问题表现根因
召回不到用户问对了,但正确内容不在 top-k文档没入库、切分断裂、标题丢失
召回太泛top-k 都相关但不精确chunk 太长、噪声多、metadata 缺失
答案断章取义只引用一句话,漏了例外条件chunk 太短、上下文断裂
旧版本覆盖新版本回答用过期政策版本字段和废止状态缺失
权限泄漏召回用户无权访问内容access scope 没进检索过滤
引用无法核查用户点开来源找不到原文locator、page、span、source hash 缺失

所以摄取流水线的目标不是“生成 embedding”,而是生成可检索、可引用、可过滤、可审计的证据单元。

2. 摄取流水线的 9 个阶段

一条可靠的知识库摄取流水线可以拆成 9 个阶段。

在这里插入图片描述

阶段目标产物
1. Source Inventory盘点知识来源source registry
2. Permission Mapping建立权限和租户边界access policy
3. Parsing从 PDF/HTML/Docx/表格中抽取内容parsed blocks
4. Normalization清洗页眉页脚、乱码、重复空白normalized text
5. Structure Recovery保留标题、页码、表格、列表、代码块structured document
6. Segmentation切成可检索证据单元evidence chunks
7. Metadata Enrichment添加版本、权限、来源、hash、locatorchunk metadata
8. Indexingembedding、稀疏索引、向量库写入vector/search index
9. Evaluation Loop用 gold set 检查召回和引用质量eval report

很多团队跳过前 5 步,直接把文档丢进默认 chunker。这在 demo 里可以工作,但在企业知识库里会很快遇到边界:

  • PDF 的两栏排版被读错;
  • 表格被拉平成无意义文本;
  • 页眉页脚重复污染 embedding;
  • 同一政策多个版本同时命中;
  • 文档标题和段落内容分离;
  • 权限只能在回答后提醒模型,而不能在检索前过滤。

摄取阶段越认真,后面的检索和归因越简单。

3. 证据单元应该长什么样

上一篇讲引用与归因时提到 Evidence Span。这一篇把它展开成一个可写入索引的 evidence_chunk

推荐每个 chunk 至少包含这些字段:

{
  "chunk_id": "refund_policy_2026_03#sec_4#chunk_02",
  "source_id": "refund_policy_2026_03",
  "source_title": "退款政策 2026-03 版",
  "source_type": "policy_doc",
  "version": "2026-03",
  "status": "active",
  "updated_at": "2026-03-18",
  "owner": "finance_ops",
  "tenant_id": "global",
  "access_scope": ["customer_service", "finance"],
  "language": "zh-CN",
  "hierarchy_path": ["退款政策", "4. 跨境退款", "4.2 人工审核"],
  "locator": {
    "page": 4,
    "section": "4.2",
    "start_char": 1832,
    "end_char": 2150
  },
  "text": "跨境支付订单需进入人工审核。审核完成后,退款通常在 1-5 个工作日内退回原支付渠道。",
  "context_prefix": "退款政策 > 跨境退款 > 人工审核",
  "content_hash": "sha256:...",
  "source_hash": "sha256:...",
  "ingested_at": "2026-06-20T22:18:24+08:00"
}

这里有几个关键点。

3.1 text 不是全部

向量索引可能主要使用 text,但系统不能只保存 text。没有 source_idversionaccess_scopelocator,后续就做不了权限过滤、版本控制和引用高亮。

3.2 context_prefix 很重要

很多 chunk 本身只有一句话:

需进入人工审核。

如果不带标题路径,它的语义很弱。可以在索引文本里加入轻量上下文:

退款政策 > 跨境退款 > 人工审核
需进入人工审核。

这不是给用户看的原文,而是帮助检索理解上下文。展示时仍然应该回到原文 span。

3.3 content_hashsource_hash 用来做增量更新

知识库会变。没有 hash,你很难判断:

  • 文档是否真的变了;
  • 哪些 chunk 需要重算 embedding;
  • 旧 chunk 是否应废止;
  • 线上事故发生时用的是哪个版本。

4. Chunking 不是固定长度切文本

最常见的错误是把 chunking 理解成:

每 800 tokens 切一段,重叠 200 tokens。

固定长度切分有价值,尤其适合纯文本和快速基线。但它不是生产 RAG 的全部。

好的 chunk 应该满足五个条件:

条件说明
语义完整一个 chunk 能表达一个相对完整的事实或步骤
可召回用户问题里的词或语义能命中它
可引用用户点开后能理解它支持什么
可过滤带权限、版本、来源和时间 metadata
可组合多个 chunk 能组成完整答案,不互相冲突

如果 chunk 太大,检索会泛:

整章退款政策都被召回,但答案只需要跨境退款一条规则。

如果 chunk 太小,证据会断:

chunk A: 跨境支付订单需进入人工审核。
chunk B: 审核完成后按原支付渠道退款。
chunk C: 退款通常在 1-5 个工作日内完成。

模型可能只看到 A,漏掉 B 和 C。

所以 chunking 的本质是证据建模,而不是字符串切片。

5. 常见 chunk 策略怎么选

5.1 固定 token chunk

按 token 数切分,并设置 overlap。

适合:

  • 快速基线;
  • 结构简单的纯文本;
  • 大量 FAQ;
  • 对引用粒度要求不高的内部搜索。

优点是简单稳定。缺点是容易切断标题、表格、步骤和例外条件。

5.2 结构化 chunk

按标题、段落、列表、表格、代码块切分。

适合:

  • 政策文档;
  • 产品手册;
  • 合同;
  • API 文档;
  • 研究报告;
  • 标准操作流程。

这是企业 RAG 最常用的生产策略。它会保留文档结构:

H1: 退款政策
H2: 跨境退款
H3: 人工审核
Paragraph: 跨境支付订单需进入人工审核...

5.3 语义 chunk

按语义边界切分,例如话题变化、段落相似度、句群聚类。

适合:

  • 长报告;
  • 访谈记录;
  • 会议纪要;
  • 没有清晰标题的文本。

风险是实现更复杂,而且边界不一定稳定。生产系统里最好保留原始 locator,避免语义切分后无法回到原文。

5.4 Parent-child chunk

索引小 chunk,展示或生成时带 parent context。

例子:

  • child chunk: 一个条款;
  • parent chunk: 所在 section;
  • source: 整份合同。

适合需要精确召回但又怕上下文断裂的场景。检索时用 child 命中,生成时把 parent 或相邻 chunk 一起带入。

5.5 Hierarchical chunk

把文档构造成多层:

文档摘要
章节摘要
段落 chunk
原文 span

RAPTOR 这类方法把文本组织成树状摘要和检索结构,适合长文档、多跳问题和总结型问题。工程上可以先做简化版:文档级摘要、section 摘要、原文 chunk 三层。

5.6 Late chunking

常规做法是先切 chunk,再分别 embedding。Late chunking 的思路是先利用更长上下文生成 token 表征,再对 chunk 聚合,从而让每个 chunk embedding 保留更多上下文信息。

它适合长上下文 embedding 能力较强、文档内部指代关系多的场景。落地时要注意成本、模型支持和可复现性,不要在没有 eval 的情况下直接替换基线。

5.7 Graph-aware chunk

如果文档之间存在实体、关系和社区结构,可以在 chunk 外再建图。GraphRAG 这类方法适合跨文档综合、组织知识图谱和 query-focused summarization。

但它不替代普通 chunking。图结构依赖底层证据单元,如果 chunk 本身质量差,图上只是组织了坏证据。

6. Chunk 大小和 overlap 怎么定

OpenAI Retrieval 文档给出一个实用基线:默认 max_chunk_size_tokens 是 800,chunk_overlap_tokens 是 400;可以通过 chunking_strategy 调整。当前限制是 max_chunk_size_tokens 需要在 100 到 4096 之间,overlap 不能超过 chunk size 的一半。

这个默认值适合做第一版,但不要把它当成永远正确。

6.1 先按任务类型定范围

场景建议 chunk 范围overlap说明
FAQ / 短问答100-300 tokens0-50一个问答就是一个证据单元
政策条款300-800 tokens50-200保留条件、例外和步骤
合同条款400-1000 tokens100-250条款和定义可能互相引用
技术文档300-900 tokens50-200保留标题路径和代码块
长报告600-1500 tokens150-400结合 section 摘要
表格按行/区域/语义块少用 token overlap保留行列关系比 token 数更重要
代码按函数/类/文件结构少量上下文不要切断函数签名和实现

6.2 overlap 不是越大越好

overlap 的作用是防止边界切断语义。但 overlap 太大,会造成:

  • 重复 chunk 过多;
  • top-k 被同一段内容占满;
  • embedding 存储成本增加;
  • 引用结果冗余;
  • MRR 和 Recall 看起来提升,但答案覆盖没有真正提升。

一个实用做法是限制同一 source 在 top-k 中的数量,或者对相邻 chunk 做去重。

6.3 用 eval 选参数

不要凭感觉选 chunk size。至少比较这些组合:

300 / 50
600 / 100
800 / 200
800 / 400
1200 / 300

对每组跑第四篇说过的检索 eval:

  • Hit Rate@k;
  • Recall@k;
  • MRR;
  • nDCG;
  • Duplicate Rate;
  • Citation Support;
  • token 成本;
  • p95 延迟。

最终选择通常不是最高 Recall 的配置,而是召回、精度、成本和引用可读性的折中。

7. 不同文档类型怎么处理

7.1 PDF

PDF 是企业 RAG 的重灾区。常见问题包括:

  • 两栏排版顺序错;
  • 页眉页脚重复;
  • 脚注混入正文;
  • 表格丢行列;
  • 扫描件 OCR 错;
  • 页码和原文 locator 丢失。

建议:

  • 保存页码和字符/坐标位置;
  • 去掉重复页眉页脚;
  • 对表格单独抽取;
  • OCR 结果要标记置信度;
  • 扫描件低质量页面进入人工修复队列。

7.2 Word / Markdown / HTML

这些格式结构较好,优先按标题树切分。

保留:

  • H1/H2/H3;
  • 列表层级;
  • 表格;
  • 代码块;
  • 链接;
  • 更新时间;
  • 文档路径。

不要把 HTML 导航、页脚、侧边栏一起入库。

7.3 表格和电子表格

表格不要简单转成连续文本。

错误做法:

企业版 10% 专业版 5% 免费版 0%

更好的证据单元:

{
  "chunk_id": "pricing_2026_q2#table_sla#row_enterprise",
  "source_id": "pricing_2026_q2",
  "table": "SLA Credit",
  "row_key": "enterprise",
  "columns": {
    "plan": "企业版",
    "sla_credit": "10%",
    "notice_window": "30 天"
  },
  "text": "SLA Credit 表中,企业版的 SLA credit 为 10%,通知窗口为 30 天。"
}

检索文本可以是自然语言化后的行,审计时仍然回到行列坐标。

7.4 代码文档和源码

代码不要按固定 token 粗暴切。

适合的边界:

  • 文件;
  • class;
  • function;
  • method;
  • docstring;
  • 注释块;
  • 测试用例。

保存:

  • repo;
  • branch/commit;
  • file path;
  • symbol name;
  • start line/end line;
  • language;
  • imports;
  • dependency relation。

代码问答的引用最好能跳到具体行号。

7.5 FAQ 和客服知识

FAQ 最适合一问一答作为 chunk。

但要注意同义问题:

{
  "canonical_question": "退款多久到账?",
  "aliases": ["钱什么时候退回来?", "退款几天到?"],
  "answer": "退款通常在 1-5 个工作日内退回原支付渠道。",
  "tags": ["refund", "payment"]
}

aliases 可以进入检索文本,但展示时不要混淆成原文。

8. Metadata 是检索质量的一半

第四篇已经讲过,权限和时效性必须在检索层处理。metadata 是实现这个目标的基础。

OpenAI Retrieval 文档里提到,vector store file 可以带 attributes,用于 attribute filtering。文档也说明 attributes 字典有键数和长度限制。这提醒我们:metadata 不能随便堆,要设计成少而关键的字段。

推荐最小字段:

字段用途
source_id关联原始文档
chunk_id稳定证据单元
tenant_id租户隔离
access_scope权限过滤
updated_at时效性
version多版本策略
statusactive/deprecated/draft
source_typepolicy/contract/faq/code/table
language多语言检索
owner责任人和审核
content_hash增量更新
hierarchy_path结构上下文

如果平台的 metadata key 数有限,可以把过滤必需字段放到 attributes,把展示和审计字段放到自有数据库。

一个常见架构是:

Vector Store:
- chunk_id
- searchable text
- embedding
- minimal filter attributes

Metadata DB:
- source registry
- locator
- permissions
- version history
- source hash
- full quote
- audit info

不要把所有治理信息塞进向量库,也不要让向量库成为唯一真相源。

9. 去重、版本和增量更新

9.1 去重

知识库里经常有重复内容:

  • 同一 PDF 被上传多次;
  • Word 和 PDF 是同一文档不同格式;
  • FAQ 复制了政策条款;
  • 新旧版本高度相似;
  • chunk overlap 造成大量重复片段。

去重可以分三层:

层级方法
文件级source hash
段落级normalized text hash
语义级embedding 相似度或 MinHash

但不要把所有重复都删掉。新旧版本相似但都需要保留时,应该保留版本关系,而不是去掉旧版。

9.2 版本

版本字段要回答:

  • 这是 draft、active 还是 deprecated?
  • 哪个版本当前生效?
  • 生效日期是什么?
  • 被哪个版本替代?
  • 查询 as_of 某个日期时应该命中哪个版本?

示例:

{
  "source_id": "refund_policy_2026_03",
  "version": "2026-03",
  "status": "active",
  "valid_from": "2026-03-18",
  "valid_to": null,
  "supersedes": "refund_policy_2025_10"
}

9.3 增量更新

每次知识库更新,不应该全量重建,除非规模很小。

更好的流程:

  1. 计算 source hash;
  2. 未变化文档跳过;
  3. 变化文档重新解析;
  4. 对比 chunk hash;
  5. 新增 chunk 写入索引;
  6. 删除或废止旧 chunk;
  7. 记录变更日志;
  8. 对受影响主题跑回归 eval。

OpenAI Retrieval 文档提到,移除 vector store 文件后,搜索结果在短时间内仍可能出现已移除内容。这类最终一致性细节在生产里要考虑:如果是高风险知识库,删除或废止后要有短暂保护策略,例如过滤 status=active,而不只依赖物理删除。

10. 一个最小结构化 chunker 示例

下面示例演示如何把 Markdown 文档按标题结构切成 evidence chunks。它不是完整生产实现,但展示了关键思路:

  • 保留标题路径;
  • 给每个 chunk 生成稳定 id;
  • 限制最大长度;
  • 添加 metadata;
  • 输出 JSONL。
import hashlib
import json
import re
from datetime import datetime, timezone
from pathlib import Path


def sha256(text):
    return hashlib.sha256(text.encode("utf-8")).hexdigest()


def approx_tokens(text):
    # 粗略估算,生产环境应使用目标模型 tokenizer。
    chinese_chars = len(re.findall(r"[\u4e00-\u9fff]", text))
    ascii_words = len(re.findall(r"[A-Za-z0-9_]+", text))
    return chinese_chars + ascii_words


def split_markdown_blocks(markdown):
    blocks = []
    hierarchy = []
    buffer = []

    def flush():
        if buffer:
            blocks.append({
                "hierarchy_path": hierarchy[:],
                "text": "\n".join(buffer).strip()
            })
            buffer.clear()

    for line in markdown.splitlines():
        heading = re.match(r"^(#{1,6})\s+(.+)$", line)
        if heading:
            flush()
            level = len(heading.group(1))
            title = heading.group(2).strip()
            hierarchy[:] = hierarchy[: level - 1] + [title]
            continue

        if line.strip():
            buffer.append(line)
        else:
            flush()

    flush()
    return [b for b in blocks if b["text"]]


def pack_blocks(blocks, max_tokens=700):
    chunks = []
    current = []
    current_path = []
    current_tokens = 0

    for block in blocks:
        block_text = block["text"]
        block_tokens = approx_tokens(block_text)

        if current and current_tokens + block_tokens > max_tokens:
            chunks.append({
                "hierarchy_path": current_path,
                "text": "\n\n".join(current)
            })
            current = []
            current_tokens = 0

        current_path = block["hierarchy_path"]
        current.append(block_text)
        current_tokens += block_tokens

    if current:
        chunks.append({
            "hierarchy_path": current_path,
            "text": "\n\n".join(current)
        })

    return chunks


def build_chunks(path, source_meta):
    raw = Path(path).read_text(encoding="utf-8")
    source_hash = sha256(raw)
    blocks = split_markdown_blocks(raw)
    packed = pack_blocks(blocks)
    ingested_at = datetime.now(timezone.utc).isoformat()

    for index, chunk in enumerate(packed, start=1):
        context_prefix = " > ".join(chunk["hierarchy_path"])
        text = chunk["text"].strip()
        chunk_id = f"{source_meta['source_id']}#chunk_{index:04d}"

        yield {
            "chunk_id": chunk_id,
            "source_id": source_meta["source_id"],
            "source_title": source_meta["source_title"],
            "source_type": source_meta["source_type"],
            "version": source_meta["version"],
            "status": source_meta["status"],
            "updated_at": source_meta["updated_at"],
            "owner": source_meta["owner"],
            "tenant_id": source_meta["tenant_id"],
            "access_scope": source_meta["access_scope"],
            "language": source_meta.get("language", "zh-CN"),
            "hierarchy_path": chunk["hierarchy_path"],
            "context_prefix": context_prefix,
            "text": text,
            "index_text": f"{context_prefix}\n{text}" if context_prefix else text,
            "content_hash": "sha256:" + sha256(context_prefix + "\n" + text),
            "source_hash": "sha256:" + source_hash,
            "ingested_at": ingested_at
        }


if __name__ == "__main__":
    meta = {
        "source_id": "refund_policy_2026_03",
        "source_title": "退款政策 2026-03 版",
        "source_type": "policy_doc",
        "version": "2026-03",
        "status": "active",
        "updated_at": "2026-03-18",
        "owner": "finance_ops",
        "tenant_id": "global",
        "access_scope": ["customer_service", "finance"],
        "language": "zh-CN"
    }

    with Path("evidence_chunks.jsonl").open("w", encoding="utf-8") as f:
        for row in build_chunks("refund_policy.md", meta):
            f.write(json.dumps(row, ensure_ascii=False) + "\n")

生产版本还需要:

  • 真正 tokenizer;
  • PDF/HTML/Docx parser;
  • 表格结构处理;
  • locator;
  • ACL 映射;
  • source registry;
  • embedding 写入;
  • 删除和废止策略;
  • eval 自动化。

但这个例子已经体现核心原则:chunk 是带上下文和治理信息的证据对象,不是纯文本片段。

11. 如何评测 chunking 是否有效

评测 chunking 不能只看“切出来多少块”。要回到业务问题。

11.1 离线检索评测

用第四篇的 gold set:

{
  "query_id": "refund_001",
  "query": "跨境订单退款多久到账?什么情况需要人工审核?",
  "gold_chunk_ids": [
    "refund_policy_2026_03#sec_4#chunk_02"
  ],
  "required_facts": ["到账时间", "跨境支付需人工审核"]
}

对不同 chunk 策略跑相同查询,比较:

  • Hit Rate@k;
  • Recall@k;
  • MRR;
  • nDCG;
  • Duplicate Rate;
  • Citation Support;
  • token 成本;
  • 检索延迟。

11.2 引用可读性评测

chunk 命中不等于适合引用。还要问:

  • 用户点开 citation 后,是否能直接看懂?
  • quote 是否过长?
  • 是否包含无关段落?
  • 是否缺少必要上下文?
  • 是否能高亮到原文位置?

11.3 失败样本归因

把错误分成几类:

失败类型说明可能修复
Missing source文档没入库source inventory
Parse failure解析错或 OCR 错parser/OCR 修复
Bad boundarychunk 切断语义结构化切分
Too broadchunk 太大缩小 chunk 或按 section 切
Too narrowchunk 太小parent-child 或 overlap
Lost table relation表格行列丢失表格结构化
Stale version旧文档命中version filter
Permission leak越权召回metadata filter

没有失败归因,chunking 实验会变成调参玄学。

12. 生产落地架构

推荐把摄取系统拆成几个模块。

Source Connectors
  -> Parser Workers
  -> Normalizer
  -> Structure Extractor
  -> Chunk Builder
  -> Metadata Enricher
  -> Quality Gate
  -> Vector Index + Keyword Index
  -> Metadata Store
  -> Retrieval Eval

12.1 Source Connectors

负责拉取:

  • 文件夹;
  • CMS;
  • Notion/Confluence;
  • Google Drive/SharePoint;
  • 数据库;
  • Git repo;
  • 工单系统;
  • 网页。

Connector 要记录 source id、路径、更新时间、owner 和权限。

12.2 Parser Workers

不同格式用不同 parser。不要用一个通用 read_text 解决所有问题。

Parser 输出 blocks:

{
  "block_id": "b_0012",
  "type": "paragraph",
  "text": "...",
  "page": 4,
  "section": "4.2",
  "bbox": [72, 120, 520, 188]
}

12.3 Quality Gate

入库前做质量检查:

  • 空文本比例;
  • OCR 置信度;
  • 重复页眉页脚;
  • 表格解析失败;
  • chunk 太短/太长;
  • 缺少权限字段;
  • 缺少版本字段;
  • source hash 异常;
  • 非 UTF-8 文本。

质量失败的文档不要静默入库。标记为 ingestion_failed,进入修复队列。

12.4 双索引

不要只依赖向量索引。很多企业问题包含精确术语、编号、合同条款、错误码、API 名称。hybrid search 通常更稳。

OpenAI Retrieval 文档也提到 ranking options 里可以调 hybrid search 中 embedding 和 text 的权重。工程上应该把 semantic 和 keyword 的表现分开观察。

12.5 Metadata Store

向量库负责找相似文本,metadata store 负责治理和审计。两者通过 chunk_idsource_id 关联。

13. 常见误区

13.1 上传文件等于建知识库

上传只是开始。生产知识库需要解析质量、权限、版本、引用定位和评测闭环。

13.2 默认 chunk 参数永远够用

默认参数适合起步,不适合所有文档。FAQ、合同、表格、代码、长报告的切分策略不同。

13.3 只保存 chunk 文本

没有 metadata 的 chunk 不能过滤、不能审计、不能可靠引用。

13.4 表格转文本后就完事

表格最重要的是行列关系。转成散文后,模型可能把专业版和企业版的字段混在一起。

13.5 overlap 越大越好

overlap 会增加冗余。它解决边界问题,也制造重复问题。必须结合 Duplicate Rate 和 Citation Support 看。

13.6 不做版本废止

删除旧文档不等于生产安全。要有 active/deprecated 状态、valid_from/valid_to 和查询时过滤。

13.7 不保留 locator

没有页码、section、行号或 span,引用 UI 就只能跳到整篇文档,用户很难核查。

14. 落地路线图

第 1 周:建立 source registry

  • 盘点知识来源;
  • 定义 source id;
  • 保存 owner、权限、版本、更新时间;
  • 标记哪些来源可以入库。

第 2 周:做结构化解析

  • 对 PDF/Docx/HTML/Markdown/表格分别解析;
  • 去掉页眉页脚和导航噪声;
  • 保留标题树、页码、表格、代码块。

第 3 周:生成 evidence chunks

  • 定义 evidence_chunks.jsonl schema;
  • 按文档类型选择 chunk 策略;
  • 添加 context_prefix;
  • 生成 chunk hash 和 source hash。

第 4 周:写入索引和 metadata store

  • embedding 写入向量索引;
  • 精确字段写入 keyword index;
  • 权限、版本、locator 写入 metadata store;
  • 建立删除和废止流程。

第 5 周:跑 chunking eval

  • 用 50-100 条业务问题标注 gold chunk;
  • 比较不同 chunk size、overlap 和策略;
  • 输出失败归因;
  • 固化一版上线阈值。

15. 发布前自检清单

上线前至少问完这些问题:

  • 是否有 source registry,而不是散落的文件路径?
  • 每个 source 是否有 owner、更新时间、版本和权限?
  • PDF 是否处理页眉页脚、页码、两栏、OCR 和表格?
  • 表格是否保留行列关系和单元格 locator?
  • 每个 chunk 是否有稳定 chunk_idsource_id
  • 每个 chunk 是否有 hierarchy_pathcontext_prefix 和原文 locator?
  • 是否有 status=active/deprecated/draft 和版本过滤?
  • 是否记录 content_hashsource_hash
  • 是否有增量更新和废止策略?
  • 是否针对 chunk 参数跑过 retrieval eval?
  • 是否看了 Duplicate Rate、Citation Support、token 成本和延迟?
  • 是否能从 citation 跳回原文位置?
  • 是否有 ingestion failed 队列,而不是静默入库坏文档?

如果这些问题答不上来,知识库还只是“文件集合”,不是生产级 RAG 知识库。

16. 总结

RAG 的摄取与 chunking,核心不是把文本切成多段,而是把原始材料变成可检索、可引用、可过滤、可审计的证据单元。

最小可用链路是:

Source Registry -> Parser -> Normalizer -> Structure Recovery -> Evidence Chunk -> Metadata -> Index -> Eval -> Fix Loop

做得好的摄取系统,会让后面的检索、引用和评测都变简单。做得差的摄取系统,会把所有问题推给 prompt 和模型。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zzj_tju

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值