RAG 数据导入与解析全攻略(一):从 txt 到 Markdown 的通用文本与结构化解

系列定位 :本文属于「RAG 七层框架」系列的第 3 层(L3 · 文档理解与解析)上篇,聚焦通用文本与结构化文本的导入解析。下篇将覆盖图文/PDF/表格。

本文目标读者:正在搭建 RAG 系统的工程师,尤其是需要把「一堆格式不同的原始文档」变成「可检索的 Document 对象」的落地场景。


一、先说结论:为什么「导入」是 RAG 的第一道分水岭

很多人以为 RAG 的难点在检索、在生成,但真正决定系统上限的,往往是最不起眼的第一步------数据导入。原因很简单:

检索质量的上限,在数据进入索引之前就已经被决定了。

如果你把一份 PDF 用最粗暴的方式读出来,得到的是「一坨没有结构的纯文本」,那么后续无论你把分块做得多精细、向量模型选得多强,都救不回丢失的结构信息。反过来,如果导入阶段就保留了标题层级、段落关系、表格结构、父子元素链接,那 L4 分块和 L7 检索就有了坚实的输入基础。

这正是本文要讲透的核心命题:不同的 Loader,解析出来的结果完全不同。选对 Loader,等于给整个 RAG 系统打好了地基。

1.1 整体原则:先「读进来」,再「读出结构」

数据导入这件事,可以拆成两个动作:

  1. 读进来 :把各种格式的文件(txt / JSON / 网页 / Markdown / 图片 / PPT / PDF / 表格)变成统一的 Document 对象。
  2. 读出结构:在读取的同时,尽可能保留文档的内在结构(标题、段落、表格、元素关系),而不是只给一坨文本。

这两步在工具层面往往被耦合在一起------很多 Loader 在「读进来」的同时就已经「读出结构」了。这也是为什么选型时不能只看「能不能读」,还要看「读成什么样」。

1.2 核心数据结构:LangChain 的 Document 对象

整个 LangChain 生态的数据导入,最终都收敛到一个统一的数据结构------Document。它只有两个核心字段,但这两个字段是理解后续所有环节的钥匙:

python 复制代码
from langchain_core.documents import Document

doc = Document(
    page_content="《民法典》婚姻家庭编共分五章......",  # 实际文本内容
    metadata={"source": "data/民法典/婚姻家庭编.txt", "doc_type": "法律法规", "date": "2021-01-01"}
)
字段 作用 在 RAG 中的价值
page_content 保存实际文本内容,是文档的主要数据部分 这是后续被分块、被嵌入、被检索的主体
metadata 存储文档相关元信息:来源路径、作者、日期、页码、标题层级等 这是后续过滤检索范围、溯源、展示 的关键

为什么 metadata 很重要? 因为元数据虽然不参与语义匹配,但它决定了检索的「范围控制」和「结果可信度」。举个例子:

  • 在法律检索场景,metadata 里可以存「法规效力层级」「颁布年份」「章节条款号」,这样检索时就能先按「效力层级」过滤,再算相似度。
  • 在企业知识库场景,metadata 里可以存「部门」「文档类型」「更新时间」,这样就能限定「只检索某个部门 2024 年之后的文档」。

一句话:page_content 决定「检索到」什么,metadata 决定「能不能精准检索到、检索到后能不能讲清楚来源」。


二、通用文本读取:从单文件到整个目录

这是最基础的场景,但也是最容易踩坑的。我们先从最简单的 txt 开始。

2.1 用 LangChain 读入单个 txt 文件

python 复制代码
from langchain_community.document_loaders import TextLoader

loader = TextLoader("data/民法典/婚姻家庭编.txt")
docs = loader.load()

print(docs[0].page_content)
print(docs[0].metadata)

输出就是上面看到的 Document 对象。TextLoader 是最朴素的加载器,只负责把文本读进来,不做任何结构解析。它适合纯文本、无结构的场景。

2.2 读取目录中的所有文档

当文档散落在一个目录里,逐个加载显然不现实。LangChain 提供了批量读取的能力:

