AI 基础-文档入库第一步,Loader 和 Splitter 决定 RAG 上限
很多人做 RAG,第一反应是上向量数据库。
选 Milvus、选 pgvector、选 Pinecone、选 Elasticsearch,开始比较索引、召回、Rerank、Embedding 模型。
这些当然重要。
但我见过很多 RAG 效果差的系统,问题根本不在向量库,而在更前面:文档入库第一步就错了。
文档读得脏,后面检索再高级也救不回来。
切块切得碎,模型拿到的上下文就断。
元数据丢了,回答就没法追溯来源。
权限没带进去,召回就可能越权。
版本没记录,用户问的是新制度,模型引用的却是旧文档。
这就是 Loader 和 Splitter 的重要性。
Loader 负责把不同来源的资料读进来。
Splitter 负责把长文档切成适合检索和生成的小块。
听起来很基础,但这一步决定了知识库的地基。
地基歪了,上面盖什么都不稳。
## 一、Loader 不是“读文件”,而是把资料变成统一 Document
LangChain 官方文档里,Document Loader 的定义很直接:它提供标准接口,把不同来源的数据读入 LangChain 的 Document 格式。
这个定义里有两个重点。
第一,不同来源。
你的资料不一定是纯文本文件。
它可能是 PDF、Word、Markdown、网页、Notion、Slack、Google Drive、数据库记录、CSV、会议纪要、工单、代码仓库、知识库页面。
每种来源的结构都不一样。
PDF 有页码。
网页有标题、正文、导航、广告。
Notion 有层级块。
Slack 有频道、用户、时间。
数据库有字段和行。
如果每种资料都用不同格式进入系统,后面的切分、索引、检索、评测都会乱。
所以 Loader 的第一层价值,是统一入口。
第二,Document 格式。
Document 不只是文本。
它通常至少包含两类信息:page_content 和 metadata。
page_content 是正文。
metadata 是来源、页码、标题、时间、作者、权限、版本、业务域等上下文。
很多人只关心 page_content,这是大坑。
因为 RAG 系统最后要回答的不只是“这段话是什么”,还要回答:
– 这段话来自哪里?
– 是哪个版本?
– 谁能看?
– 是否过期?
– 属于哪个业务域?
– 能不能引用给用户?
这些都靠 metadata。
所以,Loader 不是简单读文件。它是在把原始资料变成机器可处理、可追溯、可治理的 Document。
## 二、真正的文档处理,第一步是清洗
文档刚读进来,往往很脏。
网页里有导航栏、页脚、推荐阅读、广告、版权声明。
PDF 里有页眉、页脚、页码、断行、表格错位。
Word 里有批注、修订、目录、格式噪音。
会议纪要里有口头语、重复表达、无效寒暄。
如果这些东西不处理,模型后面会认真地学习垃圾。
这就是知识库建设最容易被忽视的地方:清洗不是洁癖,是质量控制。
比如一个制度文档,每页页脚都有“内部资料,请勿外传”。如果切分时每个 chunk 都带这句话,向量检索可能会反复召回这些无意义文本。
再比如网页抓取时,把菜单栏“首页、产品、价格、登录、联系我们”也放进正文,模型检索时就会拿到大量噪声。
再比如 PDF 里表格被解析成错乱文本,金额和字段对不上,模型后面引用时就可能答错。
所以文档入库至少要做三类清洗:
第一,去噪。
删除导航、广告、页眉页脚、重复版权、无意义空行。
第二,结构修复。
尽量保留标题层级、列表、表格、章节关系。
第三,字段补齐。
补上来源、时间、版本、业务域、权限、文档类型。
这一步做得越好,后面 RAG 越稳。
AI Native 的知识库,不是给人看着舒服就行,而是要让机器能理解、能召回、能引用、能校验。
## 三、Splitter 不是按字数切,而是按语义切
很多人第一次切文档,会这样做:
每 500 字切一段。
然后加 50 字 overlap。
能不能跑?能。
效果好不好?不一定。
因为文档不是均匀的字数块,而是有结构的。
一个标题下面可能是一整段业务规则。
一个表格可能对应一套费用标准。
一个问答条目可能必须问题和答案一起出现。
一个代码说明可能必须函数名、参数、返回值放在同一块里。
如果机械按字数切,很容易把语义切断。
比如原文是:
“退款规则如下:已发货订单不支持无理由退款。未发货订单可在 24 小时内申请退款。”
如果切块刚好把“退款规则如下”切在上一段,把具体规则切在下一段,检索时可能召回不到完整含义。
再比如制度文档里有一个小标题:
“三、费用报销标准”
后面才是具体金额。如果标题和正文分开,模型看到金额时不知道它属于哪个制度。
所以 Splitter 的本质不是切字数,而是尽量在控制 chunk 大小的同时保留语义完整性。
LangChain 官方建议多数场景可以从 RecursiveCharacterTextSplitter 开始,因为它会按一组分隔符递归尝试切分,在 chunk size 和上下文完整性之间取得一个比较稳的平衡。
这也是实用工程里的好起点。
先用默认稳妥方案,再根据业务文档调整。
## 四、chunk_size 和 chunk_overlap 怎么理解
Splitter 最常见的两个参数,是 chunk_size 和 chunk_overlap。
chunk_size 是每个文本块的目标大小。
chunk_overlap 是相邻文本块之间重叠多少内容。
很多人会问:到底设置多少最好?
没有通用答案。
因为它取决于几个因素:
– 文档类型。
– Embedding 模型上下文长度。
– 检索任务粒度。
– 目标模型上下文窗口。
– 用户问题复杂度。
– 是否需要引用原文。
如果 chunk 太小,问题是语义不完整。
用户问“报销规则是什么”,召回出来的可能只有一个金额,没有上下文。
如果 chunk 太大,问题是召回不精准。
用户只问“差旅住宿标准”,系统却召回一大段行政制度,模型需要在里面自己找重点。
overlap 的作用,是减少切块边界带来的语义断裂。
但 overlap 太大,也会带来重复内容、索引变大、召回结果相似度过高的问题。
我的建议是:不要迷信固定参数。
先基于文档类型做一个初始值,再用评测集调。
比如:
– FAQ:可以按问答对切,chunk 不必太大。
– 制度文档:按章节和小标题切,保留标题路径。
– 技术文档:按标题、代码块、参数说明切。
– 会议纪要:按议题、结论、行动项切。
– 表格数据:不要简单转成散文,要保留字段关系。
真正成熟的系统,不是调一个神奇 chunk_size,而是针对资料类型设计切分策略。
## 五、metadata 是 RAG 的身份证
如果只能强调一个点,我会强调 metadata。
很多 RAG 系统只把文本塞进向量库,metadata 极其敷衍。
最后就会遇到很多问题。
用户问:“这个政策是哪天发布的?”
答不上来。
用户问:“这个规则适用于哪个城市?”
答不上来。
用户问:“这段答案能引用原文吗?”
找不到来源。
用户权限不够,系统却召回了敏感文档。
新旧制度冲突,模型不知道哪个更新。
这些问题不是模型问题,是 metadata 缺失。
一个合格的 Document,至少要考虑这些元数据:
– source:来源路径或 URL。
– title:文档标题。
– section:章节标题。
– page:页码或位置。
– created_at:创建时间。
– updated_at:更新时间。
– version:版本号。
– owner:负责人。
– permission_scope:权限范围。
– doc_type:制度、FAQ、会议纪要、代码文档、合同等类型。
– business_domain:业务域。
metadata 的价值有三层。
第一,检索过滤。
比如只检索用户有权限看的文档,只检索最新版本,只检索某个业务域。
第二,答案引用。
让模型回答时能给出来源,而不是空口断言。
第三,问题排查。
答案错了,你能追到是哪份文档、哪个 chunk、哪个版本导致的。
没有 metadata,RAG 就像没有身份证的人群。看起来热闹,但无法治理。
## 六、文档入库要服务机器,不是服务人的阅读习惯
这里要讲一个很重要的认知。
很多人整理知识库时,会下意识按照人类阅读习惯来整理。
排版漂亮,标题好看,段落顺眼。
这当然不是坏事。
但 AI 知识库的第一目标,不是让人看着舒服,而是让机器能正确理解、检索和引用。
人阅读时,可以靠上下文脑补。
机器检索时,拿到的可能只是一个 chunk。
如果这个 chunk 里没有标题,没有来源,没有业务域,没有时间,没有权限,它就很难被正确使用。
所以 AI Native 的文档处理,要换一个视角。
不要只问:
“这篇文档人看起来顺不顺?”
还要问:
“如果模型只拿到其中一块,它能不能知道这块在讲什么?”
“它能不能知道这块适用于什么场景?”
“它能不能知道这块是不是最新版本?”
“它能不能把答案引用回原文?”
“它能不能避免越权召回?”
这才是机器可理解的知识库。
## 七、怎么评估切分效果
文档切完以后,不要直接入库就完事。
要评估。
最简单的评估方式,是准备一批真实问题。
比如制度问答,可以准备:
– 报销上限是多少?
– 哪些情况不能退款?
– 审批超过几天会自动提醒?
– 某个城市是否适用这条规则?
– 新旧制度冲突时以哪个为准?
然后看检索结果。
第一,正确 chunk 有没有被召回。
如果正确内容根本没进 top-k,后面模型再强也没用。
第二,chunk 是否完整。
召回结果是否包含必要标题、规则正文、适用范围。
第三,噪声是否过多。
是不是召回了导航、页脚、重复声明、无关章节。
第四,metadata 是否够用。
能不能过滤版本、权限、业务域、时间。
第五,模型能不能基于 chunk 正确回答。
最终答案是否引用了正确来源,是否有编造。
这才是文档处理的闭环。
不要只看“切了多少块、入了多少库”。
知识库不是仓库越大越好,而是能不能在关键问题上召回正确证据。
## 八、怎么练:从一个小知识库开始
如果你要练 Loader 和 Splitter,不建议一上来搞几万份文档。
先做一个小知识库。
选 10 份文档就够。
最好包含几种类型:
– 一份 PDF 制度。
– 一份 Markdown 技术文档。
– 一份 FAQ。
– 一份会议纪要。
– 一份表格导出的文本。
第一步,用 Loader 读入。
观察 Document 的 page_content 和 metadata。
第二步,做清洗。
去掉页眉页脚、重复文本、乱码、导航。
第三步,设计 metadata。
至少补上 source、title、section、updated_at、doc_type、permission_scope。
第四步,尝试 Splitter。
先用 RecursiveCharacterTextSplitter,再按文档类型调整。
第五步,人工检查 20 个 chunk。
看每个 chunk 是否能独立表达含义。
第六步,准备 20 个真实问题做检索评测。
看正确 chunk 能不能进 top-k。
第七步,再入向量库。
不要反过来,一上来就向量化。
这条路径看起来慢,但非常稳。
因为你不是在做“文档入库 Demo”,你是在建设知识资产生产线。
## 九、最后:RAG 的上限,往往在入库前就决定了
RAG 不是把文档丢进向量数据库。
RAG 是一条信息生产线。
Loader 负责把资料读进来。
清洗负责去掉噪声。
Splitter 负责保留语义地切块。
metadata 负责来源、权限、版本和追溯。
Embedding 负责语义表示。
Retriever 负责召回。
Rerank 负责重排。
LLM 负责基于证据生成答案。
评测负责告诉你哪里错了。
如果前面 Loader 和 Splitter 没做好,后面每一步都会被拖累。
所以,别小看文档处理。
它看起来是脏活、累活、基础活。
但真正能落地的 AI 系统,往往就赢在这些基础活。
AI 越强,越不能低估底座。
知识库越大,越要重视入库质量。
文档越复杂,越要让机器能理解。
这就是 Loader 和 Splitter 在 RAG 里的位置:它们不是配角,而是地基。
## 参考来源
– LangChain 官方文档:Document loader integrations,说明 Document loaders 提供标准接口,把 Slack、Notion、Google Drive 等不同来源数据读入 LangChain Document 格式:https://docs.langchain.com/oss/python/integrations/document_loaders
– LangChain 官方文档:Text splitter integrations,建议多数场景从 RecursiveCharacterTextSplitter 开始,在保持上下文和控制 chunk size 之间取得平衡:https://docs.langchain.com/oss/python/integrations/splitters
– LangChain 官方文档:Recursive text splitter,说明递归分割会按字符列表尝试切分,并支持 split_text / create_documents:https://docs.langchain.com/oss/python/integrations/splitters/recursive_text_splitter
– LangChain Reference:langchain_text_splitters,提供文本切分工具参考:https://reference.langchain.com/python/langchain-text-splitters
夜雨聆风