news 2026/10/10 19:04:01

文档→知识库一条龙:Docling 接上 LlamaIndex 与 LangChain,RAG 管线 10 分钟打通

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
文档→知识库一条龙:Docling 接上 LlamaIndex 与 LangChain,RAG 管线 10 分钟打通

文档→知识库一条龙:Docling 接上 LlamaIndex 与 LangChain,RAG 管线 10 分钟打通

【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling

做 RAG 最耗时、最劝退的环节,往往不是向量库调优,也不是提示词工程,而是"文档进得来、结构出得去"。PDF 里嵌套的表格被硬生生切成文本碎片、扫描件识别出来的文字乱序、Word 里的标题层级在 Markdown 导出后荡然无存——这些"脏数据"直接决定了检索召回的天花板。IBM 开源的 Docling 之所以能在短时间内收获数万 Star,正是因为它把"文档解析"这件脏活累活做成了可编程、可插拔、可入链的标准件:一次解析,同时产出带语义标签与空间坐标的结构化对象、无损 JSON 与规整 Markdown,再通过官方加载器平滑接入 LlamaIndex、LangChain 等框架。这篇文章从源码与官方示例出发,讲清楚三件事:如何用 Docling 产出"AI 就绪"的 Markdown/JSON、加载器在两个框架里的正确接法,以及一个 10 分钟可跑通的可检索知识库样例。

为什么解析层决定了 RAG 的下限

先看一个反直觉的事实:大部分 RAG 失败不是模型不行,而是"分块之前文档就已经坏了"。传统 PDF 文本抽取得到的是按阅读顺序打乱的 token 流,表格单元与行列关系丢失,标题与正文的从属关系被压平。而 Docling 的思路是先把所有格式统一"翻译"成一种结构化中间表示DoclingDocument,再基于它做导出与分块。

在仓库中,docling/datamodel/document.py 承载了ConversionResult与输入校验逻辑,docling/document_converter.py 则是总入口——它按格式注册了 30 余种 backend:PDF、DOCX、PPTX、XLSX、HTML、EPUB、Apple Pages/Numbers/Keynote、WAV/MP3、WebVTT、Box Notes、EML/MSG、图片、LaTeX、DocLang 乃至 USPTO/JATS/XBRL 等专用 XML schema(见 docling/datamodel/base_models.py 中的InputFormat枚举)。PDF 走StandardPdfPipeline(布局分析 + 表格结构识别 + 阅读顺序 + OCR),Office 类格式走SimplePipeline直接读原生 XML,图片与扫描件则叠上 OCR 与可选 VLM 理解。

这一"统一中间表示 + 按格式路由"的架构,可以用仓库内的处理流程图直观看到:

DoclingDocument本身是 pydantic 模型,把内容项(texts、tables、pictures、key_value_items)与内容结构(body正文树、furniture页眉页脚、groups容器)分门别类,所有条目通过 JSON 指针挂接父子关系,阅读顺序就是body树的遍历顺序(详见 docs/concepts/docling_document.md)。也就是说,解析结果天然自带标题层级、表格结构、图片位置与出处信息——这些正是后面分块与向量化最值钱的元数据。

一次解析,Markdown / JSON 双形态导出

Docling 的导出 API 非常薄,核心就一句话:result.document之后想导出什么形态由你选。仓库 docs/examples/minimal.py 给出了最简用法:

from docling.document_converter import DocumentConverter source = "https://arxiv.org/pdf/2408.09869" # 本地路径或 URL 均可 converter = DocumentConverter() result = converter.convert(source) print(result.document.export_to_markdown())

export_to_markdown()输出的不是裸文本,而是保留标题层级、表格栅格、列表结构与代码块语义的 Markdown——这直接决定了后续MarkdownHeaderTextSplitter能否按章节切出高质量分块。需要无损保留时则走export_to_dict()(可再序列化为 JSON/YAML),DoclingDocument中的坐标、出处、父子指针全部原样保留,适合需要精确定位或做文档原生 grounding 的场景。

批量场景下,docs/examples/batch_convert.py 演示了convert_all()配合save_as_json / save_as_html / save_as_markdown / save_as_doctags等辅助方法一次导出多形态产物,并显式处理SUCCESS / PARTIAL_SUCCESS / FAILURE三种转换状态;docs/examples/run_with_formats.py 则展示了如何用allowed_formats白名单与format_options按格式覆盖 pipeline/backend——比如 PDF 指定StandardPdfPipeline + PyPdfiumDocumentBackend,DOCX 指定SimplePipeline。对"文档→知识库"流水线而言,推荐的生产姿势是:先导出无损 JSON 留档,再导出 Markdown 供分块检索,一次转换两处消费。

分块:别忘了 Docling 原生 chunker

