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 系统最值得花时间打磨的地方。

相关推荐
xhy_07071 小时前
Git 合并冲突怎么解决?用 AI 处理冲突的流程、Prompt 和 4 个易错点
人工智能·git·安全·prompt·ai编程·代码复审
马剑威(威哥爱编程)1 小时前
【AI全栈后端12-02】Spring Boot 跑通第一个 AI 对话接口:HR 政策问答机器人实战
java·人工智能·spring boot·机器人
果霸大叔1 小时前
RAG 数据导入与解析全攻略(二):图文与 PDF 解析——OCR、多模态大模型与九种 PDF 工具选型
人工智能
IT枫斗者枫哥1 小时前
AI返回合法JSON,字段就可信吗?给抽取结果补一道业务校验
java·人工智能·后端
旋生万物1 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
天天被压力1 小时前
【别再到处找免费股票数据API了:官方204个接口,32篇一次讲透 #06】Python实时行情总报错?五档盘口+逐笔一次跑通
java·人工智能·python
智能RPA1 小时前
农业与矿业行业智能体自动化平台对比评测(计量与巡检场景)
运维·人工智能·python·自动化·agent·rpa
easyeye1231 小时前
用开源的Toonflow和MiniMax H3一步步复刻万妖
人工智能
byte轻骑兵1 小时前
VCP核心缩写概览
人工智能·音视频·le audio·低功耗蓝牙音频