python 复制代码
from langchain_community.document_loaders import DirectoryLoader

loader = DirectoryLoader(
    "data/民法典/",
    glob="**/*.txt",           # 匹配所有 txt 文件
    loader_cls=TextLoader,     # 指定用哪种 Loader
    show_progress=True         # 显示加载进度
)
docs = loader.load()

DirectoryLoader 的关键参数:

参数 作用 建议
glob 用通配符匹配文件名 **/*.txt 递归匹配所有 txt;**/*.md 匹配 Markdown
loader_cls 指定用哪个 Loader 来解析每个文件 必须和文件类型匹配,否则解析结果错乱
show_progress 显示加载进度 文件多时建议开启,方便观察

注意 :DirectoryLoader 本身不识别文件类型,它只是「批量调用你指定的 Loader」。所以如果你的目录里混着 txt、md、PDF,需要写一个根据扩展名分发 Loader 的逻辑,或者直接用 UnstructuredLoader(它自带了多格式识别能力,后面会讲)。

2.3 不同 Loader 解析结果不同的直观对比

这是整个数据导入最核心的认知,也是选型的起点。同一个内容,用不同 Loader 解析,结果天差地别:

Loader 解析结果 适用场景
TextLoader 整个文件变成一个大 Document,无结构 纯文本、无结构
UnstructuredLoader 按元素拆成多个 Document ,带 category(Title/ListItem/NarrativeText/Table...) 需要保留结构的文档
UnstructuredMarkdownLoader(mode="elements") 按 Markdown 结构拆成多个元素 ,带 category_depth(标题层级) Markdown、需要标题层级

关键洞察 :TextLoader 是「读进来」,Unstructured 系列是「读出结构」。后者多出来的 metadata["category"] 和 metadata["element_id"],正是 L4 分块和 L7 检索能「按结构处理」的前提。


三、结构化文本解析:JSON / 网页 / Markdown

这一节是本文的重头戏。结构化文本比纯文本复杂得多,因为它们自带「层级」「字段」「关系」,解析的好坏直接决定后续检索的精细程度。

3.1 JSONLoader:用 jq_schema 精确提取字段

JSON 文件最大的特点是有明确的字段结构 。如果直接用通用 Loader,会把整个 JSON 当成一坨文本读进来,丢失字段语义。JSONLoader 的杀手锏是 jq_schema------用 jq 语法精确地「挑出」你想要的字段。

python 复制代码
from langchain_community.document_loaders import JSONLoader

# 1. 提取一条法条的信息(单条记录)
main_loader = JSONLoader(
    file_path="data/法律/法律条文结构.json",
    jq_schema='.mainArticle | "条款:" + .article + ",内容:" + .content',
    text_content=True
)
main_article = main_loader.load()
print(main_article)
# 输出:[Document(metadata={'source': '...', 'seq_num': 1},
#        page_content='条款:第一千零七十七条,内容:离婚冷静期......')]

# 2. 提取所有关联条款(数组遍历)
support_loader = JSONLoader(
    file_path="data/法律/法律条文结构.json",
    jq_schema='.relatedArticles[] | "条款:" + .article + ",要点:" + .summary',
    text_content=True
)
related_articles = support_loader.load()
print(related_articles)
# 输出:3 个 Document,seq_num 分别为 1、2、3

jq_schema 是 JSONLoader 的灵魂,几个常用写法:

jq_schema 写法 含义
.mainArticle 取整个 mainArticle 对象
.mainArticle.article 只取条款号字段
.relatedArticles[] 遍历 relatedArticles 数组
`.relatedArticles[] "条款:" + .article`
`.mainArticle "条款:" + .article + ",内容:" + .content`

text_content=True 的作用:把提取出来的值强制转成字符串文本。如果不加,有些值(如数字、布尔)可能无法正确转成可嵌入的文本。

