大约 80% 的企业信息是非结构化的,并分散在演示文稿、文档、电子邮件和媒体文件中。图 3-1 展示了一些常见数据类型。
图 3-1:公司中常见的数据分布
本章展示如何将常见数据源转换为可以 embedding 和 retrieval 的文本。大多数 RAG retrievers 使用 text embeddings,因此一个实际的第一步,是把不同格式转换成一致的文本表示。
图 3-2 总结了 RAG 系统的主要组件,同时展示 indexing pipeline,也就是加载和处理文档,以及 runtime retrieval flow,也就是检索相关 chunks 并生成答案。
图 3-2:RAG 系统的组件
本章还解释了不同文档类型的加载过程。
WARNING
本书会从零构建核心组件,以阐明底层概念。在生产环境中,LangChain 或 LlamaIndex 等 orchestration frameworks 可以加速开发,但它们也会引入额外活动部件,例如频繁 breaking changes、快速演进的 APIs 和额外 abstractions。
如果使用这些框架,请 pin dependency versions,跟随它们的 upgrade guides,并将 framework-specific code 隔离在小型 adapters 后面。为了获得最稳定的基础,应优先依赖成熟的 general-purpose libraries,例如 pandas、NumPy、SQLAlchemy、Pydantic 和 Requests,并让 RAG logic 保持可测试,避免与框架强耦合。
你可以在本书 GitHub repository 中找到本章所有代码示例。
3.1 在 Python 中加载 Word 文件
Problem
你想将 Microsoft Word 文件加载到 RAG 系统中。
Solution
当你不需要区分元素类型时,可以使用 python-docx 做简单文本抽取。当你需要保留文档结构,例如 headings、paragraphs、lists、images,以便对元素进行有针对性的处理时,可以使用 Unstructured。
图 3-3 展示了使用 Unstructured 加载文档时的基本流程。它会将文档拆解成结构化元素,例如 text paragraph、headline、listing 或 image。
图 3-3:通过 python-docx 或 Unstructured 加载 Word 文件
下面演示两种方法。第一种使用 python-docx;第二种使用 Unstructured。两个示例都使用 pandas 做进一步数据处理。安装所需库:
pip install python-docx unstructured pandas
现在,我们用 python-docx 从示例 Word 文件中加载文本,并将所有段落拼接成一个字符串:
import os
from docx import Document
file_path = (
"../datasets/word_files/"
"2023_Jan_7_Feature_Engineering_Techniques.docx"
)
doc = Document(file_path)
text = []
for paragraph in doc.paragraphs:
text.append(paragraph.text)
full_text = "\n".join(text)
图 3-4 展示了输出结果,其中换行被保留为 \n 字符。
图 3-4:以字符串形式加载 Word 文件后的输出
这种方法适合连续叙事文本,但会丢失结构信息。要保留元素类型,例如 titles、narrative text、lists,需要在加载时进行拆分并打标签。
现在来看另一种方法:使用 Unstructured library 中的 partition_docx 函数加载 Word 文件,将每个元素连同 metadata,例如 filepath、category、text content、last modified,存入字典,再转换为 pandas DataFrame:
from unstructured.partition.docx import partition_docx
import os
import pandas as pd
elements = partition_docx(filename=file_path)
list_of_elements = []
for element in elements:
element_dict = {
"element_id": element.id,
"file_path": file_path,
"category": element.category,
# e.g., "Title", "NarrativeText", "ListItem"
"text": element.text,
"last_modified": element.metadata.last_modified,
}
list_of_elements.append(element_dict)
elements_df = pd.DataFrame(list_of_elements)
图 3-5 展示了每个元素及其 category 和 metadata。
图 3-5:带元素类别和 metadata 的结构化 Word 文件输出
Discussion
结构化加载支持有针对性的处理策略。Titles 可以被赋予更高 retrieval weights,或单独索引用于 hierarchical search。List items 可以被标记,用于 Q&A pair extraction。last_modified 等 metadata fields 支持 retrieval 阶段的时间过滤,避免过时信息出现在结果中。
当文档包含清晰不同 sections,例如书籍章节或政策领域时,hierarchical retrieval 可以降低噪声。系统先通过 title matching 找到相关 sections,然后只在这些 sections 内搜索。对于用户 queries 能映射到特定文档片段的场景,这种两阶段方法优于 flat text search。
如果文档很短或高度同质,不要使用这种方法。只有当不同元素需要不同处理方式,或 retrieval 能从结构化过滤中受益时,element-level processing 的开销才值得。
NOTE
大多数加载库,例如 Unstructured 或 Docling,尤其擅长加载 PDFs。当处理多种文件格式时,可以先全部转换为 PDF。Recipe 3.10 会覆盖 PDF 加载和处理方法。
See Also
python-docx Quickstart guide 覆盖了如何用程序创建和修改 Word documents。
Unstructured partitioning documentation 解释了 element types、metadata extraction 和 supported file formats。
3.2 加载 PDF 文件
Problem
你想将 Portable Document Format(PDF)文件加载到 RAG 系统中。
Solution
PyPDF2 library 是一个开源库,用于拆分、合并、裁剪和转换 PDF pages。它使用 Pillow 提取 images。
图 3-6 展示了 pipeline:加载文件、遍历 pages、提取 text,并将其存储为 metadata。
图 3-6:加载 PDF 文件并存储文本和 metadata
首先,你使用 PyPDF2.PdfReader 加载 PDF,并从每一页提取文本和 metadata。这个示例使用一本书代码 repository 中的示例 PDF 文档,内容是关于 feature engineering techniques:
import PyPDF2
import os
import pandas as pd
file_path = (
"../datasets/pdf_files/"
"2023_Jan_7_Feature_Engineering_Techniques.pdf"
)
with open(file_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
list_of_pages = []
page_counter = 1
for page in reader.pages:
page_dict = {
"file_name": reader.metadata.get("/Title"),
"producer": reader.metadata.get("/Producer"),
"page_number": page_counter,
"text": page.extract_text(),
"images": page.images,
}
list_of_pages.append(page_dict)
page_counter += 1
pages_df = pd.DataFrame(list_of_pages)
图 3-7 展示了生成 DataFrame 的打印结果。
图 3-7:以 DataFrame 形式加载 PDF 文件后的输出
Discussion
PyPDF2 会从数字生成的 PDF 中提取文本,这类 PDF 包含作为可选字符存在的文本。对于 scanned PDFs 或 image-based documents,它会失败,因为这些文档中的文字是像素,而不是字符。对于这些情况,应使用 optical character recognition(OCR)引擎,见 Recipe 3.6,或 multimodal models,见 Recipe 3.10。
Page-level metadata 支持 filtered retrieval。你可以在运行 semantic search 前,按日期、作者或 section 排除 pages,从而减少计算并提升相关性。Metadata 也提供 source attribution,让用户可以通过导航到原始文档中的具体页面来验证信息。
当你的 PDFs 是数字生成的,例如从 Word、LaTeX 或浏览器导出的 PDF 时,使用 PyPDF2。它简单且快速,非常适合处理大型文档集合,因为这类场景中 OCR 开销可能过高。
See Also
PyPDF2 text extraction guide 展示了从数字生成 PDFs 中提取文本的方法。
PyPDF2 image extraction guide 覆盖了如何提取嵌入 images 及其 metadata。
Unstructured partitioning documentation 解释了如何将 PDFs 拆分为带 metadata 的结构化元素。
3.3 从 Excel 和 CSV 文件加载并处理表格数据
Problem
你想将 Microsoft Excel 或 comma-separated values(CSV)文件中的表格数据加载到 RAG 系统中。
Solution
本 recipe 演示了在 RAG 系统中处理表格数据的三种方法:将表格行转换为文本、将表格嵌入 prompts,以及使用 text-to-SQL 方法。第三种方法的更完整实现见第 11 章。
Option 1:将表格行转换为文本
这种方法将每一行表格转换为自然语言描述。列名变成上下文标签,数值则被整合成可读文本。图 3-8 展示了这个概念。
图 3-8:通过将表格行转换为文本来加载表格数据
这个示例使用 Census Income 数据集,这是一个常用于二分类任务的数据集。
下载该数据集,并保存为包含 15 列、48000 行的 Excel 文件。你会使用 openpyxl library 打开和加载 Excel 文件,并使用 pandas 处理数据;按如下方式安装两者:
pip install openpyxl pandas
接下来,定义一个新的函数 create_text_description,并将其应用到 DataFrame 的每一行,以创建一个新列 text_description,其中包含解释其他列值的文本片段:
import os
import pandas as pd
file_path = "../datasets/csv_files/census-income.xlsx"
df_excel = pd.read_excel(io=file_path)
def create_text_description_of_row(row):
row["text_description"] = (
f"""The candidate {row['age']} years old is working in the
{row['workclass']} sector. The candidate was born in
{row['native-country']}, is {row['marital-status']}
and has a {row['relationship']} relationship.
The candidate has a {row['education']} degree and is
working as a {row['occupation']}. The income of the
candidate is {row['income']}."""
)
return row
# Apply the function create_text_description_of_row
# to each row of the DataFrame
df_extended = df_excel.apply(create_text_description_of_row, axis=1)
图 3-9 展示了新创建的 text_description 列内容。每一行都是一个文本片段,用来描述其他所有列中的值。
图 3-9:从表格数据行生成的文本片段
这些文本片段可以像其他文本一样处理:为它们生成 embeddings,并存入 vector database。这种方法适合 lookup queries,但要记住,aggregate queries,例如百分比、平均值、计数,需要使用 option 3,也就是 text-to-SQL。
Option 2:在 prompt 中嵌入表格
另一种替代方法是,不把行转换成单独文本片段,而是将整个表格直接嵌入 LLM prompt。这最适合小型表格,其完整上下文可以放入模型的 context window。
NOTE
LLMs 并不是为数值计算或复杂数据分析而设计的。不过,现代 LLMs 通常能够识别简单模式、比较行列之间的值,并在有用层级上总结趋势。由于模型能看到完整表格,它可以识别那些只检索少数行时会丢失的关系。
图 3-10 展示了一个示例 prompt template。表格使用 Markdown 语法插入 prompt。常见方法是在 Markdown 格式中包含整个表格。
图 3-10:通过将整个表格嵌入 prompt 来加载表格数据
请记住,这种方法受模型 context window 限制,对于超过几百行的表格成本会变高。对于更大的表格或复杂分析查询,option 3 提供了更可扩展的方案。
Option 3:使用 text-to-SQL 处理复杂查询
对于复杂分析查询或大型数据集,text-to-SQL 是最强大的选择。表格数据会被上传到 SQL database。当用户提问时,LLM 会分析表 schema 和用户问题,生成合适的 SQL query。随后,LLM 会解释查询结果并生成自然语言答案。
图 3-11 展示了你的 RAG app 在运行时的这一流程。
图 3-11:通过上传到 SQL database,并使用 text-to-SQL 方法回答关于数据的问题,来加载表格数据
更完整的 text-to-SQL 实现,请看 Recipe 3.4 的 database connectivity,以及第 11 章的完整端到端 text-to-SQL RAG application。
Discussion
RAG 中处理表格数据有三种根本不同的方法。每种方法适合不同 query types 和 dataset sizes。表 3-1 总结了取舍。
表 3-1:为 RAG 中的表格数据选择合适方法
| Approach | Best for | Limitations |
|---|---|---|
| Option 1:Row-to-text | 中型数据集上的简单 lookup queries,几百到几千行。例如:“Sarah 的职位是什么?” | 无法处理 aggregate queries、百分比计算或跨行比较。 |
| Option 2:Embedded table in prompt | 小型表格,少于 100 行,并且需要完整上下文分析。例如:“这个表中谁的薪水最高?” | 受模型 context window 限制。大表成本高。大数据复杂查询表现差。 |
| Option 3:Text-to-SQL | 需要 aggregations、filtering 或 complex analysis 的大型数据集。例如:“工程师中有多少比例收入超过 10 万美元?” | 需要数据库设置和 SQL query generation。实现更复杂。 |
关键取舍是 retrieval scope 与 computational cost。Row-to-text 通过 semantic search 检索特定 records,但无法回答 aggregate questions,因为每行是单独索引的。Prompt embedding 提供完整表格上下文用于分析,但超过几百行后成本会变得不可承受。Text-to-SQL 可以处理任意大的数据集和复杂分析,但需要 SQL infrastructure 和 query generation logic。
生产系统通常会根据表大小和 query patterns 组合不同方法。小型 reference tables,例如产品目录、员工名册,可以直接嵌入 prompts。中型 lookup datasets,例如客户档案、交易日志,可以用 row-to-text。大型 analytical datasets,例如销售历史、传感器读数,则需要 text-to-SQL。
TIP
这个 recipe 中的实践示例把数据集的每一行转换为文本片段。你也可以把相同方法用于 classification tasks。更多细节可以参考 Stefan Hegselmann 等人的论文 “TabLLM: Few-Shot Classification of Tabular Data with Large Language Models”。
See Also
LlamaIndex Excel loader 提供了内置 Excel 和 CSV 文件解析能力。
LangChain Excel loader 提供了用于表格数据的 document loader integration。
3.4 从 PostgreSQL 数据库加载结构化数据
Problem
你想将 Python workflows 连接到 PostgreSQL database。
Solution
安装 PostgreSQL 和 pgAdmin。可以从 postgresql.org/download/ 下载安装程序。
NOTE
本地安装适合快速实验。生产环境应使用 managed PostgreSQL services,例如 Amazon Aurora,以获得 backups、patching、monitoring 和 high availability。对于较小项目,Amazon Lightsail 提供固定月费。
使用 pgAdmin 检查 schemas、浏览 tables 并运行 queries。如果需要处理多个 database engines,可以考虑 DBeaver 或 DataGrip 这样的 multidatabase clients,它们支持 PostgreSQL、MySQL、SQLite、Oracle、IBM Db2 和 Microsoft SQL Server。
通过 SQLAlchemy 从 Python 连接 PostgreSQL,底层使用 psycopg2-binary adapter。安装所需 libraries:
pip install SQLAlchemy psycopg2-binary pandas python-dotenv
启动 pgAdmin,创建本地 server,并从 w3schools.com 填充 sample data。
接下来,创建用于在 PostgreSQL 中执行 SQL commands 的 engine:
import os
import pandas as pd
from sqlalchemy import create_engine
connection_string = (
f"postgresql+psycopg2://{username}:{password}@{host}:{port}/{database}"
)
engine = create_engine(connection_string)
with engine.connect() as connection:
query = """SELECT * FROM categories ORDER BY category_id ASC """
result = pd.read_sql(query, connection)
print(result)
填充完成后,results_df 会返回一个 DataFrame,其中包含按 category_id 排序的 categories 表中所有 records。
Discussion
PostgreSQL 在 RAG 应用中承担双重角色。它既存储结构化应用数据,例如 user sessions、query logs、feedback,也可以通过 pgvector extension 充当 vector store。相比为关系数据和向量数据维护不同数据库,这种整合降低了基础设施复杂度。
当你的 queries 需要将 structured filters 与 vector searches join 起来时,可以使用 PostgreSQL。例如按 account tier 过滤 customer embeddings,将 product descriptions 与 inventory status join,或根据存储在关系表中的 user permissions 排除 documents。当你的 RAG app 已经使用 PostgreSQL 做 transactional operations 时,PostgreSQL 非常适合。
Pinecone、Qdrant 或 Weaviate 等专用 vector databases,对于不需要 relational joins 的纯 similarity search workloads,通常性能更好。它们提供更简单 APIs,并针对 vector operations 做了专门优化。当你不需要将 vector search 与复杂 SQL queries 结合,或者最大向量搜索吞吐至关重要时,可以选择它们。
See Also
SQLAlchemy Engine Configuration guide 覆盖 connection strings、pooling 和 dialect-specific options。
SQLAlchemy tutorial with PostgreSQL examples 提供了查询和数据库操作的实践代码。
3.5 通过 Speech-to-Text Models 加载音频文件
Problem
你想加载并预处理音频文件,以便将其加入 RAG 系统的 vector store。
Solution
使用 OpenAI Whisper 将音频转换为文本。Whisper 同时提供 open source software 和 hosted API。
在这个示例中,你使用 OpenAI SDK 将音频文件发送到 Whisper API endpoint:
import os
from openai import OpenAI
audio_file_path = "../datasets/audio_files/harvard.wav"
# Initialize the OpenAI client with your API key
client = OpenAI()
with open(audio_file_path, "rb") as audio_file:
transcription = client.audio.transcriptions.create(
model="whisper-1", file=audio_file
)
Discussion
Whisper 同时提供 open source software,也就是本地运行,以及 hosted API,也就是通过 OpenAI 使用。选择取决于数据敏感性和基础设施:
Whisper API
设置快,支持 80+ 种语言,无需管理基础设施。当数据隐私允许外部 API calls,且你希望最小化运维开销时使用。
Whisper locally
完全控制数据,无外部依赖。适合不能离开环境的敏感音频,或在大规模场景下 API 成本高于本地计算成本时使用。
GPT-4o transcribe API
比 Whisper API 准确率更高,标点和格式更好,处理技术术语能力更强。当转录质量值得更高每分钟成本时使用。
Whisper 很适合 batch transcription,但缺少专用应用所需的一些功能。当你需要以下功能时,可以考虑专门 speech-to-text providers:
- 实时字幕、语音助手或对话式 AI 所需的 real-time streaming,当亚秒级延迟很重要时
- Speaker diarization,用于识别谁在什么时候说话,这对会议总结和访谈分析至关重要
- Custom vocabularies,用于提升领域术语准确率,例如医疗操作、法律术语、公司名、技术 jargon
表 3-2 比较了主要服务。
表 3-2:流行 speech-to-text 替代服务
| Service | Key features | Link |
|---|---|---|
| Google Cloud Speech-to-Text | Advanced speech models,支持 automatic punctuation、speaker separation、custom vocabularies 和 multichannel audio | https://docs.cloud.google.com/speech-to-text/docs/overview |
| Amazon Transcribe | Fully managed speech-to-text service,支持 automatic punctuation、custom vocabularies、language detection、speaker separation,以及敏感信息 redaction | https://aws.amazon.com/transcribe/ |
| Microsoft Azure Speech | Speech-to-text、text-to-speech 和 translation,支持 custom models 和可选 on-device deployment | https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-to-text |
| Deepgram Speech-to-Text | Voice AI platform,支持 real-time 和 batch speech-to-text,高准确率、speaker separation 和 punctuation | https://deepgram.com/product/speech-to-text |
| AssemblyAI Speech-to-Text | 面向 recorded 和 live audio 的 speech-to-text,支持 speaker identification、automatic formatting 和 language detection | https://www.assemblyai.com |
See Also
Hugging Face Whisper-Large-V3 model 提供开源转录能力和代码示例。
OpenAI speech-to-text API documentation 覆盖 API 使用、model options 和 parameter tuning。
3.6 通过 Tesseract OCR 从图像和 PDF 中提取文本
Problem
你想使用可以部署在自有基础设施上的开源 OCR engines,从图像或类似图像的文档中提取文本。
Solution
本 recipe 使用 optical character recognition(OCR)engines 提取文本。OCR engines 是针对 image-to-text conversion 训练的传统机器学习模型。
该工作流适用于任何包含书面文本的图像。图 3-12 展示了端到端流程:
- 将文档转换为 PDF 格式。
- 将每个 PDF page 渲染为图像。
- 对每页图像运行 OCR,并将提取文本与 metadata 一起存储,包括 page number、image path、timestamps、PDF reference。
图 3-12:OCR engines 可以从图像中提取文本,例如照片、技术图纸或扫描合同
本 recipe 使用最流行的开源 OCR engine:Tesseract。你可以本地安装,也可以使用 managed OCR service。
你可以在 Tesseract documentation 中找到不同操作系统的安装指南。如果你使用 Windows,可以使用 University of Mannheim 提供的 Windows installer。
除此之外,你需要安装 Python packages:pdf2image、Python-tesseract 和 Pillow。注意,pdf2image library 依赖 Poppler 进行 PDF 处理。pdf2image docs 提供了不同操作系统上的 Poppler 安装说明。使用以下命令安装 packages:
pip install pdf2image pytesseract pillow
接下来,加载一个包含金融数据的示例 PowerPoint slide,并提取其中的书面文本:
import os
from pdf2image import convert_from_path
from PIL import Image
import pytesseract
image = Image.open(
fp="../datasets/images/example_finance_reporting_slide.png"
)
text = pytesseract.image_to_string(image)
如果你有 scanned PDF,可以加载它,将每个 page 转换成图像,然后使用 Tesseract 从每页提取文本:
import os
from pdf2image import convert_from_path
from PIL import Image
import pytesseract
file_path = (
"../datasets/pdf_files/"
"2023_Jan_7_Feature_Engineering_Techniques.pdf"
)
images = convert_from_path(pdf_path=file_path)
text = []
for i, image in enumerate(images):
page_text = pytesseract.image_to_string(image)
text.append(page_text)
变量 text 包含一个字符串列表,每个字符串代表 PDF 某一页提取出的文本。
TIP
为了实现可靠 source attribution,应在每个提取出的 text chunk 旁边存储可追溯 metadata。至少应捕获原始 PDF path、派生 image path、page number,以及任何 extraction timestamps 或 IDs。这让你之后可以将 retrieval results 链接回确切 document 和 page,用于 citations、debugging 和 reprocessing。
Discussion
Tesseract 等传统 OCR engines 可以本地运行,快速处理文档,并且没有 per-page 成本。它们适合干净的文本扫描,标准字体和简单布局,例如 contracts、forms、typed letters。对于包含 tables、diagrams、mixed fonts、handwriting 或低质量扫描的复杂文档,准确率会下降。
Multimodal models 可以通过理解 layout、解释 tables,并同时处理多样视觉元素来处理这些复杂情况。代价是成本。Multimodal API calls 通常比本地运行 Tesseract 的每页成本高 100 倍。规模化时,这种差异非常明显。
处理大量文档的生产系统通常使用 hybrid approach。简单、文本密集文档通过 OCR 处理;复杂文档,例如包含 tables、diagrams 或质量较差的文档,交给 multimodal models。Document classifier 或 layout analyzer 可以自动完成这种路由,在控制成本的同时最大化质量。
下面列出不同方法适用场景:
Tesseract 或类似 OCR
干净扫描、typed text、高数量,数千到数百万 pages、成本敏感应用,或数据不能离开基础设施的环境。
较新的 OCR engines,例如 EasyOCR、PaddleOCR
在保持本地部署优势的同时,提高 handwriting 和复杂 layout 上的准确率。
Multimodal models
包含 tables 和 diagrams 的 mixed content、手写 notes、低质量扫描,或 extraction errors 会造成高下游成本的关键 documents,例如 financial reports、legal contracts。
表 3-3 展示了 Tesseract 的开源替代方案,它们具备更强能力。
表 3-3:Tesseract 的开源替代方案
| Tool | Key features | Official website |
|---|---|---|
| EasyOCR | 支持 80+ 种语言、GPU acceleration、Python-based implementation | https://oreil.ly/Bu4Fo |
| PaddleOCR | 高准确率,支持 layout analysis、handwriting,production-ready | PaddleOCR official documentation |
| docTR | Deep learning based、modular architecture,同时支持 PyTorch 和 TensorFlow | https://oreil.ly/28BDV |
| TrOCR | Transformer-based architecture,擅长 handwritten text recognition | https://oreil.ly/Y2RF2 |
为了帮助你根据 use case 选择正确方法,表 3-4 根据 document characteristics、volume 和 budget constraints 解释了决策标准。
表 3-4:决策指南:OCR versus multimodal models
| Document type | Volume | Recommended approach |
|---|---|---|
| Simple text-only PDFs:plain documents、contracts、books、articles | < 1,000 docs / month | OCR(Tesseract):快速、免费、本地运行 |
| Simple text-only PDFs:plain documents、contracts、books、articles | > 10,000 docs / month | OCR(Tesseract 或 EasyOCR):规模化时具成本效益 |
| Mixed content:text + tables + images | < 500 docs / month | Multimodal models(GPT-5 mini、Claude Haiku、Gemini Flash):单次处理,结果稳健 |
| Mixed content:text + tables + images | > 5,000 docs / month | Hybrid approach:先分类文档,简单 pages 用 OCR,复杂 pages 用 multimodal |
| Complex layouts:technical diagrams、handwritten notes、mixed fonts | Any volume | Multimodal models(GPT-5.2、Claude Sonnet、Gemini Pro):复杂文档上准确率更高 |
| Sensitive data:cannot leave infrastructure | Any volume | OCR(open source:Tesseract、PaddleOCR、EasyOCR):完全控制,本地部署 |
TIP
对于受监管或敏感数据,应避免使用外部 APIs。在自己的基础设施上运行 ingestion,并锁定对 raw files 和 extracted text 的访问,包括 least privilege、auditing、encryption。
先从本地 OCR 开始,例如 Tesseract、EasyOCR、PaddleOCR。如果需要 layout-aware extraction,例如 tables / forms / reading order,可以使用本地托管的 vision 或 document model,例如 Llama 3 Vision 或 Qwen-VL。
See Also
Tesseract OCR documentation 覆盖 installation、language support 和用于提高 accuracy 的 parameter tuning。
Python-tesseract package documentation 提供 Python 集成示例和 API reference。
Google Cloud Vision OCR guide 覆盖使用 managed infrastructure 处理复杂 layouts 和 handwriting。
3.7 通过 Multimodal Models 从图像中提取文本
Problem
你想从图像中提取文本、表格和代码列表,同时不丢失原始格式。
Solution
将图像附加到 prompt,并指示模型提取内容。你可以指示模型以 Markdown 格式返回文本,并反映原始结构,包括 headings、bullet lists、tables 或 code blocks。
TIP
将重要 metadata 与源文档一起保存,例如 image path、timestamps 和任何 IDs。
图 3-13 展示了如何使用 multimodal models 从图像中提取文本。
图 3-13:通过 multimodal models 从图像中提取文本
首先,定义一个示例 prompt,并将其与一张 financial reporting slide 图像一起发送给 OpenAI 的一个 multimodal model:
import os
import base64
from openai import OpenAI
png_file_path = "../datasets/images/example_finance_reporting_slide.png"
# Initialize the OpenAI client
client = OpenAI()
with open(png_file_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
prompt = (
"Extract the text from the image attached. Make sure to only "
"extract only the text. If there is no text in the image, "
"please return with the sentence 'No text found in the image."
)
response = client.chat.completions.create(
model="gpt-5.2", # Define the model to use
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {
"url": (
f"data:image/jpeg;base64,"
f"{base64_image}"
),
},
},
],
}
],
max_tokens=500,
)
content = response.choices[0].message.content
变量 content 包含从图像中提取出的文本,并尽可能保留原始结构。
Discussion
Multimodal models 可以在提取文本时保留文档结构。它们可以输出 Markdown,以反映原始格式,包括 headings、bulleted lists、tables 和 code blocks。相比 raw text extraction,这种结构保留可以改善下游处理。
成本会随着文档数量线性增长。Efficient multimodal models,例如 GPT-5 mini、Claude Haiku 4.5、Gemini 2.5 Flash,在大多数 extraction tasks 上能平衡成本和质量。Flagship models,例如 GPT-5.2、Claude Sonnet 4.5、Gemini 3 Pro,在复杂文档上准确率更高,但 per-page 成本更高。
下面列出了每个 tier 的适用场景:
Efficient models
文本清晰、布局简单的直接文档,以及成本重要的高容量处理。
Flagship models
密集表格、技术图纸、混合内容类型,或 extraction errors 会产生显著后果的关键 documents,例如 financial statements、legal contracts、medical records。
Classical OCR
极高数量下的 plain-text documents,成本敏感性超过结构理解收益。
Multimodal models 可能会在低质量扫描中 hallucinate numbers 或跳过内容。可通过提供具体 extraction instructions,例如 “extract product name, price, and weight from each row”,或在关键文档上使用多个模型 cross-validation 来提高准确率。
See Also
OpenAI vision documentation 覆盖如何将 images 发送给 GPT models,并包含代码示例。
Anthropic vision documentation 解释 Claude 的 image analysis capabilities 和 parameters。
Gemini vision API documentation 提供 image examples 和 model selection guidance。
3.8 通过 Multimodal Models 为图像生成文本描述
Problem
你想通过生成详细文本描述,从图像中提取洞察。
Solution
图 3-14 展示了流程和可使用的 sample prompt。该 prompt 指示模型生成图像的详细描述。
你还可以用以下信息进一步丰富生成的 metadata:
- 原始文档的 filepath
- 图像所在 page
- 图像周围文本 sections 的链接
图 3-14:使用 multimodal models 为图像生成文本总结
本 recipe 使用 multimodal model GPT-5 mini 分析图像。你也可以使用 Anthropic 的 Claude Haiku 4.5、Google 的 Gemini 2.5 Flash,或来自 xAI、Mistral、DeepSeek 的其他 multimodal models。
加载一张越南首都河内的示例图像,并相应指示模型:
import os
import base64
from openai import OpenAI
image_path = "../datasets/images/vietnam.png"
# Initialize the OpenAI client
client = OpenAI()
with open(image_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
prompt = (
"You are an assistant for visually impaired users. "
"Describe the image in detail."
)
response = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {
"url": (
f"data:image/jpeg;base64,"
f"{base64_image}"
),
},
},
],
}
],
max_tokens=150,
)
content = response.choices[0].message.content
模型输出是图像的详细描述,可以与图像一起作为 metadata 存储,以便未来引用。
Discussion
Multimodal models 会根据图像内容调整输出。它们会描述 portraits 中的 emotions,解释 diagrams 中的 structural relationships,总结 charts 中的数据趋势,并从 documents 中提取文本。图 3-15 展示了处理混合视觉内容时,这种 content-aware flexibility 的表现,即无需提前预测图像类型。
图 3-15:从不同图像类型中提取文本
这个 recipe 先将图像转换为文本描述,再使用 text embedding models 生成 embeddings。另一种方法是使用 OpenAI 的 CLIP 等 multimodal embedding models,它们可以直接在同一向量空间中处理 images 和 text。CLIP 支持直接 image-to-image search 和 cross-modal retrieval,即文本 query 匹配 images,或反过来,而无需中间文本转换。实现细节见 Recipe 5.5。
See Also
OpenAI CLIP repository 提供了 Contrastive Language–Image Pre-training 的官方实现,用于 multimodal embeddings。
LangChain multimodal RAG tutorial 展示了如何结合 text 和 image embeddings,并提供代码示例。
LlamaIndex multimodal retrieval guide 提供了 indexing 和 retrieving multimodal content 的模式。
3.9 通过 Multimodal Models 为嵌入式表格生成文本摘要
Problem
你想加载包含 embedded tables 的文档,并通过将表格转换成文本摘要,为 RAG 系统进行预处理。
Solution
图 3-16 展示了准备表格的步骤:
- 从文档中识别并提取 tables。
- 将 table 与 instructions 组合,构建合适 prompt。
- 将 prompt 发送给 LLM,生成文本摘要。
摘要随后会与重要 metadata 一起存储。如果表格来自 PDF,metadata 可能包括该表格所在 page number,以及原始 PDF 的 filepath。
图 3-16:使用 multimodal models 为表格生成文本摘要
本示例使用 Unstructured library 将 PDF 拆分为 text、tables 和 images。解释表格时,这个 recipe 仍然使用 OpenAI models。安装这些 dependencies:
pip install unstructured openai pandas
加载 PDF,对其进行 partition,并将所有 tables 存入一个名为 tables 的新列表:
import os
from unstructured.partition.pdf import partition_pdf
pdf_file_path = "../datasets/pdf_files/adult_data_article.pdf"
tables = []
texts = []
# Partition the PDF file into its elements
raw_pdf_elements = partition_pdf(
filename=pdf_file_path,
strategy="hi_res",
)
for element in raw_pdf_elements:
if "unstructured.documents.elements.Table" in str(type(element)):
tables.append(str(element))
接下来,用合适的 LLM 总结 table content:
from openai import OpenAI
import pandas as pd
def summarize_tables(row):
summary_prompt = (
f"You are an assistant tasked with summarizing tables. "
f"Give a concise summary of the table. "
f"Table chunk: {row.table}"
)
# Initialize the OpenAI API client and generate table summary
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
response = client.chat.completions.create(
model="gpt-5-mini",
messages=[{"role": "user", "content": summary_prompt}],
temperature=0.7,
max_tokens=150,
)
row["table_summary"] = response.choices[0].message.content
return row
# Create a pandas dataframe from the tables
tables_df = pd.DataFrame(tables, columns=["table"])
# Add a column to the dataframe to store the summaries
tables_df = tables_df.apply(summarize_tables, axis=1)
结果是一个 DataFrame,包含 summaries 和所有 metadata fields。
Discussion
将 tables 转换为文本摘要,可以捕捉 raw table strings 缺失的语义含义。以季度收入表为例。Embedding 原始字符串 “Q1: 100, Q2: 120, Q3: 140” 只能捕捉数字,而无法捕捉这些数字代表 20% quarter-over-quarter growth 的洞察。
像 “Revenue increased by 20% each quarter, rising from 140M in Q3, indicating strong growth momentum” 这样的文本摘要,则捕捉到了 pattern、trend 和 business implication。当用户问 “哪些公司显示出持续收入增长?” 时,summary 的 embedding 会在语义上匹配,而 raw table string 不会。
这种方法适合以下情况:
- Tables 包含 numeric data,且 patterns 和 trends 很重要,例如 financial results、performance metrics、time series。
- 用户提出 analytical questions,而不是查找特定 cells。
- Table insight 可以用两三句话表达。
避免在以下情况使用这种方法:
- Tables 主要是 lookup data,例如 employee directories、product specifications,这时 row-to-text conversion 就足够。
- Tables 是大型 reference datasets,更适合 text-to-SQL。
- 用户需要 exact cell values,而不是 summarized insights。
这里的取舍是 accuracy 与 interpretability。Raw table text 保留 exact values,但缺乏 context。Summaries 提供 context,但可能遗漏细节或引入 interpretation errors。
See Also
OpenAI PDF parsing guide 展示了如何使用 GPT-5.2 从 PDFs 中识别和提取 tables 与 diagrams。
Pinecone multimodal RAG example 演示了让模型分类 content types 并提取信息。
3.10 解析包含多模态内容的 PDFs
Problem
你想加载并预处理一个包含 text、listings、tables 和 images 的 PDF,以便将其摄入 RAG 系统。
Solution
图 3-17 展示了加载包含多模态内容 PDF 的端到端流程。它需要一个多步骤 pipeline,将 PDF partition 成不同 element types,并对每种类型分别处理:
- 加载 raw document,并将其 partition 为 text、images 和 tables。
- 使用 multimodal model,例如 OpenAI GPT-5.2、Anthropic Claude Sonnet 4.5 或 Google Gemini 2.5 Flash,为 images 和 tables 生成 text summaries。
- 为 text chunks、image summaries 和 table summaries 生成 text embeddings。
- 然后将 embeddings 加入 vector store,并存储 metadata,例如原始 filepath 和 page numbers。
图 3-17:加载并处理多模态 PDFs
这个示例使用 Unstructured library 将 PDF 拆分为 text、images 和 tables。该 library 依赖 Poppler utilities 进行 PDF 处理,尤其是提取 images 和读取 page-level information 等任务。
Poppler 的安装方式取决于操作系统:
Windows
从 Poppler for Windows 下载最新 release ZIP,将其解压到机器上的某个目录,并将该目录加入 PATH environment variable。pdf2image docs 中有一些有用链接。
Ubuntu / Debian
按如下方式安装 Poppler:
sudo apt-get install poppler-utils
为了处理扫描 PDFs 等 image-based documents,Unstructured 会使用 OCR engine。这个示例使用 Tesseract,因此也要确保安装了 Tesseract。
然后将 PDF partition 成 elements。每个 element 都有 category,例如 narrative text、titles、images 或 tables:
from unstructured.partition.pdf import partition_pdf
import os
# Set the OCR agent to tesseract
os.environ["OCR_AGENT"] = "tesseract"
pdf_file_path = "../datasets/pdf_files/adult_data_article.pdf"
image_output_dir = "../datasets/extracted_content_from_pdfs/images"
# Create output directory if it doesn't exist
os.makedirs(image_output_dir, exist_ok=True)
# Get elements by using the function extract_pdf_elements
raw_pdf_elements = partition_pdf(
filename=pdf_file_path,
extract_images_in_pdf=True,
extract_image_block_types=["Image", "Table"],
extract_image_block_to_payload=False,
extract_image_block_output_dir=image_output_dir,
)
# Categorize elements by type
tables = []
texts = []
titles = []
# Fill the just created lists with the elements
for element in raw_pdf_elements:
element_type = str(type(element))
if "unstructured.documents.elements.Table" in element_type:
tables.append(str(element))
elif "unstructured.documents.elements.NarrativeText" in element_type:
texts.append(str(element))
elif "unstructured.documents.elements.Title" in element_type:
titles.append(str(element))
结果是三个列表,每个列表对应一种 element type。随后,每种 element 会进一步处理。表 3-5 展示了每种 element type 的常见处理步骤。
表 3-5:PDF element types 的处理步骤
| Element type | Recommended processing steps |
|---|---|
| Text and titles | 拆分成适合 embedding models 的较小 text chunks。使用 recursive、semantic 和 agentic chunking 等技术。 |
| Images | 通过 OCR engines 或 multimodal models 提取文本。也可以通过 multimodal models 为 images 生成 text summaries。 |
| Tables | 加载 table 中的 plain text,或者更推荐,通过 multimodal models 提取关键洞察。 |
NOTE
这个 recipe 展示如何将 PDFs partition 成 elements。完整处理实现,包括生成 summaries、创建 embeddings 和存入 vector stores,请看 Recipe 3.7 的 image processing 和 Recipe 3.9 的 table processing。
Discussion
PDFs 经常包含混合内容:narrative text、embedded images、data tables 和 diagrams。Text embedding models 只能处理文本,因此如果不先转换,视觉元素对 semantic search 是不可见的。
解决方案是 modal conversion。Multimodal models 分析 images 和 tables,生成能够捕捉其内容和意义的文本描述。这些描述随后作为文本被 embedding,使视觉内容可以通过 semantic search 被发现。
这使全面 retrieval 成为可能。用户关于 “quarterly sales trends” 的 query,可以同时匹配讨论增长的叙事段落,以及展示季度数字的 embedded tables,因为两者都以可搜索 text embeddings 的形式存在。
在以下场景使用这种方法:
- 文档中的关键信息位于视觉格式中,例如 charts、diagrams、tables。
- 用户问题跨越文本和视觉内容。
- 你需要保持 document fidelity,而不只是抽取 plain text。
以下场景可以跳过这种复杂性:
- 文档是纯文本,或视觉元素只是装饰而非信息。
- 视觉内容已经在周围文本中被描述,例如表格摘要出现在表格上方段落中。
- 你可以使用专门 parsing 保留 table structure,用于 text-to-SQL,而不是转换为文本摘要。
See Also
OpenAI PDF parsing guide 演示了从复杂 PDFs 中提取 text、tables 和 images。
LangChain multimodal RAG tutorial 展示了端到端实现和代码示例。
3.11 通过 Speech-to-Text 和 Multimodal Models 加载视频
Problem
你想将视频加载到 RAG 系统中。
Solution
将视频拆分为它的 modalities 和 components,例如 audio track 和 individual frames,并分别处理每个组件。
图 3-18 展示了所需步骤:
- 将视频 partition 成 sections。可以使用固定长度 segments,例如 10 秒片段,也可以通过分析视觉内容和语音内容检测 logical breakpoints。
- 对每个 breakpoint,分配一个 label,将对应 frame 保存为 image,并创建该图像的 text summary。
- 提取两个 breakpoints 之间的 audio,保存为 MP3 文件,并转录它。
- 使用 text embedding models 为 transcribed audio sections 和 image summaries 创建 embeddings。
- 将 embeddings 存入 vector store。
图 3-18:通过将视频拆分成元素,并使用 speech-to-text 和 multimodal models 处理这些元素,将视频加载到 RAG 系统中
本 recipe 中的代码使用以下工具:
- MoviePy library,用于加载视频、提取 frames,并保存 breakpoints 之间的 audio clips
- Multimodal models,用于处理提取出的 images
- Speech-to-text models,用于转录 audio sections
- OpenAI 的一个 multimodal model,用于处理提取出的 images
- OpenAI 的一个 speech-to-text model,用于转录 audio section
使用以下命令安装所需 dependencies:
pip install moviepy pandas
先对视频进行 partition,并保存每个 segment 的 timestamps。你可以选择固定间隔 timestamps,也可以手动选择最佳 split points。
这个 recipe 中使用的示例视频是一个 beginner data science tutorial。讲者展示 slide deck,通常每页 slide 大约停留 30 秒,然后进入下一页。
图 3-19 展示了如何从加载的视频中提取 frames,并将它们保存为 images。
图 3-19:从加载的视频中提取 frames 并保存为 images
将视频切成 10 秒 segments,并将提取的 frames 保存为 images:
import os
import pandas as pd
from moviepy import VideoFileClip, TextClip, CompositeVideoClip
video_file_path = "../datasets/videos/learn-data-science-tutorial.mp4"
image_output_folder = "../datasets/videos/video_extracted_images"
# Create output folder if it doesn't exist
os.makedirs(image_output_folder, exist_ok=True)
clip = VideoFileClip(video_file_path)
# Create a list of timestamps from which to extract a frame
time_step = 10 # Time in seconds
timestamps = list(range(0, int(clip.duration) - time_step, time_step))
# For each timestamp extract a frame
for timestamp in timestamps:
frame_image_path = os.path.join(
image_output_folder, f"frame_{timestamp}.png"
)
clip.save_frame(frame_image_path, t=timestamp)
结果是一组 images,每张 image 代表视频中的一帧,见图 3-20。
图 3-20:从视频中提取出的 frames
接下来,决定如何处理保存的 images。最常见方法是使用 multimodal models 解释每张 image 的内容。
下一步会提取每两个 timestamps 之间的 audio,并保存为 MP3 文件。图 3-21 展示了如何将视频中的 audio sequences 保存为 MP3 文件。
图 3-21:将视频中的 audio sequences 保存为 MP3 文件
遍历保存的 timestamps,将两个 timestamps 之间的 audio 写入 MP3 文件,例如使用视频库中的 clip / subclip 函数:
# For each timestamp extract the audio sequence and save it to a .mp3 file
audio_output_folder = "../datasets/videos/video_extracted_audio"
# Create output folder if it doesn't exist
os.makedirs(audio_output_folder, exist_ok=True)
for timestamp in timestamps:
audio_clip = clip.subclip(timestamp, timestamp + time_step).audio
output_audio_path = os.path.join(
audio_output_folder, f"audio_{timestamp}.mp3"
)
audio_clip.write_audiofile(output_audio_path)
接下来,使用 speech-to-text model 转录每个 audio segment。细节见 Recipe 3.5。
之后,每个 video segment 至少会生成两个文本片段:一个来自 audio transcription,另一个来自该 segment 第一帧的派生内容,例如通过 image understanding 得到。
Discussion
视频 segmentation strategy 取决于内容结构和 retrieval 需求。根据你的视频类型选择策略:
Fixed intervals(10–30 seconds)
适合 lectures、presentations、tutorials,且节奏一致。实现简单。当内容逐渐变化时效果很好。
Scene detection(visual analysis)
适合 movies、documentaries、training videos,且有清晰视觉转场。可自动检测 cuts、fades 和主要视觉变化。
Transcript-based segmentation
适合 meetings、interviews、podcasts,因为 topic shifts 可能发生在同一场景中。使用文本分段技术分析 transcripts,寻找 topic boundaries。
Chapter markers(when available)
适合 educational content、webinars 且有预定义 sections 的内容。使用这些 markers 做分段是最准确方法,但实际中很少会遇到有这类 markers 的内容。
双模态方法,也就是 frames + audio,可以捕捉互补信息。Slide presentations 更受益于 frame analysis,因为 slides 包含关键信息。Podcasts 和 interviews 主要依赖 audio transcription。动作密集视频需要密集 frame sampling,以捕捉视觉事件。
Segment metadata 支持精确 source attribution。用户不仅能得到答案,还能得到确切 video timestamps。比如 “How do I configure authentication?” 这样的问题,会返回答案和相关视频时刻的 deep link,相比纯文本 response 显著提升用户体验。
See Also
MoviePy documentation 详细说明了该库对 video editing、frame extraction 和 audio processing 的支持。
OpenAI Whisper API guide 提供音频转录和 speech recognition 的官方文档。
LlamaIndex video processing guide 演示了用于 multimodal search 的 frame extraction、audio transcription 和 CLIP embeddings。