导出 Markdown 后接通用文本分割器只是玩法之一。Docling 的另一条路是直接基于DoclingDocument原生分块,由 docling/chunking/init.py 导出的HybridChunker、HierarchicalChunker实现。二者的差别见 docs/concepts/chunking.md:

  • HierarchicalChunker:按文档元素逐一成块,自动挂接标题、图注等上下文元数据;
  • HybridChunker:在层级分块之上叠加"token 感知"精修——先对超限块拆分,再把同标题、同图注下的过小相邻块合并(merge_peers默认开启),并支持repeat_table_header让跨块的表格每块都携带表头上下文。

用法的关键细节是:真正喂给 embedding 的是contextualize(chunk)的返回值,而非chunk.text裸文本。以 docs/examples/hybrid_chunking.ipynb 为例,一个关于 IBM 的条目在contextualize()后会补上所在章节标题("1910s–1950s")作为前缀:

from docling.chunking import HybridChunker chunker = HybridChunker() chunk_iter = chunker.chunk(dl_doc=doc) for chunk in chunk_iter: embed_text = chunker.contextualize(chunk) # 标题/上下文已注入

这种"文档结构感知 + tokenizer 对齐 embedding 模型"的分块方式,是 Docling 在 RAG 场景里对比朴素文本切割的核心竞争力。

接入 LlamaIndex:DoclingReader 与两种导出路线

LlamaIndex 侧由官方扩展llama-index-readers-docling与llama-index-node-parser-docling提供组件(说明见 docs/integrations/llamaindex.md),docs/examples/rag_llamaindex.ipynb 给出了两条路线:

路线一:Markdown 导出 + 通用解析器,最轻量:

from llama_index.core import StorageContext, VectorStoreIndex from llama_index.core.node_parser import MarkdownNodeParser from llama_index.readers.docling import DoclingReader from llama_index.vector_stores.milvus import MilvusVectorStore reader = DoclingReader() # 默认导出 Markdown node_parser = MarkdownNodeParser() # 按 Markdown 标题切 node index = VectorStoreIndex.from_documents( documents=reader.load_data(SOURCE), transformations=[node_parser], storage_context=StorageContext.from_defaults(vector_store=vector_store), embed_model=EMBED_MODEL, )

路线二:JSON 无损导出 + DoclingNodeParser,检索命中时能拿到文档级 grounding:

from llama_index.node_parser.docling import DoclingNodeParser reader = DoclingReader(export_type=DoclingReader.ExportType.JSON) node_parser = DoclingNodeParser() index = VectorStoreIndex.from_documents( documents=reader.load_data(SOURCE), transformations=[node_parser], storage_context=StorageContext.from_defaults(vector_store=vector_store), embed_model=EMBED_MODEL, )

注意两条路线查出来的source_nodes元数据差异:路线一只有Header_2这类标题信息,路线二的doc_items里则带着page_no、bbox(边界框)与headings数组——这就是"文档原生 grounding":答案不只告诉你"在哪一页",还精确到页内坐标。若想把 DoclingReader 混入既有目录扫描管线,只需把它注册为SimpleDirectoryReader的file_extractor:

dir_reader = SimpleDirectoryReader( input_dir=tmp_dir_path, file_extractor={".pdf": reader}, # PDF 交给 Docling 处理 )

接入 LangChain:DoclingLoader 的两种导出模式

LangChain 侧对应官方扩展langchain-docling(见 docs/integrations/langchain.md),docs/examples/rag_langchain.ipynb 展示了DoclingLoader的两种ExportType:

  • ExportType.MARKDOWN:每个输入文档导出为一条独立的 LangChain Document,之后交给MarkdownHeaderTextSplitter自行切分;
  • ExportType.DOC_CHUNKS(默认):加载器内部直接调用 Docling 原生 chunker,把每条 chunk 作为一条 LangChain Document 输出,分块逻辑与 tokenizer 对齐。
from langchain_docling import DoclingLoader from langchain_docling.loader import ExportType from docling.chunking import HybridChunker from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer from transformers import AutoTokenizer tokenizer = HuggingFaceTokenizer( tokenizer=AutoTokenizer.from_pretrained(EMBED_MODEL_ID) # 与 embedding 模型同款分词器 ) loader = DoclingLoader( file_path=FILE_PATH, # 本地路径或 URL 列表 export_type=ExportType.DOC_CHUNKS, chunker=HybridChunker(tokenizer=tokenizer), ) docs = loader.load() # 每条即一个 chunk 的 LangChain Document

随后无论是MarkdownHeaderTextSplitter二次切分(MARKDOWN 模式),还是直接把docs当作splits(DOC_CHUNKS 模式),下游都是标准的 LangChain 套路。官方示例把Milvus.from_documents(...)与create_retrieval_chain(retriever, question_answer_chain)一接,端到端问答就通了。

10 分钟跑通:一个可检索知识库的完整链路

最后把两条官方路线浓缩成一个"文档→知识库"最小闭环。以下组合均来自仓库内可复现的官方示例(LLM 可换成任意本地或远程推理端点,向量库也可替换为其他 Milvus/FAISS 兼容实现):

# 1) 解析:一次转换,双形态留档 from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("https://arxiv.org/pdf/2408.09869") doc_json = result.document.export_to_dict() # 无损 JSON,供归档/调试 markdown = result.document.export_to_markdown() # 结构化 Markdown,供检索 # 2) 分块:原生 chunker 注入文档结构上下文 from docling.chunking import HybridChunker from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer from transformers import AutoTokenizer chunker = HybridChunker( tokenizer=HuggingFaceTokenizer( tokenizer=AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2") ) ) chunks = [chunker.contextualize(c) for c in chunker.chunk(dl_doc=result.document)] # 3) 入库:embedding + 向量存储(LangChain 侧等价代码见 rag_langchain.ipynb) from langchain_huggingface.embeddings import HuggingFaceEmbeddings from langchain_milvus import Milvus from langchain_core.documents import Document docs = [Document(page_content=t) for t in chunks] vectorstore = Milvus.from_documents( documents=docs, embedding=HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2"), collection_name="docling_demo", connection_args={"uri": "docling.db"}, # 本地轻量部署,无需单独起服务 drop_old=True, ) # 4) 检索问答 retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

跑完这套流程后,提问 "Which are the main AI models in Docling?",两条官方示例给出的答案都指向同一事实:Docling 随包发布布局分析模型(页面元素目标检测)与 TableFormer(表格结构识别)两个模型——且命中的检索源都带着headings(如 "3.2 AI models")与页码/坐标元数据。这就是结构化解析的价值:答案可溯源到章节与版面位置,而不是一段来源不明的文本碎片。

小结

从仓库源码与官方示例可以得出一个清晰的工程结论:Docling 并不只是"格式转换器",而是一套把文档解析、结构化表示、感知型分块与框架集成串起来的完整管线基座。接入 LlamaIndex 时按需选 Markdown 轻量路线或 JSON 无损路线;接入 LangChain 时用DoclingLoader的DOC_CHUNKS模式省去手工分块;想要更高召回质量,则务必使用contextualize()注入文档结构的 embedding 文本。把这四步固化下来,"文档→知识库"从接文件到可问答,确实可以在十分钟内打通——而后续要做的,就是在解析质量与分块策略上持续打磨了。

【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 19:03:11

Linux lpq命令深度解析:打印队列可观测性实战指南

1. 为什么今天还要学lpq?——一个被低估的系统级诊断工具很多人看到“Linux lpq 命令”第一反应是:“这玩意儿不是上个世纪的遗存吗?现在谁还用行式打印机?CUPS 图形界面点几下不就完事了?”——我第一次在某高校实验室…

作者头像 李华
网站建设 2026/10/10 19:03:04

麻雀搜索算法复现实战:从机制拆解到收敛曲线验证

前阵子把手头一篇麻雀搜索算法(Sparrow Search Algorithm,SSA)的论文从头到尾复现了一遍。说实话,如果只看标题我可能会划过去——2020年提出来的群体智能优化算法,已经不算最新了;但就是因为“不算最新”&…

作者头像 李华
网站建设 2026/10/10 19:02:04

AI Coding 时代 FDE 成长路径:90 天从需求到上线

一句话需求丢过来,三天后要看到能装到手机上的 App,这种场景在最近一年变得越来越常见。以前这意味着产品、设计、前端、后端、测试排期至少两周起步,现在借助 AI Coding 工具链,一个人从需求到上线的时间被压缩到了以小时计。Wor…

作者头像 李华
网站建设 2026/10/10 19:01:51

Recover-LoRA:4-bit 压缩掉的精度,Edge0 怎么找回来

Recover-LoRA:4-bit 压缩掉的精度,Edge0 怎么找回来 【免费下载链接】Edge0-35B-A3B-preview 项目地址: https://ai.gitcode.com/hf_mirrors/Edge0/Edge0-35B-A3B-preview 把 35B 参数的 MoE 模型塞进 3 GiB 内存跑起来,靠的是两条路…

作者头像 李华
网站建设 2026/10/10 19:00:07

深入Spring底层:自动装配、Bean生命周期、循环依赖与事务代理全解析

写了好几年 Spring,日常 CRUD 里用 IOC 和 AOP 也算得心应手,但有一次面试被问到“EnableAutoConfiguration 加载的配置类到底是由谁扫描出来的”,我当时居然卡住了。后来啃了一段时间源码,又把 Bean 生命周期、循环依赖、事务代理…

作者头像 李华