为什么 jq_schema 重要? 因为「挑字段」这个动作,本质上是在做数据清洗。你可以:

  • 只挑与业务相关的字段(比如法律条文里的「效力层级」「条款内容」),过滤掉无关字段(比如内部 ID、时间戳)。
  • 把多个字段拼接成一句语义完整的文本(比如把「姓名 + 背景」拼成一句话),这样嵌入时是语义连贯的,而不是碎片化的。

对企业落地的启发 :如果你的数据源是 JSON(比如从业务系统导出的 API 数据、配置数据),用 jq_schema 精确挑字段,比把整个 JSON 丢进去要好得多------既能控制索引体积,又能保证语义完整性。

3.2 网页解析:五种 Loader 的选型

网页是 RAG 最常见的输入源之一,但也是最「脏」的------有导航栏、广告、脚本、样式,还有复杂的嵌套结构。LangChain 提供了五种网页加载器,各有侧重。

加载器 底层实现 特点 适用场景
WebBaseLoader urllib + BeautifulSoup 简单易用,适合基本网页内容抓取 单页、结构简单的网页
UnstructuredLoader Unstructured 解析 支持多种复杂网页结构,适合异构内容 内容复杂、需要保留结构的网页
RecursiveURLLoader 递归抓取子链接 自动化链接抓取 大规模站点抓取
SitemapLoader 从站点地图抓取 高效解析站点结构 有 sitemap 的站点
FirecrawlLoader 可本地部署的 API 服务 灵活可扩展,托管版有免费额度 需要实时抓取和转换

3.2.1 WebBaseLoader:最朴素的网页抓取

python 复制代码
import bs4
from langchain_community.document_loaders import WebBaseLoader

page_url = "https://example.com/wiki/中华人民共和国民法典"
loader = WebBaseLoader(web_paths=[page_url])
docs = loader.load()
doc = docs[0]

print(doc.metadata)
# {'source': 'https://example.com/wiki/中华人民共和国民法典',
#  'title': '中华人民共和国民法典 - 示例文档', 'language': 'zh'}

print(doc.page_content.strip())

WebBaseLoader 的坑 :它用 BeautifulSoup 把 HTML 里的 <script>、<style> 等标签剥掉,但不会做内容净化 。从上面的输出可以看到,page_content 里残留了大量 JS 代码(RLCONF={"wgBreakFrames":false...})。这些垃圾内容如果被嵌入,会严重污染向量空间。

解决办法 :用 WebBaseLoader 的 bs_kwargs 参数自定义 BeautifulSoup 的解析规则,或者干脆换成 UnstructuredLoader。

3.2.2 UnstructuredLoader:保留网页结构

python 复制代码
from langchain_unstructured import UnstructuredLoader

page_url = "https://example.com/wiki/中华人民共和国民法典"
loader = UnstructuredLoader(web_url=page_url)
docs = loader.load()

for doc in docs[:5]:
    print(f'{doc.metadata["category"]}: {doc.page_content}')
# 输出:
# Title: 中华人民共和国民法典
# ListItem: العربية
# ListItem: مصرى
# ListItem: Azərbaycanca

关键区别 :UnstructuredLoader 解析网页时,会给每个元素打上 category 标签(Title、ListItem、NarrativeText、Table 等)。这意味着网页被拆成了有语义的多个元素,而不是一整坨文本。

对企业落地的启发 :抓取官网、文档站、新闻页时,用 UnstructuredLoader 能保留标题和正文的层级,这样 L4 分块时就能「按标题分块」,而不是「按字数硬切」。

3.2.3 网页解析的进阶:递归抓取与站点地图

  • RecursiveURLLoader:从根 URL 开始,递归抓取所有子链接。适合知识库网站、文档中心这种「一个入口、很多子页面」的场景。
  • SitemapLoader :直接读 sitemap.xml,按站点地图抓取。效率高,但要求站点有 sitemap。
  • FirecrawlLoader:一个可本地部署的 API 服务,能把网页转成干净的 Markdown。适合对抓取质量要求高、需要实时转换的场景。

3.3 Markdown 解析:保留标题层级的关键

Markdown 自带标题层级(#、##、###),这是 RAG 里最宝贵的结构信息。但用朴素 Loader 读 Markdown,会把整个文件变成一个大文本,标题层级就丢了。

3.3.1 默认模式:整个文件一个 Document

python 复制代码
from langchain_community.document_loaders import UnstructuredMarkdownLoader

markdown_path = "data/民法典/婚姻家庭编.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()
print(data[0].page_content)
# 输出:整个 Markdown 的内容,标题层级全部丢失

3.3.2 elements 模式:保留标题层级

python 复制代码
loader = UnstructuredMarkdownLoader(markdown_path, mode="elements")
data = loader.load()
print(f"Number of documents: {len(data)}")

for document in data:
    print(document)

mode="elements" 是 Markdown 解析的分水岭。它把 Markdown 拆成多个元素,并且每个元素都带上了关键的元数据:

python 复制代码
# 第一个元素(标题)
page_content='婚姻家庭编 📖'
metadata={
    'source': 'data/民法典/婚姻家庭编.md',
    'category_depth': 0,          # 标题层级!0 表示一级标题
    'category': 'Title',          # 元素类型
    'element_id': 'b89add...',    # 元素唯一 ID
    'filetype': 'text/markdown',
    'languages': ['zho'],
    ...
}

# 第二个元素(正文)
page_content='婚姻家庭编是调整因婚姻家庭产生的民事关系...'
metadata={
    'source': '...',
    'parent_id': 'b89add...',     # 指向父元素(标题)的 ID!
    'category': 'UncategorizedText',
    'element_id': '4d1fd...',
    ...
}

两个最关键的元数据字段:

  1. category_depth :表示标题层级。0 是一级标题,1 是二级标题,以此类推。这是 L4 分块时「按标题层级切分」的输入基础。

  2. parent_id / element_id :表示元素之间的父子关系。正文元素的 parent_id 指向它所属标题的 element_id。这是构建「父子块」「层级索引」的关键。

对企业落地的启发 :如果你要构建一个按章节组织 的知识库(比如技术文档、规章制度、法律条文),mode="elements" 是必须的。它让你可以在 L4 分块时按标题层级切,而不是按字数硬切,从而保证「一个章节的内容不被切碎」。


四、解析元素之间的关系:父子元素链接

这一节是「结构化解析」的精华,也是很多教程容易略过、但对 RAG 质量影响极大的技术点。

4.1 为什么需要「父子元素链接」?

在文档里,标题和它下面的正文、表格,天然存在「父子关系」。比如:

bash 复制代码
# 婚姻家庭编        ← 父元素(Title)
离婚冷静期条款 📄 规定三十日内可撤回   ← 子元素(NarrativeText)

如果把这些元素平铺成一个个独立的 chunk,检索时就会出现问题:

  • 检索「离婚冷静期包含什么」,可能只命中正文,但不知道它属于哪个章节。
  • 检索「婚姻家庭编」,可能命中标题,但不知道下面有哪些内容。

父子元素链接就是解决这个问题的:把「标题」和「它下面的内容」绑定起来,检索时既能命中细节,又能找到所属的章节。

4.2 实战代码:构建父子元素关系

下面这段代码演示了如何遍历 Unstructured 解析出的元素,把「标题/表格」作为父元素,把「它下面的元素」作为子元素,构建父子关系:

python 复制代码
from langchain_unstructured import UnstructuredLoader
from typing import List
from langchain_core.documents import Document

page_url = "https://example.com/wiki/中华人民共和国民法典"

def _get_setup_docs_from_url(url: str) -> List[def _get_setup_docs_from_url(url: str) -> List[Document]:
    loader = UnstructuredLoader(web_url=url)
    setup_docs = []

    parent_id = None       # 初始化 parent_id
    current_parent = None  # 用于存储当前父元素

    for doc in loader.load():
        # 检查是否是 Title 或 Table(作为父元素)
        if doc.metadata["category"] == "Title" or doc.metadata["category"] == "Table":
            parent_id = doc.metadata["element_id"]
            current_parent = doc  # 更新当前父元素
            setup_docs.append(doc)
        # 检查是否属于当前父元素
        elif doc.metadata.get("parent_id") == parent_id:
            setup_docs.append((current_parent, doc))  # 将父元素和子元素一起存储

    return setup_docs

docs = _get_setup_docs_from_url(page_url)
for item in docs:
    if isinstance(item, tuple):
        parent, child = item
        print(f'父元素 - {parent.metadata["category"]}: {parent.page_content}')
        print(f'子元素 - {child.metadata["category"]}: {child.page_content}')
    else:
        print(f'{item.metadata["category"]}: {item.page_content}')
    print("-" * 80)

这段代码的核心逻辑:

  1. 遍历 Unstructured 解析出的所有元素。
  2. 遇到 Title 或 Table,把它标记为「当前父元素」,并记录它的 element_id。
  3. 遇到 parent_id 指向当前父元素的元素,把它作为「子元素」,和父元素一起存成 (parent, child) 元组。
  4. 后续检索时,就能用这个父子关系做「父子块」检索(L4 的 Parent-Child Docs 技术)。

为什么这个技术点值得单独讲? 因为它是从「解析」跨到「分块」的桥梁 。很多资料把「解析元素关系」一笔带过,但它的直接用途,就是 L4 的「父子块索引」。这也是为什么我在系列里说:L3 的「结构保留」正是 L4 分块的输入基础。


五、选型决策:什么时候用哪个 Loader?

这是本文最实用的一节。我把通用文本/结构化文本场景的选型逻辑,浓缩成一张决策表。

5.1 速查选型表

你的输入 首选 Loader 备选 关键注意点
纯 txt、无结构 TextLoader UnstructuredLoader 无结构,直接读即可
目录下多文件 DirectoryLoader + 对应 Loader UnstructuredLoader 需按扩展名分发 Loader
JSON 数据 JSONLoader + jq_schema 通用 Loader 用 jq 挑字段,控制体积、保语义
单个网页 WebBaseLoader UnstructuredLoader WebBase 会残留 JS,建议净化
复杂网页 UnstructuredLoader FirecrawlLoader 保留 category 结构
大批量站点 RecursiveURLLoader / SitemapLoader FirecrawlLoader 递归抓取或读 sitemap
Markdown UnstructuredMarkdownLoader TextLoader 必须用 mode="elements" 保留标题层级
需要保留结构 Unstructured 系列 手动解析 检查 category / category_depth / parent_id

5.2 三条选型铁律

铁律一:能保留结构,就不要只读文本。 纯文本 Loader(TextLoader、WebBaseLoader)只负责「读进来」,丢掉了标题、段落、表格关系。只要你的文档有结构(Markdown、网页、PDF、JSON),就应该用能「读出结构」的 Loader(Unstructured 系列、JSONLoader)。

铁律二:metadata 决定检索的「可控性」,别让它空着。 source、category、category_depth、parent_id、element_id 这些元数据,是后续 L4 分块、L7 检索、结果溯源的「基础设施」。选 Loader 时,要看它能产生哪些 metadata,而不是只看它能不能读。

铁律三:解析结果「可复现、可检查」,不要黑盒。 不同 Loader 解析结果差异巨大(P11 的核心结论)。上线前,一定要把解析结果 dump 出来看一眼 ,确认 category 标对了、parent_id 链接对了。否则等到检索环节才发现结构丢了,返工成本极高。

5.3 一个完整的选型决策流程

ini 复制代码
你的文档是什么格式?
├── 纯文本(txt)→ TextLoader
├── JSON → JSONLoader + jq_schema(挑字段)
├── 网页 → 单页用 WebBaseLoader / 复杂用 UnstructuredLoader
│        └── 大批量 → RecursiveURLLoader / SitemapLoader / FirecrawlLoader
├── Markdown → UnstructuredMarkdownLoader(mode="elements")
├── 需要保留结构 → Unstructured 系列(检查 category / parent_id)
└── 其他格式(图片/PPT/PDF/表格)→ 见本系列下篇

六、企业落地视角:这套解析逻辑怎么用在法律/知识库场景?

作为系列的一贯风格,我把这套通用解析逻辑,映射到一个具体的企业落地场景------法律条文检索平台(这也是你正在搭建的七层 RAG 框架中 L3/L4 的核心任务)。

6.1 通用 Loader 能解决什么、不能解决什么

场景 通用 Loader 能做的 通用 Loader 做不到的
法律条文 PDF 用 UnstructuredPDFLoader 把 PDF 转成带结构的元素 识别「效力层级」「新旧法时效」
法规条款 Markdown 用 UnstructuredMarkdownLoader(mode="elements") 保留标题层级 识别「但书」「兜底条款」等法律结构
案例 JSON 用 JSONLoader 挑出「案号」「裁判要旨」字段 识别「援引关系」(本案援引了哪条法条)
条文表格 用 Unstructured 保留表格结构 识别「上位法/下位法」的效力关系

核心结论 :通用 Loader 解决的是「把文档变成有结构的文本 」这个通用问题。但法律知识真正有价值的部分------效力层级、新旧法时效、援引关系、条款结构(但书/兜底条款) ------是关系型的,通用 Loader 给不了。

6.2 这意味着什么?

这意味着在 L3/L4 层,你不能只依赖通用 Loader。你需要:

  1. 在 L3 用通用 Loader 做「结构保留」 :把 PDF、Markdown、JSON 转成带 category、parent_id、category_depth 的元素。
  2. 在 L4 做「关系建模」 :在通用 Loader 产出的元素基础上,自研一层关系抽取,把「效力层级」「援引关系」「条款结构」建模成实体-关系三元组(GraphRAG 式)。
  3. L7 检索时:用「结构 + 关系」做约束检索,而不是纯向量相似度。

这也是为什么我说:法律检索平台的核心开发任务在 L3/L4,而不是 L7 。因为 L7 的 BM25 + 向量 + RRF + 领域 rerank 是成熟通用的,真正的差异化在「导入阶段建模了什么关系」。


七、小结与下篇预告

7.1 本文核心要点

  1. 导入是 RAG 的第一道分水岭:检索质量的上限,在数据进入索引之前就被决定了。
  2. Document 对象是统一载体 :page_content 决定检索到什么,metadata 决定能否精准检索、能否溯源。
  3. 不同 Loader 解析结果不同 :TextLoader 只读文本,Unstructured 系列读出结构(category / parent_id)。
  4. JSON 用 jq_schema 挑字段:控制索引体积、保证语义连贯。
  5. 网页五种 Loader 各有侧重:单页用 WebBase,复杂用 Unstructured,大批量用 Recursive/Sitemap/Firecrawl。
  6. Markdown 必须用 mode="elements" :保留 category_depth(标题层级)和 parent_id(父子关系)。
  7. 父子元素链接是 L3→L4 的桥梁:解析出的结构,正是 L4 分块和 L7 检索的输入。
  8. 企业落地要自研关系建模:通用 Loader 给不了法律场景的效力层级、援引关系,需在 L4 自研。

7.2 下篇预告

本文聚焦通用文本与结构化文本 (txt / JSON / 网页 / Markdown)。下一篇将进入图文与 PDF 解析,覆盖:

  • 图片中的文字识别(UnstructuredImageLoader、OCR)
  • PPT 中的文字提取
  • 多模态大模型解析图文(这是 L3 里最前沿、也最值得关注的技术)
  • PDF 九种工具对比(PyPDF / Unstructured / Marker / LlamaParser / PDFPlumber / PyMuPDF...)
  • 三大类解析方法(规则 / 深度学习 / 多模态)

以及表格与数据库导入(CSV 自动切分、source 元数据定制、LlamaHub DatabaseReader 连库)。


本文是个人在七层 RAG 框架落地过程中的实践记录与选型笔记,欢迎交流指正。

八、延伸阅读:2026 年生态的四个重要增量

本节是我在项目之外的独立选型调研。经典方案胜在「稳」,但到了 2026 年,数据导入这个环节已经发生了四件大事。如果你正在做企业级 RAG,这四件事值得你重新审视选型。

8.1 增量一:LangChain 的 Loader 生态已经「拆包」了

我们早期代码大量用了 from langchain_community.document_loaders import ...。但在 LangChain 1.0 之后,这套生态发生了结构性变化:

  • langchain-community 的 Loader 大量被拆分到独立包 。比如 Unstructured 相关的 Loader,现在有独立的 langchain-unstructured 包,需要单独安装:pip install -U langchain-unstructured,并配置对应的环境变量(如 UNSTRUCTURED_API_KEY)。
  • langchain-core 成为公共底座 :Document、BaseLoader 这些核心抽象从 langchain_community 抽到了 langchain_core,所以现在导入 Document 应该用 from langchain_core.documents import Document。
  • 新增了官方集成包 :langchain-docling、langchain-firecrawl 等,把新一代解析工具通过 DoclingLoader、FirecrawlLoader 等官方 Loader 接入。

对企业落地的启发 :写代码时不要再用「一把梭」的 langchain_community 大杂烩导入。按需装独立包,能显著减小依赖体积、避免版本冲突,也更符合 LangChain 1.0 的「模块化」设计哲学。

8.2 增量二:网页解析从「抓取」进化到「LLM 友好的转换」

我在项目里试过五种网页 Loader(WebBase / Unstructured / RecursiveURL / Sitemap / Firecrawl)。但 2026 年,网页解析的竞争焦点已经从「能不能抓」变成了「能不能转成 LLM 友好的干净 Markdown」。这里出现了几个以往资料少有提及的新选择:

工具 定位 核心优势 局限
Jina Reader (r.jina.ai) 网页→Markdown 转换 API 零配置、免注册、单 URL 快速读取,返回干净 Markdown 无爬虫端点,大批量需自建抓取逻辑
Crawl4AI 开源爬虫框架 开源、可本地部署、LLM 友好输出、支持 JS 渲染 需自建基础设施
Firecrawl 抓取 + 转换一体化 API 有爬虫端点、支持大规模站点、过滤控制强 托管版收费,自部署有成本
Apify / Spider 结构化数据抓取平台 适合抓结构化数据(非纯文本) 面向数据抓取,非纯 RAG 场景

一个关键的选型判断(来自实测对比):

  • 如果你要快速读单个 URL 、零配置跑起来 → Jina Reader 最省事。
  • 如果你要批量抓取整个站点 、且对内容质量要求高(需要过滤导航/广告/脚本)→ Firecrawl 更强。
  • 如果你要开源、可本地部署、完全掌控 → Crawl4AI。

对企业落地的启发 :WebBaseLoader 会残留 JS 代码(前面 3.2.1 已经演示过),这在 2026 年已经不是最优解。真正生产级的做法是:用 Firecrawl / Jina Reader / Crawl4AI 先把网页转成干净的 Markdown ,再喂给 UnstructuredMarkdownLoader 做结构解析。「网页→Markdown→结构化解析」这个两段式管道,比直接抓 HTML 更干净。

8.3 增量三:文档解析的「三巨头」格局------Docling / MinerU / Marker

这是 2026 年数据导入领域最大的变化。早期 PDF 解析方案还停留在「PyPDF / Unstructured / LlamaParser」的经典组合,但新一代的开源解析引擎已经崛起,形成了「三巨头」格局。

引擎 定位 核心优势 局限
Docling 开源项目,MIT 协议 输入覆盖最广(PDF/Word/PPT/HTML/EPUB/邮件),纯 CPU 友好,文档模型专为 RAG 设计,有活跃的 LangChain 集成 复杂公式弱于 MinerU,未上公开榜单、精度难客观引用
MinerU 开源项目 中文、日文、韩文复杂版式最强,公式转 LaTeX、合并单元格表格、高精度 OCR,pipeline(CPU)+ VLM(GPU)双路线 相对重,GPU 高精度路线成本高
Marker 开源项目(基于 VLM) 2026 年把布局/OCR/公式都路由到单一 VLM(轻量模型),端到端简洁 依赖 GPU,中文/复杂版式不如 MinerU

一个关键的架构趋势 (非常重要):2026 年,「布局识别 + OCR + 公式识别」这三层,正在被一个单一的视觉语言模型(VLM)取代 。Marker 用一个轻量 VLM 搞定,MinerU 也推出了 VLM 后端,Docling 增加了 --pipeline vlm 选项。这意味着:

  • 过去:你要拼装 DocLayout-YOLO(布局)+ PaddleOCR(文字)+ UniMERNet(公式)三套模型。
  • 现在:一个 VLM 就能端到端输出结构化的 Markdown。

对企业落地的启发:

  • 如果你的文档以中文为主 (法律、政务、科研、中文企业文档)→ 我倾向用 MinerU,它在中文复杂版式上的表现更稳。
  • 如果你的文档类型杂、要 CPU 上跑、追求开源宽松协议 → Docling 更合适。
  • 如果你有 GPU 资源、追求端到端简洁 → Marker 或 MinerU 的 VLM 路线。

8.4 增量四:多模态解析,从「OCR」进化到「视觉理解」

早期资料里只简单提到「用大模型解析图文」,但 2026 年这一块已经成熟到可以直接上生产 。多模态大模型(GPT-4o、Qwen-VL、Claude、Gemini)不仅能 OCR,还能理解图表、表格、流程图、公式,直接输出结构化内容。

多模态解析的三种做法对比:

做法 原理 适用场景 成本
传统 OCR(Tesseract / PaddleOCR) 纯文字识别,不理解语义 纯文字图片、扫描件 低
布局 + OCR 组合(Docling / MinerU pipeline) 先布局识别,再分区 OCR 复杂版式、表格、公式 中
多模态 VLM 直接解析(GPT-4o / Qwen-VL) 视觉语言模型端到端理解 图表、流程图、跨页表格、需要语义理解的复杂文档 高

一个实用的工程建议 :多模态解析不是「非此即彼」的。真正生产级的做法是分层路由:

  1. 先用轻量规则判断:这张图/这一页是纯文字?还是含表格/图表/公式?
  2. 纯文字 → 走传统 OCR(便宜、快)。
  3. 含表格/图表/公式 → 走多模态 VLM(贵,但准确)。
  4. 关键业务文档(法律条文、财务报表)→ 强制走多模态 VLM,确保图表内容被正确解析。

对企业落地的启发 :多模态解析的成本是传统 OCR 的几十倍,但图表、公式、表格的解析质量 是纯 OCR 给不了的。如果你做的是法律/金融/科研这种「图表和公式是核心价值载体」的场景,该花的钱要花,但要通过「分层路由」把成本控制在刀刃上。


九、结语:L3 是「地基」,选型决定上限

回到开头的命题:检索质量的上限,在数据进入索引之前就被决定了。 这句话在 2026 年依然成立,而且比以往更深刻。

因为现在的工具链已经足够丰富------从经典的 TextLoader / JSONLoader / Unstructured,到新一代的 Docling / MinerU / Marker、Firecrawl / Jina / Crawl4AI、多模态 VLM------问题已经不是「能不能解析」,而是「你选对了没有」。

最后,把本文的核心判断浓缩成一句话,送给你做选型决策:

选 Loader 的本质,是选「你愿意在导入阶段保留多少结构」。保留的结构越多,后续分块和检索的精度上限就越高;但代价是解析成本、复杂度和对特定工具的依赖。

在 L3 层,这个「结构保留 vs 成本」的权衡,就是整个 RAG 系统最值得花时间打磨的地方。

相关推荐
Navicat中国1 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI1 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区2 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris3 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy4 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V4 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI4 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技4 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
海宇数据4 小时前
零信任架构实战:基于海宇网上踪迹欺诈行为识别构建自动化信贷资信评估网关
运维·人工智能·架构·自动化
冬奇Lab4 小时前
开源项目第242期:HyperFrames —— 写 HTML 就能渲染视频,专为 AI Agent 设计的确定性视频框架
人工智能·开源·音视频开发