DLAI-文档人工智能笔记-全-

DLAI 文档人工智能笔记(全)

001:1.课程介绍

概述

在本课程中,我们将学习如何构建文档处理流程,将复杂的文档转换为可供大语言模型(LLM)处理的Markdown文本,并从中提取信息用于分析。课程由吴恩达与Landing AI合作推出,旨在引导你从传统OCR技术开始,逐步深入到智能体化文档抽取(ADE)的现代方法。

课程内容简介

数据通常被锁定在PDF、网页文档、笔记本电脑或公司云存储中的各种文件里。本课程将指导你构建文档处理流程,以应对这一挑战。

首先,我们将探索传统的光学字符识别(OCR)技术。传统OCR仅能提取文本,我们将在此基础上学习检测文档结构、识别视觉组件(如具有复杂格式的表格、带标题的图表或带复选框的表单)的技术。

接着,我们将实现一个结合了布局检测与大语言模型推理能力的智能体工作流。

然后,你将学习使用由Landing AI设计的工具——智能体化文档抽取(ADE)。这个工具能为你自动化整个工作流程。

本课程的讲师是Landing AI的应用AI高级总监David Park和应用AI工程师Onreroprop。他们拥有丰富的经验,曾帮助许多开发者构建复杂的文档AI系统。

传统OCR与ADE的对比

上一节我们介绍了课程的整体目标,本节中我们来看看两种核心方法的不同。

传统OCR系统的工作原理是将文档分割成单个字符,然后使用监督学习对每个字符进行分类。这些系统可以准确地提取字符,但它们缺乏对文档各部分如何关联的理解,例如文档结构、表格、表单,甚至不同元素的阅读顺序。

Landing AI的ADE采用了不同的方法。它将文档的每一页视为一张图像,并将该图像分解为多个区块,然后使用智能体工作流分别从每个区块中提取信息。

ADE将文档视为视觉对象,其中意义编码在布局、结构和空间关系中。它使用定制的视觉模型直接解释复杂的表格、图形、图像、图表等元素,并将提取的每一段文本精确定位到页面上的具体位置。

此外,ADE使用一个智能体编排层,将复杂文档分解为更小的部分,以便在多个步骤中进行仔细检查。人类处理文档时并非一眼扫过,而是通过多次迭代,逐步检查文档的不同部分以提取信息。ADE的工作方式与此相同。

例如,给定一个复杂文档,ADE可能会先提取一个表格,然后进一步提取表格结构,识别出行、列、合并单元格等。

你将学到什么

以下是本课程的核心学习路径:

  1. 探索基于传统OCR的处理流程:了解它们能做什么,以及它们的局限性。
  2. 从零开始构建你自己的智能体文档工作流
  3. 学习使用ADE处理复杂文档:构建一个流程来解析混合文档并提取所需的关键值对。
  4. 学习将ADE提取的信息集成到检索增强生成(RAG)应用中
  5. 在AWS上实现基于云的版本:使用事件驱动架构,在新文档出现时自动触发ADE进行处理。

总结

本节课中,我们一起学习了文档AI课程的整体介绍。我们了解了数据提取的挑战、课程的目标,并对比了传统OCR与新一代智能体化文档抽取(ADE)方法的根本区别。ADE通过将文档视为视觉对象并采用多步骤的智能体工作流,能够更好地理解文档的结构和复杂元素。

在接下来的第一课中,我们将从审视传统的OCR模型开始我们的实践之旅。

002:文档处理基础 📄

在本节课中,我们将学习如何使用光学字符识别(OCR)来解析文档,以及如何将其结果集成到智能体工作流中。你将构建一个智能体,使用OCR从文档中解析文本,然后利用大语言模型从文本中提取信息。在此过程中,你将识别出手写、表格和扫描图像等使这些步骤极具挑战性的情况。

今天,我们将从基础开始。我们所涵盖的一切都是当今企业和开发者使用的生产级系统的基础,这些系统将在后续课程中介绍。

以下是今天的议程:

  • 处理:它是什么以及为何重要
  • 解析、提取和输出格式:JSON、Markdown及其用途
  • OCR:其工作原理、工作流程和局限性
  • 智能体AI与ReAct框架:在OCR之上添加“大脑”
  • 我们将回顾一些实际演示、失败模式以及仍然挑战真实系统的难题
  • 最后,我们将动手编写代码,构建你的第一个简单文档智能体

你会注意到,这是一个自底向上的旅程:从像素、文本到结构,再到推理。

现实世界的问题 🌍

这些系统旨在解决现代组织面临的问题:数字文档泛滥。发票、收据、合同和报告等文档存在于庞大的数字文件柜中,通常以PDF、PowerPoint、Word文档甚至图像的形式存在。它们是为人类眼睛设计的,而不是为机器设计的,这意味着信息难以搜索、分析,当然也难以自动化。

如果你的数据被困在非结构化文档中,就必须有人手动打开、阅读并将其重新输入到另一个系统中,这无法扩展。

因此,如果我们想在分析、自动化或AI中使用这些信息,就需要将非结构化文档转换为结构化的、机器可读的数据。

解决方案概览 🛠️

文档处理是将非结构化文档转换为结构化的、机器可读的数据,通常是JSON或Markdown。它不仅仅是抓取文本,解析必须理解文本片段实际的含义、它们之间的关系,以及如何将它们组织成可预测的结构。例如,解析发票时,你想要的不是一堆文本,而是提取供应商名称、发票日期、总金额和行项目。

提取信息的前提是文本已经是机器可读的。但如果你的文档是扫描件或照片,计算机看到的只是图像像素。因此,在提取之前,我们需要OCR将像素转换为文本。

输出格式 📝

解析和提取通常产生两种有用的格式。

第一种是Markdown或HTML,它们是为人类和我们的大语言模型设计的。这种格式保留了标题、表格和列表等结构,非常适合输入给大语言模型或展示给最终用户。

第二种是JSON,它是为机器和应用程序设计的。它是分层的,易于编程遍历,非常适合下游管道和应用程序。

记住一个经验法则:如果你考虑的是分析或数据库,JSON是一个很好的选择。如果你考虑的是构建RAG解决方案或聊天用户界面,Markdown或HTML可能是一个很好的选择。

但所有这些都假设我们已经有了文本。如果我们只有像素呢?

OCR:光学字符识别 👁️

OCR是一种将文本图像转换为机器可读文本的技术。它通常分两步工作。

第一步是图像清理,例如去歪斜、去噪或对比度调整。

第二步是文本识别,即模式匹配。例如,判断这个形状看起来像“8”还是“B”。

一旦识别出文本,它就会产生可编辑的文本作为数字输出,或生成可搜索的PDF。

OCR的局限性 ⚠️

我们也需要诚实地面对OCR不能做什么。OCR非常擅长阅读干净的文档,但它不理解结构、含义或关系。OCR之后,你通常得到的是一堵“文本墙”。如果你想找到总计、提取表格、识别标题和分类文档,你需要在OCR之上添加智能。

你可以把OCR看作是“眼睛”,但不是“大脑”。除了缺乏理解能力,它也可能直接失败。OCR会在可预测的情况下失效,例如:

  • 图像质量差:模糊的照片、阴影和噪点
  • 复杂的布局和倾斜:多列文本、嵌套表格
  • 非标准文本:手写、印章、风格化字体

这些失败会级联到解析和提取错误中。因此,OCR是必要的,但对于完整的文档理解来说还远远不够。

如果你曾尝试在昏暗的餐厅里对收据照片进行OCR,你可能同时经历了所有三种失败模式。

关键理念:解析不等于理解 🧠

所有这些引出了一个关键理念:解析不等于理解。OCR可以读取字符,但它不理解它们。它不知道什么是标题,什么是数值;哪个数字是总计;或者文本是属于表格还是脚注。

OCR为你提供了感知能力(从像素到字符),但没有认知层。为了将那堵“文本墙”转化为有意义的结构化数据,我们需要一个“大脑”,这就是智能体AI的用武之地。

智能体AI:添加缺失的认知层 🤖

智能体AI添加了缺失的认知层。智能体是一个可以感知其环境、推理目标并采取某些行动的自主系统。

具体到文档处理,智能体通过OCR(如果需要)读取文档,思考用户的要求,选择调用哪些工具,并迭代直到达到目标。如果说OCR是“眼睛”,那么智能体就是“大脑”。基于规则的管道在遇到边缘情况时可能会崩溃,而智能体可以通过推理来处理它们。

智能体系统的构成 🧩

那么,一个智能体系统在内部是什么样子的呢?智能体文档系统通常有三个部分:

  • 大脑:使用大语言模型,负责推理、规划和决策。
  • 眼睛:OCR,将视觉内容转换为文本。
  • :智能体可以使用的工具,如API、数据库查询、文件操作和函数调用。

当这三者连接在一起时,你可以说:“在这张发票上找到总金额。”然后智能体会决定运行OCR,检查文本,定位总计,并返回答案。你不需要对每个步骤或每个边缘情况进行硬编码。

这个“大脑、眼睛和手”的心智模型将在后续的实验和课程中反复出现。

ReAct框架:智能体如何思考 🔄

但是,智能体是如何一步一步思考的呢?ReAct(推理与行动)框架描述了智能体的思考过程。

首先是思考:我下一步需要做什么?

然后它会采取行动:选择并调用它需要的一组工具。

完成后,它会进行观察:检查结果。

然后重复:思考、行动、观察,再思考。

这个循环赋予了智能体自主性、适应性和纠正错误的能力。大多数现代智能体框架都建立在ReAct的某种变体之上。

当然,一个主要好处是它是可调试的——你实际上可以阅读智能体的想法和工具调用记录。

实验时间:动手构建 🧪

好了,理论讲得够多了。让我们来构建点东西。

现在是实验时间。我们将把幻灯片上涵盖的所有内容——解析、OCR和智能体推理——结合起来,共同构建一个简单的文档智能体。到实验结束时,你将拥有一个可以读取文档、使用OCR作为工具、提取结构化信息的智能体。

如果你是第一次尝试,不用担心,我们会一步一步来。让我们切换到笔记本环境。


在本节课中,我们一起学习了文档处理的基础知识。我们了解了将非结构化文档转换为结构化数据的需求,探讨了OCR作为“眼睛”将图像像素转换为文本的工作原理及其局限性。我们引入了智能体AI作为“大脑”,通过ReAct框架进行推理和行动,从而理解文档内容并提取信息。最后,我们为动手构建第一个简单的文档智能体做好了准备。

003:使用OCR进行文档处理 📄

在本节课中,我们将动手构建一个简单但功能强大的文档处理智能体。我们将看到OCR、规则集和基于大语言模型的推理如何协同工作,并理解传统方法的局限性以及智能体如何带来改进。


概述

我们将构建一个文档处理智能体。其目标不是构建一个完美的生产系统,而是理解OCR、规则和大语言模型如何结合。我们将导入必要的包,将OCR封装为智能体可调用的工具,并测试其在不同类型文档上的表现。


导入必要包

以下是构建系统所需的包。我们使用 PIL 加载图像,pytesseract 进行OCR,langchain 提供智能体框架,以及 OpenAI 模型。

from PIL import Image
import pytesseract
from langchain.agents import Tool, AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

将OCR封装为工具

上一节我们介绍了所需的工具,本节中我们来看看如何将OCR功能转化为智能体可以调用的工具。

以下函数接收图像路径,使用Tesseract提取文本,并返回结果。@tool 装饰器使其成为智能体可以识别和按名称调用的工具。

from langchain.tools import tool

@tool
def ocr_tool(image_path: str) -> str:
    """使用Tesseract OCR从图像中提取文本。"""
    image = Image.open(image_path)
    text = pytesseract.image_to_string(image)
    return text

在智能体循环中,大语言模型会决定“我需要先读取文档”,然后调用OCR工具,获取结果,并继续推理。


测试案例一:清晰的数字发票

现在,让我们在一个理想的OCR案例上进行测试:一张清晰、干净的数字发票。这是传统OCR表现最佳的场景。

以下是第一张测试图像。它具有完美的光照、清晰的字体、没有手写和阴影。

# 假设图像文件为 'clean_invoice.jpg'
ocr_result = ocr_tool('clean_invoice.jpg')
print(ocr_result)

OCR输出是原始文本,没有结构、没有含义、没有理解。这正是将图像通过Tesseract管道处理得到的结果。


传统规则方法的尝试

仅仅有原始文本还不够,我们需要从中提取特定信息,例如“税费”和“总计”。传统方法会使用正则表达式。

以下是用于匹配“税费”和“总计”的简单正则表达式设置。

import re

text = ocr_result
tax_pattern = r"tax\s*\$?(\d+\.?\d*)"
total_pattern = r"total\s*\$?(\d+\.?\d*)"

tax_match = re.search(tax_pattern, text, re.IGNORECASE)
total_match = re.search(total_pattern, text, re.IGNORECASE)

让我们看看会发生什么。它完全错过了税费行,并且抓取了错误的“总计”值(它首先抓取的是“小计”)。

这并非我们正则表达式代码的bug,而是规则与嘈杂OCR数据交互时的根本缺陷。正则表达式不等于理解。


引入大语言模型智能体

正则表达式在OCR文本上非常脆弱,它严重依赖确切的措辞和布局,且没有语义理解能力。这就是大语言模型和智能体的用武之地。

本节我们将构建我们的智能体,它包含三个关键组件:作为“大脑”的大语言模型(我们使用GPT-4)、我们之前定义的OCR工具,以及告诉智能体它拥有什么工具以及如何使用它们的提示词。

# 1. 定义大语言模型
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 2. 定义工具列表
tools = [ocr_tool]

# 3. 定义提示词模板
prompt = PromptTemplate.from_template(
    """你是一个文档处理助手。你可以使用OCR工具来读取图像中的文本。
    用户的任务是:{task}
    请逐步思考,并使用可用的工具。
    """
)

# 4. 创建智能体
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

这是ReAct框架的本质:思考、行动、观察、再次思考。


智能体处理清晰发票

当我们要求智能体提取税费和总计值时,神奇的事情发生了。它识别出需要先进行OCR,于是调用OCR工具,读取文本,并返回结构化的JSON。没有正则表达式,没有固定规则,也没有模板。

result = agent_executor.invoke({"input": "处理图像 'clean_invoice.jpg' 并提取税费和总计值。"})
print(result['output'])

正如你所见,智能体返回了结构化的JSON,并且正确提取了税费和总计值,而不是小计。借助大语言模型的理解能力,你无需创建任何规则或模板就能实现这一点。


测试案例二:复杂的学术表格

我们之前的例子运行良好,因为发票布局清晰、文本质量高。但现实世界的文档并非总是如此友好。让我们探索当输入变得更混乱时会发生什么。

这里有一张来自《Attention Is All You Need》论文的表格。表格对于OCR来说是出了名的困难,它们需要空间对齐和列重建,而Tesseract最初并非为此设计。

我们给智能体一个新任务:使用OCR工具从表格中提取“英语到德语翻译”所有方法的“训练成本”和“FLOPS”,并以模型名称及其训练成本的列表形式返回结果。

首先,让我们获取原始的OCR输出。

ocr_result_table = ocr_tool('research_table.jpg')
print("原始OCR输出(表格):\n", ocr_result_table)

OCR输出是混乱的:指数变成了撇号,小数点变成了感叹号,列发生了错位。

但智能体仍然尝试进行最佳解释。这正是大语言模型真正擅长的:即使输入质量下降,它们也试图理解意图。

result_table = agent_executor.invoke({
    "input": "处理图像'research_table.jpg',提取‘英语到德语翻译’所有方法的‘训练成本’和‘FLOPS’,以列表形式返回,包含模型名称和成本。"
})
print("智能体输出(表格):\n", result_table['output'])

即使OCR输出不完美,大语言模型在某些情况下仍能得出正确结论。例如,它理解到“Bytenet”的训练成本是空的。然而,它也可能从错误的列中抓取值,或者错误解释科学计数法。


测试案例三:手写文档

让我们看另一个例子,尝试将OCR推向其崩溃点:手写文档。

这是一个学生的“填空练习”文档,学生名为John Smith。我们可以看到学生提交的答案,例如“I am an honest person”,以及语法不正确的“day is dancing”。

我们要求智能体处理该文档,提取学生姓名以及所有10个问题的学生答案。

ocr_result_handwriting = ocr_tool('handwriting_exercise.jpg')
print("原始OCR输出(手写):\n", ocr_result_handwriting)

result_handwriting = agent_executor.invoke({
    "input": "处理图像'handwriting_exercise.jpg',提取学生姓名以及所有10个填空问题的学生答案,以JSON格式返回。"
})
print("智能体输出(手写):\n", result_handwriting['output'])

原始OCR输出未能捕获学生姓名,填空答案也完全不正确。例如,“am”被识别为“a u m”,数字被识别为字母。对于第九个问题,学生答案是“is”,但OCR可能误读,而大语言模型可能会过度纠正为“R”。


测试案例四:杂乱的收据

最后,让我们看一个收据的图片。收据通常非常杂乱:可能是低分辨率的热敏打印,文本可能错位,可能有阴影。

我们给智能体一个新任务:处理收据文档,并评估总计金额是否正确。

ocr_result_receipt = ocr_tool('messy_receipt.jpg')
print("原始OCR输出(收据):\n", ocr_result_receipt)

result_receipt = agent_executor.invoke({
    "input": "处理图像'messy_receipt.jpg',评估收据上的总计金额计算是否正确。"
})
print("智能体输出(收据):\n", result_receipt['output'])

比较OCR输出和实际收据,你会发现它产生了小的数值错误。例如,第一个食品项目的实际价值是$7.95,但OCR可能误解为$7.99。

智能体的推理过程是扎实的:它读取OCR工具提供的所有行项目,进行计算,并与实际总计进行比较。然而,由于OCR工具没有准确捕获数字,最终得出的结论可能是错误的。


总结

本节课中我们一起学习了以下核心要点:

  1. OCR擅长阅读,但不擅长理解:它在清晰的打印文本上表现非常好,能提供准确的文档解析。然而,它在处理复杂表格和手写体时会遇到困难。OCR可以读取字符,但并不真正理解结构或含义。
  2. 正则表达式非常脆弱:当OCR输出即使发生微小变化时,基于正则表达式的规则也很容易失效。
  3. 大语言模型智能体引入了理解能力:通过添加大语言模型层和智能体框架,信息提取和解释变得更加有效。智能体结合了工具和推理,为我们提供了现代文档系统的基础。
  4. 现实世界的文档理解是一个系统工程:它需要OCR、版面检测、视觉语言模型、智能体化工作流程以及 grounding 和验证循环。

好了,第一课到此结束。你现在已经看到了基本的文档处理系统是如何工作的,OCR如何融入其中,以及我们如何在其之上叠加智能体推理来使这些系统变得更加智能。

在下一节课中,我们将更深入地探讨所有这些概念的基础:OCR本身在过去四十年的演变。我们将探索OCR如何从Tesseract等经典引擎转变为PaddleOCR等现代深度学习方法,以及这种演变对于现实世界文档工作流程为何重要。

004:OCR四十年的演进历程 📜

在本节课中,我们将学习OCR技术如何从早期依赖字母形状的方法,演进到现代的深度学习系统。我们将聚焦于两个代表不同时代的OCR技术:Tesseract和PaddleOCR,并通过实验了解深度学习OCR带来的改进与挑战。

课程概述

上一节我们介绍了简单的文档处理系统。本节我们将深入探讨OCR本身,回顾其四十年的发展历程。我们将重点关注两个代表性技术:Tesseract(代表传统计算机视觉方法)和PaddleOCR(代表深度学习时代)。通过对比,您将理解AI从手工设计流程到数据驱动模型的根本性转变。

两个时代的代表技术

首先,我们将Tesseract和PaddleOCR置于各自对应的技术时代。

Tesseract代表了传统程序化计算机视觉方法,其特点是大量手工工程、多步骤处理和众多规则。PaddleOCR则是深度学习时代的代表,它使用端到端的神经网络进行文本检测和识别。

从Tesseract风格系统到PaddleOCR风格系统的转变,真实反映了AI领域的整体演进:从精心设计、依赖手工特征的流程,转向直接从数据中学习的、可训练的模型。

Tesseract:传统OCR的典范

现在,您已经在实验1中使用过Tesseract。这里我们补充一些背景信息。这段历史很重要,因为直到大约20年前,这仍是业界最先进的技术。

以下是关于Tesseract的几个要点:

  • Tesseract在80年代和90年代是惠普的专有技术,于2005年开源,此后由谷歌维护。
  • 在实验1中,您使用的是Tesseract的第5版。
  • Tesseract在干净的印刷文档(如纯文字小说)上表现良好,但不擅长处理带有大量图表(如物理教科书)或布局复杂的文档。
  • 它支持多种语言,并且只需CPU即可运行,适合资源受限的系统。

您已在实验1中看到它的一些局限性。任何非标准、非黑白、非直线的“野外文本”都会成为问题。

对于历史爱好者和视觉学习者,可以查看2017年Tesseract论文中嵌入的这些示例。请注意,这些图表充分展示了通过手工工程来理解单个字母形状和间距的方法。

现在我们将告别Tesseract。需要指出的是,尽管这是最早的OCR方法,但您不应将其视为基础技术。下一个时代的技术实际上是全新的方法。换句话说,我们即将探讨的深度学习方法并非建立在Tesseract的代码或架构概念之上。

进入深度学习OCR时代

大约从2015年开始,深度学习方法真正成为OCR的新标准。

深度学习将OCR问题分解为两个可以独立优化的阶段,这使得整个过程更加模块化:

  1. 文本检测:找到所有包含文本的区域。
  2. 文本识别:读取每个文本区域的内容。

在今天的第2课实验中,您将使用由百度开发的PaddleOCR。它是一个开源的深度学习OCR系统,截至2025年已获得相当广泛的采用。

PaddleOCR目前是第3版。在第3版中:

  • 文本检测基于DBNet(可微分二值化网络)。
  • 文本识别基于SVTR(短视觉Transformer)。

PaddleOCR的几个显著优势包括:

  • 处理复杂、不规则和弯曲文本的能力。
  • 在GPU加速下效率很高。
  • 提供多种轻量级部署选项。

PaddleOCR架构解析

这是2025年《PaddleOCR 3.0技术报告》中的架构图。在此图中:

  • 请注意以_DET结尾的灰色框(文本检测模块)和以_REC结尾的文本识别模块。
  • 围绕这两个模块的是左侧的预处理和中心的文本行方向校正。

让我们跟随流程:左侧的输入图像(看起来像一个乳液瓶)首先整体逆时针旋转。然后检测所有文本区域,您会看到叠加的红色矩形。其中一个矩形位于乳液瓶上,且相对于其他文本行是旋转的,因此需要校正。最后,识别框内的文本并返回给用户。

与旧流程相比,您可以清楚地看到,现在大部分复杂性由学习到的模型处理,而非固定规则。

工具选择指南

由于这是一门关于构建文档智能流程的课程,我们来谈谈何时应选择这些工具。

以下是逐行对比分析:

  • 核心方法与技术:我们已经分别介绍了两者的核心方法和技术基础。
  • 许可:好消息是,两者都是开源的。
  • 最佳适用场景:Tesseract非常适合文档扫描场景,特别是黑白文本、布局规则的书籍。PaddleOCR在现实世界用例中表现更好,能够处理标识牌、收据和内容布局复杂的文档。
  • 语言与工具支持:两者都支持广泛的语言(包括拉丁和非拉丁字符),并且都能轻松与Python集成。
  • 部署选项:Tesseract相对轻量,而作为PaddleOCR框架的一部分,它更像一个完整的工具包。

总结与展望

总结本课的幻灯片部分,需要认识到Tesseract和PaddleOCR各自代表了一个OCR发展时代。市场上还有许多其他OCR解决方案可供选择,它们只是众多选项中的两个。

在第4课中,我们将介绍另一个未列于此处的OCR工具,它属于智能体时代

实验环节

又到了实验时间。在本实验中,您将亲手使用PaddleOCR。通过实验,您应该能够:

  • 熟练地在自己的图像上运行PaddleOCR。
  • 解读其输出结果。
  • 思考如何将其集成到更大的文档处理或智能体系统中。

课程总结

本节课我们一起回顾了OCR技术四十年的演进历程,从基于规则和手工特征的Tesseract时代,发展到基于端到端深度学习的PaddleOCR时代。我们了解了深度学习如何将OCR分解为检测和识别两个可优化模块,并显著提升了处理复杂、不规则文本的能力。在接下来的实验中,您将亲身体验深度学习OCR的强大之处,并思考其在实际应用中的集成方式。

005:使用PaddleOCR进行文档处理 🧪

在本节课中,我们将学习如何使用PaddleOCR构建一个现代OCR处理流程,并将其与大型语言模型(LLM)智能体结合,以从复杂文档中提取结构化信息。

概述

我们将首先设置PaddleOCR环境,处理不同类型的文档图像(如收据、表格和手写练习),并分析其输出。接着,我们会将OCR功能封装为智能体可用的工具,并利用LLM的推理能力来纠正OCR错误并提取信息。最后,我们将探索PaddleOCR的布局检测功能,以处理更复杂的多栏文档和图表,并讨论其优势与局限。

实验设置

首先,我们需要为现代OCR流程进行一些基础设置。

我们将导入必要的库:

  • PIL(Pillow)用于处理图像。
  • csvnumpy 用于数据处理。
  • matplotlib 用于绘图。
  • 当然,还有 paddleocr 库中的 PaddleOCR
# 导入所需库
from PIL import Image
import csv
import numpy as np
import matplotlib.pyplot as plt
from paddleocr import PaddleOCR

此外,与上一个实验类似,我们需要导入用于智能体的API密钥。

完成设置后,让我们正式开始。

初始化PaddleOCR

在这里,我们创建一个PaddleOCR对象,并指定语言为英语。请注意,幻灯片架构图中的两个模型在这里有所体现:

  • det 代表文本检测模型。
  • rec 代表文本识别模型。

你无需手动依次调用这些模型。PaddleOCR会将所有预处理和模型调用作为一个完整的流水线来处理。

# 初始化PaddleOCR
ocr = PaddleOCR(lang='en', use_angle_cls=True, det=True, rec=True)

处理收据图像

这张收据图像与实验1中的相同。运行此单元格将执行OCR。

结果将是一个列表,其中每个元素对应一个被处理的页面。对于单页图像,它只包含一个字典。

# 对图像进行OCR
result = ocr.ocr('receipt.png', cls=True)

这个单元格将打印 result[0] 的部分内容,特别是识别出的文本、相关的置信度分数以及边界框坐标。

向下滚动,我们可以看到在整个收据上识别出的所有文本。

# 打印OCR结果示例
for line in result[0]:
    print(f"文本: {line[1][0]}, 置信度: {line[1][1]:.2f}, 坐标: {line[0]}")

在执行下一个单元格之前,先解释一下预期结果。

回顾架构图,流水线还会在必要时对图像进行预处理,例如校正旋转、去歪斜或扭曲问题。

接下来我们将看到的实际上是经过预处理的图像,上面叠加了边界框和识别出的文本。

让我们继续请求图像,然后在另一个单元格中执行以查看叠加了所有信息的图像。

现在,如果你仔细比较这张图像和原始图像,会发现部分背景被移除,并且图像被顺时针轻微旋转以使其更水平。

代码在处理后的图像上绘制边界框,并将识别出的文本置于顶部。

为什么要重复这样的例子?请注意边界框的添加。我们现在获得了关于收据中特定文本字段位置的位置信息。同时注意到我们获取的值是正确的。之前,这个“795”是错误的。

将PaddleOCR封装为智能体工具

现在,我们将把PaddleOCR变成一个可以与智能体一起使用的工具,这与实验1非常相似。

这个较长的函数被设置为一个工具。这里的结果将来自PaddleOCR,其余大部分内容与之前相同。我们将打印出文本、边界框和置信度分数,正如在前面的例子中看到的那样。

定义好函数后,我们现在指定该函数是智能体可用的工具。其余部分将与第1课相同,注意我们使用 gpt-3.5-turbo 作为我们的LLM。

# 定义OCR工具函数
def ocr_tool(image_path):
    result = ocr.ocr(image_path, cls=True)
    # ... 处理并返回结果 ...
    return processed_data

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-docai/img/5ec87d4aeb173b68365a886cd54090d8_9.png)

# 设置智能体,将ocr_tool作为可用工具
# ... 智能体设置代码 ...

使用智能体验证收据总额

这个任务看起来很熟悉,我们将使用收据并验证总额是否正确。

向下滚动,当然我们看到青绿色的PaddleOCR输出,绿色的输出来自LLM。任务同样是进行一些基本的加法运算,因为输入是正确的,所以加法运算正确的几率大大增加,我们确实得到了正确的总额。

这个例子真正展示了PaddleOCR和LLM的结合如何能处理现实世界的收据。

记住表格练习和学生手写作业,我们接下来将处理这两个。

我们将定义另一个辅助函数。这里实际上没有什么新内容,OCR结果仍然来自PaddleOCR,我们仍然有一些打印输出。我们仍然在处理后处理图像,然后使用边界框坐标在其上进行一些标注,但现在它被包装进一个名为 run_ocr 的函数中,在本课剩余部分你会反复看到这个函数。

处理表格图像

让我们将所有这些应用到表格上。这是表格的预览。

我们的第二块输出是PaddleOCR打印的信息。同样,我们有识别出的文本、置信度分数和边界框。滚动过去,我们有实际叠加的边界框,识别出的文本显示为蓝色。

当你目视检查时,可能会发现一个或多个OCR错误。我看到的第一个错误实际上延续了之前指数表示法的问题。这里的“10的20次方”被识别为“1,0,2,0”。这与真实答案相差了数十亿倍。似乎大多数指数都是以这种方式识别的。

但让我们继续前进。我们将给智能体分配任务,再次从标记为“EN to DE”的列中提取FLOPS(每秒浮点运算次数)。

让我们再次滚动查看输出,青绿色来自PaddleOCR,绿色来自LLM。然后,在我们的函数中,我们还要求在底部打印这个结果。

你可以自己进行目视检查,或者相信我,这实际上是完全正确的。让我提请注意在上次运行中不正确的两个方面。

这个“Btenet”和“deep ATT”被正确地标记为未找到,因为它们在原始表格中是空白区域。

幸运的是,我们的科学记数法已被纠正。不再是“1020”,现在报告为“10^20 ops”,代表浮点运算次数。结果不可能是1020,这没有意义。它需要是一个大得多的数字。因此,智能体能够对OCR输出应用推理并纠正这种情况。

处理学生手写练习

让我们看看是否也能解决学生手写语法练习的问题。这是它的预览。

让我们看一下输出,并注意顶部的几个问题。

问题一非常清楚是“I am”,但在文本识别中,我们得到的是“I _”,然后这看起来像“A UAN”给我,所以这不好。问题二,我们在这里有一个额外的下划线,我们可能可以在后处理中清理它,这比Tesseract的表现有所改进。

现在,让我们给智能体分配提取学生回答为JSON的任务。

我们将直接跳到这里的LLM响应。

所以问题一的回答有些无意义。回顾Tesseract架构图,有一个检测阶段和一个识别阶段。检测阶段为我们提供边界框,这显然有助于理解整个文档。这里的识别也更好。在转录或识别文本方面,实际的字符级错误更少。因此,在这些相同的例子上表现肯定更好。

现在我们将继续看一些更困难的例子,以暴露一些弱点。

探索PaddleOCR的局限性

在本节中,我们将查看三个新例子,我们特意选择它们来暴露一些弱点。但如果你正在使用PaddleOCR进行构建,这些都是你需要意识到的事情。

这是一个名为 report.png 的新例子,它似乎是一份关于美国经济报告的内页。

顶部有一个表格,中间有一些基本文本,底部有一个带左侧标题的折线图。

让我们看看PaddleOCR如何提取这个文档。乍一看,表格输出实际上看起来相当不错,文本也相当直接。但让我们看看这个折线图。整个图表周围没有边界框,这是我第一个线索,表明它没有被识别为一个单一单元。但是有一些X轴和Y轴标签周围有边界框,比如这个“0”、“-2”、“-4”、“-6”。现在这些完全脱离了上下文,如果我们向上滚动就能看到。

确实,这里是那个“0”,与这个“-2”完全断开,中间被其他内容隔开,然后是“-4”。因此,真的无法理解这些是Y轴标签,或者它们实际上属于一个被完全忽略的图表。我们暴露了一个弱点。

让我们看看 article.jpeg。它包含什么?

这看起来像是一篇关于牙齿的学术文章的前页。

那么,我们注意到这篇文章有什么特点?这里有多栏文本。在文章顶部,有两栏,摘要旁边是这段说明文字,当然正文呈现为三栏文本,中间被这个表格打断。

让我们向下滚动查看边界框输出,看看它在处理这种多栏文档时表现如何。

这里有很多边界框,但我只想提请你注意第一栏和第二栏。当然,这里的阅读顺序,在大多数进行口腔健康调查的西方国家,你会先读完左手栏。

但在OCR中,如果你跟着我的鼠标,我们会看到直接横向阅读,所以会是“in most of the Westernized countries, that system based on some of the interview”,这当然完全没有意义,并且如果你继续这样再读10到15页,就会把整篇文章弄得一团糟。

我们了解到PaddleOCR无法处理这些多栏布局,如果这是你文档的一部分,它就有弄乱文本的风险。

引入布局检测

我们已经揭示了这些关于布局的弱点,并开始意识到,布局感知的文本检测将成为准确现实世界OCR的基石,因为当然,像这样的布局非常常见。但PaddleOCR模型没有视觉能力,对于更复杂的文档,我们实际上需要某种视觉模型。

好消息是,PaddleOCR实际上有自己的布局检测版本,正如我们之前提到的,它一直在积极开发中,我们之前没有使用布局检测功能,所以现在是我们导入并开始将其与文本检测和识别结合使用的机会。

在这里,我们将初始化该布局检测模型。

# 初始化布局检测模型(假设PaddleOCR有此功能)
# 注意:实际API可能有所不同,此处为示意
layout_ocr = PaddleOCR(use_angle_cls=True, det=True, rec=True, layout=True)

我们将定义一个新函数 process_document,并将图像发送到这个布局引擎。作为响应的一部分,我们将获得标签、分数和边界框。分数和边界框我们以前见过,但现在我们将获得该区域的标签。一旦你看到它,就会更清楚。

让我们将 process_document 应用到那份关于美国经济报告的内页上。

现在,标签的含义应该更清楚了。我们得到诸如“文本”、“图表”、“段落标题”或“数字”,甚至“页脚”等内容。这是在识别文档上的不同区域,并标记它们是什么。

这个长函数的目的是帮助可视化。

让我们再次查看那份经济报告,但现在叠加了布局检测。

看看这里。我看到了几个文本块。我看到了一个段落标题。我看到了这里的一个表格。非常重要的一点是,我看到了一个图表以及围绕该图表的整个边界框,然后还有一些小细节,如数字和页脚。但布局模型现在确实正确地识别了本文档的主要区域。

现在,对那篇关于牙齿的文章做同样的事情。

当我目视观察时,我看到了之前见过的标签,如“文本”和“段落标题”。我看到了一些新的,如“文档标题”和“摘要”。我看到与这些相关的高置信度分数,然后当我向下滚动时,看到了一些脚注、页脚和表格。但我要指出的是,这个表格对我来说是一个表格,但在这里被识别为两个置信度稍低的表格。但总的来说,布局模型现在确实有助于将这些文本保持在一起,所以我们不再有从单词“that”继续到单词“system”的问题,因为整个文本块将表明“that”之后的单词是“undertake”。

处理银行对账单

让我们看看用一个例子能推进到什么程度。这是一个银行对账单。

通常,在处理银行对账单时,最终的下游用例是从中提取一定数量的键值对。

我实际上看到了与表格相同的挑战。在这种情况下,这全部被检测为一个大表格。但对我来说,凭借我的人类视觉和查看银行对账单的经验,实际上可以判断表头应该在这里:“Date”、“Description”、“Category”、“Amount”和“Balance”。这实际上是表格的分隔和表头。上面的一切都可以被解释为一个表格,但它肯定应该与下面的表格分开。

所以这里肯定还存在一些弱点。奇怪的是,在这个例子中,底部的小文本被完全忽略了,而有时法律脚注或其他重要信息可能出现在那里。

总结

好的,实验2到此结束。让我回顾一下几个主要要点。

PaddleOCR属于深度学习OCR时代,显然是一个强大的引擎,在许多现实世界图像上击败了传统OCR,但它仍然主要从单行文本的角度思考。

然后我们添加了布局检测,这为你提供了一些区域级别的结构。比如段落、表格和图形在哪里,但它仍然不是完整的语义理解。它并没有真正代表人类查看文档的方式,而人类在很大程度上是基于视觉系统运作的。

因此,随着课程的深入,我们将继续引入更多来自视觉的概念。在下一课中,我们将交还给David。他将在名为“布局检测与阅读顺序”的课程中,深入探讨一些关于布局和阅读顺序的概念。

006:布局检测与阅读顺序 📄➡️🔍

在本节课中,我们将学习如何处理具有复杂布局的文档。你将了解布局检测和阅读顺序识别的概念、重要性,以及现代系统如何利用基于学习的方法来解决这些问题。我们还将探讨处理表单、表格、手写体等现实挑战,并介绍视觉语言模型(VLM)如何与布局分析结合,构建更强大的文档理解系统。


文档布局的复杂性

文档并不总是以严格的自上而下、从左到右的顺序呈现信息。在本节中,我们将了解为何简单的文本提取流程在处理复杂文档时会失效。

许多团队和组织至今仍依赖以下流程:首先从文档中提取文本,然后将文本输入语言模型并提问。这种方法看似合理、简单甚至优雅,但问题在于,大多数文本提取过程会破坏文档的结构。当我们“压平”一个文档时,其结构就丢失了:列和行混在一起,表格变成无意义的浮动文本框,图注与图表分离,对于复杂的文档(如财务报告、研究论文和法律合同),阅读顺序变得不可预测。仅凭OCR加一个大语言模型(LLM),没有足够的上下文来进行正确推理。


布局检测:理解文档结构

上一节我们介绍了简单OCR流程的局限性,本节中我们来看看如何通过布局检测来修复这个问题。

布局检测,或称文档布局分析,是解决上述问题的第一步。它不再将文档视为原始文本的集合,而是识别页面上的有意义的区域,并确定它们的位置和代表的内容。它能区分段落、表格、图表、页眉、页脚和图注。换句话说,我们不再仅仅是提取内容,而是在理解文档的结构。

这被证明是极其重要的。保留布局可以防止不同部分的文本混在一起变得混乱,在多栏文档中保持叙述的流畅性,并允许你定位页面的特定部分,如表格中的总计或表单中的关键字段。核心思想简单而有力:布局很重要。一旦你丢弃了它,理解就会变得脆弱且容易出错。

以下是布局检测能识别的典型文档组件:

  • 段落
  • 表格
  • 图表
  • 图像
  • 图注
  • 页眉
  • 页脚
  • 印章和批注

通过明确检测和标记这些组件,下游模型在尝试推理之前就知道它们正在处理的是何种信息。


阅读顺序:确定信息流

一旦我们知道了页面上有什么以及它们在哪里,下一个问题就是:应该如何阅读它们?布局告诉我们东西在哪里,而阅读顺序告诉我们人类实际阅读它们的序列。这对于多栏布局或带有浮动图表和图注的文档至关重要。没有可靠的阅读顺序,即使有清晰的布局,仍然存在歧义。

历史上,阅读顺序是通过规则来处理的:你假设区域是自上而下、从左到右的,可能应用一个XY切割算法,然后希望得到最好的结果。这对于非常整洁的文档有效,但对于任何真实且复杂的文档会立即失效。任何分栏、侧边栏或浮动元素都会使这些启发式方法产生无意义的结果。

这种情况随着Layout Reader的出现而改变。它不是一个基于规则的系统,而是一个在Reading Bank数据集上训练的学习模型。该数据集包含超过50万页带有正确阅读序列的标注页面。每个单词被表示为一个元组,包含单词本身、其页面索引以及布局特征(如边界框坐标、宽度和高度)。模型从这些视觉和空间特征中学习预测正确的阅读顺序。这可以处理复杂的布局、多栏结构以及基于规则的系统无法管理的不规则阅读流。


Layout Reader的工作原理

现在,让我们深入了解Layout Reader的实际工作原理。

其架构是一个使用LayoutLM作为编码器的序列到序列模型。LayoutLM由微软在2020年开发,它结合了文本、布局和视觉信息。其目的是预测文档中单词或文本行的正确阅读顺序。

该过程接收OCR产生的边界框(例如,来自PaddleOCR),并重新排列文档的标记序列,以重建人类可读的阅读顺序。该模型在Reading Bank上进行了训练,这是微软为此任务专门创建的基准数据集。它包含50万张标注了阅读顺序的文档图像。你可以看到它处理的多样性:具有多栏布局的研究论文、具有混合结构的科学文档,以及带有表格和列表的财务文档。尽管布局各异,但每个文档都获得了正确的阅读序列。


超越OCR与阅读顺序的挑战

然而,阅读顺序检测完全依赖于OCR的输入质量。这里存在一个根本性的限制:OCR只捕获文本。它错过了视觉信息:图像、图表、图示、空间关系和视觉上下文。因此,即使有完美的阅读顺序,你也在处理不完整的信息。

在实践中,你会反复遇到这些挑战:错位的表单、复杂的表格、手写体、多语言文档以及需要解释而非转录的图表。这些正是基于OCR的流程崩溃的地方。

以下是几个具体的挑战领域及其解决方案:

表单
核心挑战是将标签与值关联起来,尤其是当它们不相邻时。有几种可能的方法:

  • 基于模板的提取:使用固定坐标,速度快,但布局变化时不灵活。
  • 灵活的键值对检测:使用基于邻近度和内容的规则或机器学习。
  • 微调Transformer模型:如LayoutLM,在FUNSD等数据集上训练,自动学习这些关系。
  • 计算机视觉:用于处理非文本元素,如复选框状态。

表格
表格是另一个臭名昭著的失败点。传统OCR通过压平行列关系破坏了表格的完整性。一旦结构丢失,数字就失去了意义。现代模型以不同方式解决这个问题:

  • Table Transformer:使用目标检测将表格、行和列作为独立对象来查找。
  • TAPAS:采用图像到序列的方法,直接将表格图像翻译成HTML。
  • Split & Merge:用于处理大型密集表格。
    输出可以是CSV、JSON、HTML或Pandas DataFrame,以便我们可以对数据进行推理,而不仅仅是文本框。

手写体与多语言文档

  • 手写体:标准OCR在印刷文本上训练,由于各种风格、草书和不一致的字符形态,在手写体上会失败。智能字符识别(ICR)是专门在手写数据集上训练的模型,使用CNN-RNN架构进行序列建模和字符级预测。
  • 多语言文档:带来不同的挑战,如非标准字符、Unicode字体和替代的阅读方向(阿拉伯语从右到左,东亚语言的垂直文本)。这需要具有自动语言检测、特定语言OCR引擎、脚本检测和路由以及按语言调整阅读顺序的强大多语言模型。

视觉语言模型:范式转变

这些专门的模型多年来一直是文档AI的主力,每个都经过优化以擅长特定任务。但最近,一个新的范式出现了:视觉语言模型。这代表了从专门工具到通用智能的根本性转变,可以处理所有这些任务甚至更多。

那么,什么是视觉语言模型?传统的LLM纯粹在文本标记上操作。VLM统一了视觉和语言,同时处理图像和文本,以形成共享的语义表示。这使得模型能够推理视觉场景中发生的事情,而不仅仅是出现了哪些单词。

让我们讨论一下从LLM转向视觉语言模型时发生了什么变化。左侧是一个常规的LLM:它接收文本标记作为输入,经过Transformer处理,然后得到文本输出。模型理解的一切都仅来自文本。右侧是一个视觉语言模型:输入是图像加文本。在任何信息到达语言模型之前,视觉输入必须通过一个视觉编码器转换成LLM能理解的东西,然后它才产生文本输出。

以下是其三个核心组件:

  1. 视觉编码器:如CLIP或SigLIP等模型,将像素转换为视觉向量。
  2. 投影器:这个翻译层将视觉向量转换为LLM可以处理的标记嵌入。
  3. LLM主干:此时,一个标准的LLM对这些视觉标记进行推理并产生文本输出。

关键要点很简单:一个VLM仍然是一个LLM,但在它前面加了一个视觉栈。它能“看到”图像,但它对文档的理解程度仍然取决于如何处理结构、布局和阅读顺序。


结合布局检测与VLM推理

视觉语言模型很强大,但它们本身并不是一个完整的解决方案。如果你一次性给VLM一个视觉丰富的文档,VLM可能会遇到困难:当视觉线索缺失或模糊时会产生幻觉;缺乏确定性定位,意味着它们无法可靠地将答案与页面的特定区域联系起来;它们难以处理嵌套布局、多页结构和小文本。VLM在与结构配对时表现出色,但它们无法取代现实世界文档任务所需的结构推理。

一个可能的解决方案是将布局检测与VLM推理结合起来。我们之前讨论的布局分析提供了结构基础:识别阅读顺序、文档区域及其类型。然后,这些结构信息可以指导你如何处理每个区域:

  • 图表和可视化可能被发送给带有针对性提示的VLM。
  • 表格可能使用VLM或专门的Transformer,取决于复杂性。
  • 文本区域可能使用传统OCR或基于VLM的提取。

总之,布局检测提供了确定性定位,而VLM则处理那些受益于视觉推理的元素。在实验环节中,你将实现一个展示这种工作流程的管道。


实验环节概述:构建混合架构

在实验环节,你将通过一个智能体框架来编排这个工作流程。以下是实现步骤:

  1. 输入文档:从输入文档开始。
  2. 文本提取与排序:使用PaddleOCR提取和排序文本。这将为你提供所有OCR文本以及边界框和置信度分数。
  3. 阅读顺序重排:使用Layout Reader对提取的文本进行重新排序。
  4. 区域检测:运行PaddleOCR的布局检测,以识别表格、图表和文本块。
  5. 构建上下文:将以上所有信息(按正确顺序排列的OCR文本、区域ID和块类型)作为上下文提供给一个LangChain智能体。
  6. 智能体工具调用:该智能体将可以访问两个专用工具:
    • analyze_chart:将裁剪后的图表图像发送给VLM,以提取图表类型、坐标轴、数据点和趋势。
    • analyze_table:对表格执行相同操作,根据用户问题提取表头、值和注释。
  7. 智能决策:根据用户的问题,智能体可以决定哪个区域需要VLM分析,以及调用哪个工具。

通过实验,你将清楚地看到这些部分如何协同工作,以及这如何为下一节课中Andrea将要介绍的ADE(智能体化文档抽取)奠定基础。


总结

本节课中,我们一起学习了文档AI中布局检测与阅读顺序的核心概念。我们了解到,简单的OCR加LLM流程在处理复杂布局时会失效,因为结构信息丢失了。布局检测通过识别文档中的不同区域(如段落、表格、图表)来恢复结构,而阅读顺序模型(如Layout Reader)则能预测人类阅读这些信息的正确序列。我们还探讨了处理表单、表格、手写体等现实挑战的专门模型,并介绍了视觉语言模型(VLM)这一新范式。最后,我们认识到最强大的解决方案是将确定性的布局分析与灵活的VLM视觉推理相结合,构建一个混合架构,这将在实验环节中具体实现。

007:实验3 - 构建智能体化文档理解系统 🧠

在本节课中,我们将构建一个结合了OCR、版面检测和视觉语言模型(VLM)工具的智能文档分析智能体。你将学习一种智能体化的方法,使系统能够自动识别不同的内容类型,为每种类型应用正确的工具,并将洞察整合成连贯的答案。这类似于人类分析师处理复杂报告的方式:先扫描结构,然后根据需要深入分析特定部分。

整个流程包含三个核心组件:

  1. 文本提取:使用 PaddleOCR 进行文本提取,并使用 LayoutLM 确定阅读顺序。
  2. 版面检测:使用 PaddleOCR 的版面检测功能来识别表格、图表和区域。
  3. 智能体处理:使用 LangChain 智能体,配备两个专用工具:analyze_chart(基于VLM的图表分析)和 analyze_table(基于VLM的表格提取)。

环境与库准备 🛠️

首先,我们从 .env 文件加载环境变量,该文件包含后续定义智能体时所需的 OpenAI API 密钥。

接下来,导入构建流程所需的核心库:

  • Pillow:用于加载和操作图像。
  • cv2:来自 OpenCV,用于图像处理和绘制边界框。
  • matplotlib:用于可视化结果。
  • numpy:用于对图像数组进行数值运算。
  • dataclass:Python 内置装饰器,用于创建清晰的数据结构。
  • typing:用于类型提示,使代码更具可读性。

环境设置完成后,让我们进入流程的第一个阶段。


阶段一:文本提取与阅读顺序 📖

上一节我们介绍了环境准备,本节中我们来看看如何从文档中提取文本并使用 LayoutLM 确定正确的阅读顺序。

首先,我们创建一个配置为英文文本的 PaddleOCR 实例。然后加载我们的示例文档(与上一个实验中使用的经济学报告相同)。现在,在文档上运行 OCR 引擎。

正如你在上一个实验中看到的,OCR 引擎为每个检测到的区域返回三个关键信息:识别的文本字符串、置信度得分和边界框坐标。

视觉验证很重要。我们为每个检测到的文本区域绘制绿色边界框。这有助于我们确认 OCR 是否正确识别了文档中的所有文本区域。请注意,每一行文本、每个表格单元格和每个标签都有自己的边界框。

原始的 OCR 输出只是一个值列表。为了使代码更清晰、更易于维护,我们创建一个 OCR 区域数据类。这为每个文本区域提供了一个清晰的结构类型,以及一个方便的 bounding_box_xyxy 属性,该属性将 4 角多边形转换为简单的 (x1, y1, x2, y2) 格式。这个结构将在我们流程的其余部分中使用。

现在我们有了原始的 OCR 输出,需要确定正确的阅读顺序。让我们使用基于 LayoutLMv3 的 Layout Reader 来完成这个任务。我们从 Hugging Face 加载预训练的 Layout Reader 模型。

该模型以边界框坐标作为输入,并预测每个框的阅读顺序位置。该模型经过训练,能够理解常见的文档布局,包括单栏、多栏、表格和混合布局。

模型加载后,让我们定义阅读顺序检测的核心函数。

以下是它的分步操作:

  1. 计算图像尺寸:我们根据边界框估算图像大小,并添加 10% 的填充。
  2. 归一化坐标:LayoutLM 期望坐标在 0 到 1000 的范围内,因此我们缩放边界框。
  3. 准备输入:我们将其转换为 Transformer 模型期望的格式。
  4. 运行推理:获取模型的预测结果。
  5. 解析结果:我们从模型的输出 logits 中提取阅读顺序。

结果是一个列表,告诉我们每个 OCR 文本区域的阅读顺序。让我们看看阅读顺序的实际效果。

此可视化在每个文本区域上叠加数字,显示它们应该被阅读的顺序。红色数字表示阅读位置。注意逻辑流程:标题在前,然后是内容,遵循人类阅读文档的自然方式。当然,它并不完美,有时会出现跳跃。这展示了使用这种方法可能面临的一些限制。根据复杂程度,你可能需要微调自己的布局模型来进行分块和确定阅读顺序,这可能难以开发、扩展和维护。

最后,让我们将 OCR 文本与阅读顺序结合起来,生成正确排序的文本。此函数将每个 OCR 区域与相应的阅读位置配对,然后按位置对所有区域进行排序,并返回一个包含位置、文本、置信度和边界框信息的字典列表。

这个有序文本将成为我们智能体上下文的一部分,使其能够理解文档内容,而无需为基本的文本问题调用 VLM。


阶段二:版面检测与区域准备 🗺️

现在我们已经可以提取和排序文本,接下来需要了解文档中存在哪些类型的内容。这就是版面检测发挥作用的地方。正如你在上一个实验中看到的,PaddleOCR 提供了一个单独的版面检测类,专门用于识别文档结构。

让我们初始化它。接下来,我们定义 process_document 函数来运行版面检测并返回检测到的区域列表。每个区域包括标签(内容类型,如文本、表格、图表、图形等)、置信度得分和边界框坐标(XYXY 格式)。

让我们仔细看看前五个检测到的区域。注意不同的内容类型:有文本块、图表和段落标题。

与我们的 OCR 结果类似,让我们创建一个版面区域数据类,以便进行清晰的数据处理。每个区域将获得一个唯一的 ID,我们的工具需要用它来引用特定区域。

然后,我们将遍历每个结果并将其存储在结构化格式中。现在,让我们用颜色编码的框可视化所有版面区域。每种区域类型都有唯一的颜色,我们显示区域 ID、类型和置信度得分。如你所见,我们识别出了标题、表格、图表和正文。

识别出版面区域后,我们需要为智能体准备它们。为了让我们的智能体分析图表或表格,我们需要将裁剪后的区域发送给 VLM。这种方法有几个好处:

  1. 聚焦分析:VLM 只看到相关内容。
  2. 减少干扰:周围文本不会造成干扰。
  3. 降低成本:较小的图像意味着更低的 API 成本。

现在,让我们使用 crop_region 函数裁剪每个区域。我们还将图像转换为 base64 编码,这是视觉 API 期望的格式。然后加载图像,并使用这两个函数为每个裁剪区域创建一个包含信息的字典。

请记住,尽管 VLM 功能强大,但它们在定位方面并不出色。这种方法可能有助于提高准确性,但你需要为各种边缘情况设计新的提示词,以应对更大的复杂性和变化。

让我们显示所有裁剪区域,看看智能体将可以访问哪些内容。每个区域都标有其 ID 和类型。


阶段三:构建智能体工具 🛠️

现在我们已经提取并排序了文本,并检测了版面,可以开始构建我们的智能体了,首先从工具开始。

我们将创建两个专用工具,每个都设计用于特定类型的内容分析:

  • analyze_chart:使用视觉语言模型来解释图表和图形。
  • analyze_table:同样使用 VLM 从表格中提取结构化数据。

通过创建专用工具,我们可以为每种内容类型使用优化的提示词,并返回易于处理的 JSON 结构。对于 VLM,我们将使用 OpenAI 的 GPT-4o-mini。

接下来,让我们为每个工具定义提示词。提示词对于从 VLM 获取有用的结构化输出至关重要。每个提示词将包含三个部分:

  1. 定义角色:例如,“你是一个图表分析专家”。
  2. 定义要提取的内容:特定字段,如图表类型、坐标轴和数据点。
  3. 提供 JSON 模板:以确保一致的输出格式。

精心设计的提示词将有助于确保 VLM 返回我们的智能体可以可靠使用的数据。我们将为第二个提示词使用相同的逻辑。

在创建工具之前,让我们定义一个实用函数,处理调用 VLM 与图像的机制。它创建一个包含文本提示词和 base64 编码图像的多模态消息,然后返回模型的响应。

来自 LangChain 的 @tool 装饰器将我们的函数转换为智能体可用的工具。它接收区域 ID,使用我们刚刚创建的字典检查区域是否存在,获取区域数据,然后将裁剪后的图像(base64 格式)和相应的提示词传递给 VLM。

遵循相同的模式,让我们创建第二个用于表格提取的工具。它使用特定于表格的提示词来指导 VLM 提取具有适当标题和行组织的结构化数据。

在将工具集成到智能体之前,让我们单独测试它们。这可以验证 VLM 连接是否有效,以及我们的提示词是否产生有用的输出。

我们将找到第一个图表区域并分析它。注意数据点很接近,但并非 100% 准确。这是由于我之前提到的视觉推理和定位能力不足。同样地,让我们测试表格工具。这个工具在从简单表格中提取信息方面做得不错。然而,随着复杂性的增加,智能体在定位和提取方面可能会遇到更多困难,并且可能更容易产生幻觉。


阶段四:组装与测试智能体 🤖

工具经过测试并正常工作后,我们准备构建将协调一切的智能体。

那么智能体是如何工作的呢?

  1. 智能体收到一个关于文档的问题。
  2. 它读取包含所有 OCR 文本和版面信息的系统提示词。
  3. 它将决定是仅从文本中回答,还是需要使用工具。
  4. 对于图表和表格等视觉内容,它将调用适当的工具。
  5. 最后,它将所有信息组合成一个连贯的响应。

在构建智能体之前,让我们验证我们的数据结构是否已准备就绪。我们需要来自 OCR 加 LayoutLM 的有序文本,以及来自版面检测的版面区域。

现在,让我们为智能体准备上下文。智能体需要一个格式良好的系统提示词。因此,这些辅助函数将我们的数据结构转换为可读的文本字符串。格式化后的上下文充当智能体对文档的记忆。

现在我们构建系统提示词,这是我们智能体行为的基础。它包含:

  • 角色定义:例如,“你是一个文档智能体”。
  • 文档上下文:按阅读顺序排列的所有 OCR 文本。
  • 版面信息:区域类型和 ID。
  • 工具描述:何时使用每个工具。
  • 指令:如何处理不同的内容类型。

最后,让我们把所有东西整合起来。我们将使用 LangChain 的 create_tool_calling_agent 来构建我们的智能体。这涉及:

  • 工具列表:我们的 analyze_chartanalyze_table 函数。
  • LLM:GPT-4o-mini(出于成本考虑)。
  • 提示词模板:结合了系统提示词、用户输入和思考过程。
  • 创建智能体:一个可以调用工具的智能体。
  • 设置执行器:设置代理执行器以在循环中运行工具。我们将 verbose 设置为 True,这个设置让我们可以看到智能体的推理过程。

智能体组装完成后,让我们对其进行测试。首先,我们问一个关于文档的一般性问题,这个问题应该仅从 OCR 文本中就能回答,无需调用任何工具。观察智能体如何使用其上下文进行响应。

现在让我们要求智能体提取表格数据。观察它如何识别表格区域并调用 analyze_table 工具来获取结构化数据。详细输出显示了智能体的推理(或“思考”)过程。

作为最终测试,让我们询问关于图表的信息。这个智能体将使用 analyze_chart 工具来提取无法仅从 OCR 文本确定的趋势和数据点的视觉信息。


总结 📝

本节课中,我们一起构建了一个完整的智能体化文档智能流程,它结合了多种 AI 技术来理解复杂文档。这些组件共同形成了一个混合系统,能够深入理解更复杂的报告、表格、图形、多栏布局、标题和叙述流程。

然而,随着文档变得更加多样化和多变,这种多智能体流程可能会开始失效。它变得难以维护,在边缘情况下变得脆弱,并且难以扩展,因为每个组件都必须手动调整、监控和协调。

在下一课中,Andrew Ng 将介绍 Landing AI 的智能体化文档提取,它将文档理解的所有基本步骤(版面分析、文本提取、区域分割、阅读顺序重建、多模态推理和基于模式的字段提取)统一到一个单一的、协调的智能体工作流程中。我们下节课见。

008:用于智能体化文档理解的单一API 🚀

在本节课中,我们将学习如何使用Landing AI的智能体文档提取框架来解析文档并提取关键信息对,整个过程无需使用字符识别、版面分析或视觉语言模型。在实验环节,你将使用ADE来解析复杂文档,并构建一个处理混合文档的流程。

概述

上一节我们介绍了文档AI的基础概念,本节中我们来看看Landing AI的智能体文档提取技术。ADE是一个将输入文档、演示文稿、图像甚至电子表格转换为结构化的Markdown和JSON的API。它旨在简化文档处理流程,用一个统一的接口替代传统方案中的多个复杂步骤。

关键用例

ADE主要服务于两大核心应用场景。

以下是两种主要的应用场景:

  1. 字段提取:也称为键值对提取。用户需要从大量外部提供的文档中提取特定信息,并能够追溯到原始文档。
  2. RAG:构建能够理解底层内容的知识助手。用户需要处理包含表格、图表、流程图等复杂内容的文档,并同样具备追溯能力。

技术核心:ADE的工作原理

ADE并非对现有技术的渐进式改进,而是一种由Landing AI工程师开创的文档AI新方法。它属于智能体时代的一部分,采用视觉优先、数据为中心的方法。

ADE建立在三大支柱之上:

  • 视觉优先:将文档视为视觉对象,其含义编码在布局、结构和空间关系中。
  • 数据为中心:在最高质量的精选数据上进行训练。我们相信正确的数据与正确的模型架构同等重要。
  • 智能体化:ADE系统能够规划、决策、行动和验证,直到响应满足质量阈值。

技术架构

下图清晰地说明了视觉是ADE的基础。Landing AI将多年的视觉专业知识应用于文档AI问题。

架构的最底层是最先进的文档原生视觉模型,它们经过训练,能够像人类一样“看”文档。在此基础上,智能体负责解析和路由不同的文档块(如文本、表格、图形),它们各自遵循独立的处理路径。最顶层的智能体与应用层则专注于交付用户真正需要的功能,如字段提取和文档拆分。

上一张幻灯片中提到的基础视觉模型,属于不断增长的文档预训练Transformer家族。在录制本课程时,你可以选择DPT1、DPT2和DPT2 mini。所有这些模型都能返回高质量的文档解析结果。

以下是DPT模型的核心能力:

  • 阅读顺序检测
  • 版面检测
  • 文本识别
  • 图注生成

性能表现

那么,作为ADE核心的这些DPT模型表现如何呢?

ADE在DocVQA基准测试集上的表现实际上超过了人类水平,准确率达到99.15%,同时也超过了所有其他已发布的模型。我建议大家关注宣布这些结果的博客文章,其中有一个精彩的交互式文档库可供探索。

如果你不熟悉DocVQA,它是一个基于真实扫描文档的问答基准,文档来自UCSF行业文档库。本页幻灯片右下角展示了一个例子:需要回答的问题是“个人的家庭电话号码是什么?”,答案可以在绿色边界框内的手写部分找到。

如何使用ADE

这自然引出了下一个问题:我该如何使用它?

下图是ADE可视化操作平台的截图。ADE提供解析拆分提取三个独立功能,你可以在开发文档处理流程时灵活组合它们。

开发选项包括:

  • 用于拖放操作的可视化操作平台。
  • REST API。
  • Python和Typescript库。

在接下来的实验中,你将只使用Python库以及parseextract这两个API。请注意,split API也可用于拆分大型PDF,parse_jobs API则适用于处理超大型文档(例如数百到数千页)。

若要在课程之外使用ADE,你需要自己的API密钥。你可以访问 vdo.landing.ai 生成一个免费密钥并获得一些初始免费额度。

现在,又到了实验时间。请和我一起完成三个动手练习。

总结

本节课中我们一起学习了Landing AI的智能体文档提取框架。我们了解了ADE如何通过一个单一的API简化复杂的文档处理任务,其核心是视觉优先、数据驱动和智能体化的架构。我们还看到了它在DocVQA基准测试上的卓越表现,并介绍了开始使用它的基本方法。接下来,请在实验环节亲自体验ADE的强大功能。

009:使用智能体化文档抽取进行文档理解 🧪

在本节课中,我们将学习如何使用Landing AI提供的基于API的智能体化文档抽取功能。我们将通过三个练习,从一份简单的公用事业账单开始,逐步探索该技术如何解析和理解各种复杂文档,包括图表、流程图、手写内容等。

概述 📋

智能体化文档抽取(Agentic Document Extraction)通过两个核心API——parse(解析)和extract(抽取)——简化了文档理解流程。parse API将文档转换为结构化的标记语言(Markdown)和区块(Chunk)信息,而extract API则根据用户定义的JSON模式(Schema),从解析结果中精准地抽取所需信息。这种方法无需训练自定义模型,即可处理多样化的文档类型。


实验设置与初始化 ⚙️

首先,我们需要设置实验环境并初始化必要的库和客户端。

上一节我们介绍了课程目标,本节中我们来看看如何准备实验环境。在Notebook的顶部,有几个用于设置的单元格。

大多数导入的库是熟悉的数据科学工具或在之前课程中使用过的。本节课新增的是从Landing AI导入的模块。

我们将导入landingai_ade以及用于解析和抽取的特定类型。

接下来,从环境变量中加载API密钥。请注意,如果你想在本课程之外创建自己的API密钥来使用智能体文档抽取功能,Notebook中会提供相关说明。

初始化客户端后,我们的设置就完成了。

以下是初始化步骤的代码示例:

# 导入Landing AI相关库
from landingai_ade import AdeClient, ParseResponse, ExtractResponse
import os

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-docai/img/a9e1707fd9a9854f8195cb7dd87a39b8_7.png)

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-docai/img/a9e1707fd9a9854f8195cb7dd87a39b8_8.png)

# 从环境变量加载API密钥
api_key = os.environ.get(“LANDINGAI_API_KEY”)
# 初始化客户端
client = AdeClient(api_key=api_key)

在设置的延续部分,我们将导入一系列辅助函数。你可以查看所有这些函数的内容,它们主要用于在屏幕上绘制和显示内容,以保持Notebook的整洁。


练习一:从公用事业账单中抽取可验证的键值对 🧾

在本练习中,我们将从一份圣地亚哥的燃气和电费合并账单中抽取关键信息。

预览文档

首先,使用一个辅助函数预览文档。这是一份特定月度周期的账单,包含独立的燃气和电费明细,以及两者的使用历史记录。

解析文档

我们将使用在幻灯片部分介绍过的文档预训练转换器(Document Pretrain Transformer)来解析文档。

调用parse API非常简单,只需指定文档路径和模型类型(我们将使用dpt2-latest)。DPT2大约每月更新一次,因此你的结果可能与我的略有不同。

解析完成后,我们会打印响应中的一些细节,例如作业ID、字符数和区块数量。这份单页账单被解析为约6000个字符和24个区块。

探索解析结果

解析输出的结构良好,便于探索。以下是你可以进行的操作:

  • 叠加可视化:将解析结果(如不同颜色的边界框)叠加到原始文档图像上,直观查看区块划分。
  • 检查区块结构:打印前几个区块,查看其标识符(ID)、类型(如文本、表格、图形)、页码和坐标。
  • 统计区块类型:对区块按类型进行简单聚合统计。
  • 查看Markdown:显示整个文档或特定区块(如表格)的Markdown内容。表格单元格在Markdown中拥有自己的唯一标识符,这对于后续的可视化定位(Visual Grounding)至关重要。

以下是调用解析API的示例:

# 调用parse API
parse_result = client.parse(
    document_path=“utility_bill.pdf”,
    model_type=“dpt2-latest”
)
# 打印解析作业信息
print(f“Job ID: {parse_result.job_id}”)
print(f“Character count: {parse_result.character_count}”)
print(f“Number of chunks: {len(parse_result.chunks)}”)

定义抽取模式(Schema)

抽取步骤将解析步骤得到的Markdown与一个模式(Schema)结合起来。我们需要定义一个与公用事业账单对应的JSON模式。

这个模式包含10个属性。需要注意以下几点:

  • 可以拥有嵌套模式(例如,account_summary下嵌套了current_chargestotal_amount_due)。
  • 支持多种数据类型,如布尔值(Boolean)、字符串(String)、数值(Number)等。
  • 每个字段都有详细的描述。描述越详细,抽取时得到预期结果的可能性就越高。

以下是模式定义的示例:

# 定义抽取模式
schema = {
    “account_summary”: {
        “type”: “object”,
        “properties”: {
            “current_charges”: {“type”: “number”, “description”: “本月当前费用总额(美元)”},
            “total_amount_due”: {“type”: “number”, “description”: “应付总额(美元)”}
        }
    },
    “has_usage_chart”: {“type”: “boolean”, “description”: “文档中是否包含使用量图表”},
    “max_gas_month”: {“type”: “string”, “description”: “过去12个月中燃气消耗量最高的月份”}
    # ... 其他字段定义
}
schema_str = json.dumps(schema, indent=2) # 转换为格式化字符串供API使用

执行信息抽取

准备好模式后,就可以调用extract API了。我们将使用最新的抽取模型。

抽取完成后,探索结果。我们期望得到10个值,混合了数字、布尔值和字符串。例如:

  • 账户摘要中的当前费用为155.15美元,这也等于应付总额。
  • 布尔值告诉我们是否存在使用量图表。
  • max_gas_month字段正确解读了图表中的信息,指出在过去12个月中,1月是燃气消耗最高的月份,8月是电力消耗最高的月份。

在抽取元数据(extraction_metadata)中,我们可以看到每个抽取值的信息来源引用(例如0-A0-D)。这些引用指向解析结果中的特定区块或表格单元格,实现了信息的可视化定位。

结合解析结果和抽取响应,我们就获得了构建用户界面所需的所有信息,可以向用户展示他们想要的字段及其在文档中的确切来源位置。

练习一小结

在练习一中,你使用了两个API:parseextractparse API的功能类似于我们在第1、2、3课中所做的工作,但智能体化文档抽取API抽象了所有模型训练、配置和自定义编码,仅需一次API调用即可理解整个文档,并且输出质量更高。当你用更复杂的文档进行测试时,这种质量差异在练习二中会更加明显。


练习二:探索智能体化文档抽取在复杂文档上的性能 🚀

在练习二中,我们将观察智能体化文档抽取API在一些更困难文档上的表现。我们为你挑选了6个在不同方面具有挑战性的文档,但相同的API和底层文档预训练转换器能够理解所有这些文档。

本节将快速浏览这些示例,但你可以花时间仔细探索。我们定义了一个函数,它会为每个文档调用parse API并打印多种输出,使我们能够快速运行所有六个示例。

图表与流程图

示例1:投资者演示文稿中的图表

  • 文档特点:包含条形图等商业图表。
  • 解析表现:ADE正确检测出所有图形区块。例如,它能识别出图表中的水平平均线并描述为“平均收入倍数为4.3倍”。

示例2:流程图

  • 文档特点:包含大量基于空间和关系的箭头指向信息,人类凭直觉理解,但对传统OCR极具挑战。
  • 解析表现:ADE将整个流程图检测为一个大的图形区块,这是正确的。在Markdown中,它能够理解流程逻辑,例如“良好参考 -> 如果是 -> 选择候选人”,即使“选择候选人”在页面上的位置比“良好参考”更高。

复杂表格

示例3:无线格的表格

  • 文档特点:没有列分隔线和行分隔线,且空白区域很大,作为OCR问题非常困难。
  • 解析表现:ADE将整个部分检测为表格,并理解了所有空白单元格,在输出的Markdown中表现为规整的单元格阵列。

示例4:巨型表格(Mega Table)

  • 文档特点:包含超过1000个独立单元格,且存在合并的行和列。
  • 解析表现:ADE成功解析了整个巨型表格的结构。如果尝试用大语言模型(LLM)提取此类表格,由于其上下文窗口无法容纳如此多的数字,很容易产生幻觉(Hallucination)。这正是智能体化方法变得至关重要的地方。解析后,可以轻松将表格保存为CSV或HTML格式。

手写与特殊标记

示例5:包含手写、复选框和圈选的医疗表格

  • 文档特点:包含手写文字、复选框、多个被圈出的“否”字,以及标记出的既有病症(如哮喘、循环系统问题等)。
  • 解析表现:令人印象深刻的不只是区块识别,更是Markdown内容本身。它能检测到复选框([ ][x])和被圈出的选项(如(no)),并准确列出被标记的病症。这种输出结合LLM,可以非常可靠地提取医疗信息。

示例6:手写微积分题解

  • 文档特点:包含复杂的手写数学公式和符号,如平方根、分数等。
  • 解析表现:ADE为每个问题生成了独立的边界框,并在Markdown中成功表示了数学函数和符号,例如正确显示了“√(√2/2)”。

纯图形与复杂版式

示例7:宜家组装说明书(纯图形页面)

  • 文档特点:没有任何文字,全是图示。
  • 解析表现:我们尝试使用dpt1-latest模型。它将所有内容检测为图形区块,并对中间图形生成了准确的文字描述,例如“指示用户避免在坚硬表面上直接组装或放置物体以防止损坏,建议在组装时使用保护垫或地毯”。

示例8:信息图(Infographic)

  • 文档特点:本质上是信息图,版式复杂,混合了图形、文本、徽标和表格。
  • 解析表现:这对布局检测能力提出了考验,但ADE识别出的图形、文本、徽标和表格区块看起来相当可靠。

示例9:原产地证书(包含印章和签名)

  • 文档特点:包含印章(内有弯曲文字)和签名,背景可能有其他文字,是难度很高的示例。
  • 解析表现:ADE使用“attestation”(证明)区块类型来标识这些区域。在Markdown中,它能够识别出这是“官方公司印章和签名”,并提供可读的签名文字和印章内容。

练习二小结

在练习二中,你看到了智能体化文档抽取方法如何能够处理文档内部和跨文档的复杂性,而无需用户进行任何额外的操作或输入。这意味着智能体化文档抽取可以可靠地用于处理现实世界中具有此类复杂性和多变性的文档处理流程。


总结 🎯

本节课我们一起学习了智能体化文档抽取的核心工作流程。

  1. 初始化与解析:我们首先设置环境,然后使用parse API将文档转换为结构化的区块和Markdown表示。这步抽象了底层视觉模型,提供了丰富的文档结构信息。
  2. 定义模式与抽取:接着,我们根据目标信息定义JSON模式,并使用extract API从解析结果中精准抽取所需数据。抽取结果包含了值的来源引用,实现了可视化定位。
  3. 处理复杂文档:通过多个示例,我们验证了该技术能够稳健处理各种挑战,包括复杂图表、无边框表格、手写内容、纯图形文档以及带有印章签名的文件。

智能体化文档抽取通过简化的API接口,将强大的文档理解能力封装起来,使开发者能够更轻松地构建处理多样化、非结构化文档的应用程序。我们将在后续视频中继续探讨练习三的内容。

010:基于智能体化文档抽取的文档理解(第二部分)

概述

在本节课程中,我们将继续完成Lab-4的练习。我们将构建一个模拟银行审核贷款申请的完整文档处理流程。该流程将处理用户上传的多种未知类型的财务文件,自动识别文件类别,提取关键信息,并进行数据验证。我们将主要使用Landing AI的ADE(Agentic Document Extraction)API来实现这些功能。


准备工作

上一节我们介绍了实验的基本设置,本节中我们来看看具体的实现步骤。首先,我们需要确保环境已正确配置。

以下代码导入了必要的库并设置了API密钥:

import pandas as pd
import os
from landingai.vision import DocumentExtraction
from landingai.common import APIKey, ClientInfo
import landingai

# 从环境变量加载API密钥
api_key = os.environ.get("LANDINGAI_API_KEY")
landingai.configure(api_key=api_key)

我们还需要导入一些辅助函数,这些函数可以在单独的帮助文件中查看。


实验三:构建贷款申请文档处理流程

在实验三中,你将扮演一名银行职员,负责审核贷款申请。你需要开发一个相当真实的完整文档处理流程。

申请贷款时,申请人通常需要上传各种财务文件,例如:

  • 收入证明(如工资单)
  • 显示上一年总收入的税表
  • 显示资产价值的银行对账单或投资对账单
  • 政府签发的身份证明文件

银行面临的挑战是,收到的文件可能有各种奇怪的名称(例如 upload_a.pdf, image_4.jpg)。银行需要理解上传的文件是什么,然后从中提取出他们真正感兴趣的关键信息(例如总资产价值)。

这就是我们要构建的处理流程:用户上传多个未知类型的混合文档,我们将解析所有文档,识别它们是什么,从中提取关键信息对,然后运行一些验证。我们将使用ADE作为主要的API来完成所有这些工作。


预览上传的文档

首先,我们预览一下提供的文档。需要提醒的是,这些文档来自不同的人,并非一个完整的贷款申请套件,因为我们使用的是来自互联网的匿名样本文档。

我们创建一个函数来显示特定目录中的所有文件:

def display_documents_in_directory(directory_path):
    # 函数实现:列出并显示目录中的所有文档
    ...

执行后,我们可以看到以下文档:

  • upload_a.pdf:看起来是一份富达投资对账单。
  • upload_b.pdf:看起来是一份工资单。
  • upload_c.pdf:乍一看像是一份银行对账单。
  • upload_d.pdf:看起来是一本护照。
  • upload_e.pdf:看起来是一份美国W-2税表。

当然,人类可以瞬间识别所有这些文档,但文档处理流程如何以同样的方式理解它们呢?回想一下,所有文档都是以upload_a这样的名称传入的,因此我们需要对它们进行分类。


使用Pydantic模式进行文档分类

为了对文档进行分类,我们将使用ADE的extract API,并配以一个Pydantic JSON模式。

这个模式与练习一中的模式略有不同,因为我们使用的是Pydantic而不是纯JSON。ADE接受这两种格式作为输入。

以下代码定义了一个DocumentType类,枚举了我们期望的文档类型,并为每种类型提供了丰富的描述:

from pydantic import BaseModel, Field
from typing import Literal

class DocumentType(BaseModel):
    """定义可能的文档类型"""
    doc_type: Literal["paystub", "investment_statement", "bank_statement", "id", "w2"] = Field(
        ...,
        description=(
            "The type of financial document. "
            "`paystub`: A payroll statement showing income. "
            "`investment_statement`: A statement from a brokerage like Fidelity or Vanguard. "
            "`bank_statement`: A statement from a bank. "
            "`id`: A government-issued ID like a passport or driver's license. "
            "`w2`: A US tax form W-2."
        )
    )

接下来,我们继续使用Pydantic来定义文档提取模式。我们想知道从工资单、投资对账单、银行对账单等文件中提取什么信息。

在这个单元格中,我们有五个不同的文档提取Pydantic模式。让我们看其中一两个:

  • 身份证明文件模式 (IdSchema):我们期望某种政府签发的身份证明,如驾照或护照。它应包含个人的全名、签发州或国家、签发日期和某种唯一标识符。
  • W-2税表模式 (W2Schema):W-2是美国每年年底签发的税表。我们期望雇员姓名、签发年份以及box1_wages(指该人在该年的总收入)。这里我们有一个整数和一个浮点数。

你可以在自己的时间查看其他模式。

在单元格底部,请注意我们将模式映射到一个简写名称,以便后续使用。此单元格使用Landing AI提供的函数将Pydantic模式转换为JSON。请记住,ADE接受JSON和Pydantic来定义你的模式,但在底层,我们会先将Pydantic转换为JSON,然后再传递给extract API。


执行文档解析与分类

现在我们已经准备好所有组件来进行解析和分类。在执行之前,我们先看一下代码。

我们将处理输入文件夹中的所有文档,将它们发送到parse API,使用最新的gpt-4模型。这里有一个新参数:我们实际上要求为每个单独的页面返回Markdown响应,而不是整个文档。例如,如果投资对账单有11页,我们将得到11个不同的Markdown。这样做的原因是,为了识别文档,我们通常只需要第一页。因此,在识别步骤中,我们将只使用第一页的Markdown。

在第二步中,我们调用extract API,使用之前定义的doc_type JSON,并发送第一页的Markdown。

我编写的代码实际上是按顺序发送每个文档,但有很多选项可以并行化此工作负载,从而使整个过程更快。

让我们现在开始执行。结果显示:

  • upload_c 被识别为银行对账单。
  • upload_a 是投资对账单。
  • upload_e 是W-2税表。
  • upload_d 是身份证明文件。
  • upload_b 是工资单。

非常好!至此,我们有了所有五个文档对应的Markdown,并且知道它们的类型。接下来,我们准备将正确的模式应用于正确类型的文档,并提取那些关键信息对。


提取关键信息对

在执行此单元格之前,我们先看一下代码。首先,我们创建一个字典来存储提取结果。然后,对于每个文档,我们希望根据其文档类型查找适当的模式。接着,我们将正确的模式与Markdown一起发送到extract API。最后,我们将存储返回的所有结果,重点关注提取内容本身和提取元数据(我们将在其中找到视觉定位信息)。

现在开始执行。在所有输出中,回想一下:

  • upload_d 是身份证明文件,是一本由日本签发的护照,签发日期为2025年3月24日。
  • upload_e 是W-2税表,属于John Doe,他在2024年赚了2323美元。

这一切看起来都不错。输出的顶部是提取内容本身,下面开始显示提取元数据,其中有这些长的chunk_id。我们在第一课中介绍过如何将chunk_id连接回原始文档。


可视化解析结果

让我们用叠加的边界框来可视化这些解析结果。这与我们在第一课中所做的非常相似。这里我们将把五个文档中的每一个都保存为带注释的图像,然后查看这些图像。

例如,这是带注释的银行对账单(upload_c)。你可以看到几个文本块、两个主要的表格块以及所有单元格级别的定位。

再看一个,这是W-2税表。你可以看到年份非常醒目,工资(2323美元)在box1这里。

如果你正在构建一个文档处理系统(例如带有人类在环的系统),将人类的视线吸引到发现提取值的特定字段会非常有帮助。在这里,我们再次遍历所有文档,但只注释模式中出现的特定字段。

让我们看看upload_c(应该是银行对账单)。确实,现在我的注意力被吸引到了姓名出现的位置、总余额出现的位置以及对账单日期出现的位置。


创建申请摘要

回到我们作为接收所有这些贷款文件的人的场景,我们将希望创建某种最终摘要,代表该人提供的所有信息。

在这段代码中,我们将遍历之前所有的文档提取结果,并创建一个包含五列的表格作为Pandas DataFrame。

这是一个对申请人所提供信息的相当不错的摘要。我们有对应申请人的文件夹、文档名称、分类类型、请求的字段以及(屏幕外的)字段值。这绝对比单独打开所有这些文档、用眼睛寻找信息然后将其输入某种用户界面节省大量时间。


执行数据验证

正如我们之前提到的,这些文件上的姓名并不匹配,但这实际上是接收所有这些文件的人的关键职责。他们需要检查这些资产是否确实属于申请贷款的人,或者身份证明是否确实属于申请贷款的人,而不是上传了你邻居的文件。

以下是一个很好的验证示例,用于检查所有这些文档中的姓名字段是否匹配,并在不匹配时快速发现。你可以想象现在向申请人发送一个触发器或提醒电子邮件,说“这些文件不匹配,请检查你的工作”。

这是另一个验证示例。申请贷款时,你需要提供近期文件,因此检查所有这些文件是否来自同一年可能是合适的。其余代码主要专注于从其他格式的日期中提取年份。同样,由于它们是演示文档,这里混入了一些旧文件。它们肯定不是都来自当前年份。


计算总资产

你可能已经掌握了要领,但为了完整起见,让我们将所有银行余额和投资余额相加,以了解此人的总资产。在我们的示例中,只有一份银行对账单和一份投资对账单。但想象一下,如果该人的资产分散在各处,并且上传了10份不同的银行对账单,这至少可以将所有资产汇总成一个最终数字。


总结

本节课中我们一起学习了实验三的完整流程。在这个练习中,你完成了以下任务:

  1. 使用ADE API进行身份验证。
  2. 对混合的财务文档进行分类。
  3. 解析这些文档并提取其布局和内容。
  4. 定义对应于不同文档类型的自定义Pydantic模式。
  5. 提取带有视觉定位的结构化数据。
  6. 可视化这些结果。
  7. 最后,定义自定义验证逻辑以发现文档间的差异和错误。

这也标志着第四课和实验四的结束。现在,你已经有了使用单一智能体API的实践经验,该API无需用户的进一步指令即可理解你的文档。Landing AI的ADE真正让你能够专注于你的用例,而不是低级别的文档解析任务。其固有的智能体设计,基于视觉优先的模型架构,使其能够理解任何布局、任何语言的任何输入。

如果你想自己尝试更多,请获取你自己的API密钥。接下来在第五课中,我们将把它交还给David,讲解用于RAG的智能体化文档提取。

011:基于智能体化文档抽取构建RAG应用

在本节课中,我们将学习如何利用解析后的文档数据,构建一个检索增强生成(RAG)应用来查询PDF文件。我们将首先使用智能体化文档抽取(ADE)解析文档,然后将解析后的文本块存储到向量数据库中,最后通过检索这些信息来回答关于文档的问题。

概述

在之前的课程中,我们学习了文档AI的相关知识,包括OCR的局限性、如何有效利用文档布局、阅读顺序的重要性,以及Landing AI的智能体化文档抽取(ADE)如何通过统一的工作流处理文档的复杂性,输出干净、可追溯的结果。

现在,一个显而易见的问题是:我们如何处理所有这些结构化数据?一个可能的答案是构建真实的系统。具体来说,我们将构建一个检索增强生成(RAG)管道,将一个74页的财务申报文件转化为一个可查询的知识库。这是当今所有行业和领域中,驱动现代文档问答系统的通用架构模式。

真实场景:为对冲基金构建内部平台

想象你正在为一个对冲基金构建一个内部平台。分析师们需要处理像这份苹果公司10-K报表这样的SEC文件。这些文件包含了上市公司必须向证券交易委员会披露的详细财务和业务信息。

分析师希望将文件上传到平台,并提出诸如“苹果公司2023年的净销售额是多少?”、“最大的业务风险是什么?”、“服务收入同比趋势如何?”等问题。他们需要的信息就在文档中,可能在28页的表格里,可能在45页的脚注中,也可能分散在12、15、18页的三个不同风险披露部分。

传统关键词搜索为何失效

首先,存在语义不匹配问题。如果分析师搜索“revenue”,但文档中使用了“net sales”这个短语,传统搜索将一无所获。概念相同,但词语不完全匹配。

其次,存在上下文盲区。单词“revenue”可能在文档中出现75次。传统关键词搜索无法在没有额外指导的情况下,判断哪个提及与分析师的提问相关,这无法扩展。

第三,信息是碎片化的。要完整回答“风险是什么?”这个问题,需要综合多个页面的内容。传统搜索无法做到这一点。你需要语义理解,也需要上下文感知检索。这意味着你需要一个能理解用户意图,而不仅仅是匹配字符串的系统。

这就是RAG的用武之地,也是你将在本课实验中构建的内容。

什么是RAG及其重要性

检索增强生成(RAG)是当今几乎所有现代文档问答系统背后的架构。其管道包含三个核心阶段的六个步骤。

第一阶段是预处理阶段,包括解析、嵌入和存储。

  • 解析:将原始文档解析并提取为干净的结构化文本。这正是ADE发挥作用的地方。干净的输入至关重要,垃圾进,垃圾出。如果解析结果充满OCR错误和混乱的表格,下游所有环节都会受到影响。
  • 嵌入:将解析后的内容转换为嵌入向量,这些向量能捕捉语义信息。
  • 存储:将这些向量存储在针对相似性搜索优化的向量数据库中。我们将使用ChromaDB,这是一个非常适合开发和学习的本地开源选项。

第二阶段是检索阶段,包括查询和检索。

  • 查询:将用户的问题转换为嵌入向量。
  • 检索:在数据库中搜索,通过相似性度量找到排名最高的向量,即与查询最相似的向量。

第三阶段是生成阶段
将检索到的内容作为上下文提供给语言模型,以生成自然语言答案,并提供相应的检索内容用于追溯和验证。这对于金融服务、医疗保健和生命科学等受严格监管的组织至关重要。

这里有一个关键的见解,它联系了你所学的所有内容:ADE在第一阶段提供的干净、可追溯的输出,是整个管道得以实现的基础。如果你的解析不可靠,向系统输入的是扭曲的OCR输出或缺失了包含视觉叙事的表格、图表,那么任何巧妙的嵌入、提示工程或复杂的检索都无法挽救你。

在本课实验结束时,你将理解ADE的输出如何直接流入RAG系统,使用ChromaDB和OpenAI模型生成的嵌入构建一个本地RAG管道,在真实文档上运行语义搜索(而非关键词搜索),使用追溯图像可视化验证每个结果,并为在第6课中将此系统扩展和部署到AWS做好充分准备。

为何先在本地构建

你可能会想,为什么我们要在本地构建,而不是直接跳到AWS构建真正的生产系统?这是一个很好的问题,原因有三点。

第一,迭代更快。你可以更改代码,在两秒内重新运行一个单元格并查看结果,无需部署开销。在学习时,速度很重要。
第二,成本更低。本地实验在初始API调用后基本上是免费的。你不会在学习和调试时消耗云计算积分。
第三,学习更清晰。你可以剥离云计算的复杂性,专注于RAG机制和数据流本身。

在第6课中,我们将采用完全相同的逻辑,并在AWS上将其产品化。数据流在云规模上相同,但你将在本实验中打下基础。

具体来说,以下是实验中你将使用的内容:

  • 输入:上一财年的苹果公司10-K报表,一份74页的密集财务报告PDF,具有真实世界的复杂性。
  • 解析器:来自Landing AI的ADE API。输出已提供给你,因此你无需执行解析步骤。这个过程在之前与Andrea的课程中已详细介绍。
  • 输出:Markdown文本加上带有元数据的JSON块,是干净的结构化数据,已准备好进行嵌入。
  • 嵌入:将使用OpenAI的text-embedding-3-small模型生成。为了方便,我们使用其API,但你可以将其替换为任何开源替代方案。
  • 向量数据库:在实验环境中本地运行并具有持久存储的ChromaDB。

预处理第一步:使用ADE解析

当你使用ADE解析文档时,将得到一个解析响应对象。让我们分解其结构。

在顶层,你有parse_results.splits,这是一个列表,当你传递split=page参数时,每个页面对应一个分割项。

每个分割项有两个关键属性:

  1. .markdown:这是从该页面提取的干净Markdown文本。表格变成了带有竖线和短横线的Markdown表格,标题变成了带有井号的Markdown标题。这是人类可读的结构化文本,而不是一堵未经格式化的OCR文字墙。它也适合下游LLM用例。
  2. .chunks:这是一个chunk对象的列表。每个块是页面上的一个内容片段,可以是一个段落、一个表格、一个图形或一个标题。

对于每个块,你还有元数据:

  • chunk_id:唯一标识此内容片段的UUID。
  • text:提取的内容本身。
  • chunk_type:告诉你这是什么类型的内容(文本、表格、图形、证明、徽标等)。
  • bbox:边界框坐标,精确显示此块在页面上的位置。
  • page:块出现的页码。

重要的是,对于每个块,你都可以生成一个追溯图像,即原始PDF的视觉裁剪。这使你在构建管道时能够进行调试,并可以直观地验证提取是否正确。无需猜测。

当你的系统告诉用户“净销售额为3830亿美元”时,你可以向他们展示该信息来自的确切表格。他们可以亲眼看到,这为你的系统建立了巨大的信任。

在金融、医疗和法律等受监管的行业,你需要证明信息的来源。追溯图像为你提供了审计追踪,避免了幻觉。

由于你已经看过如何与Andrea一起调用解析API,我们将在实验中跳过这一步。你将直接获得这些ADE输出。

预处理第二步:嵌入ADE块的文本

你将把每个块的文本转换为一个大小为1536的嵌入向量。每个嵌入向量将从数学上编码解析文本的含义,使得语义相似的文本获得相似的向量。

因此,当你询问关于文档的问题时,你可以检索那些嵌入与问题嵌入相似的块。这称为块级嵌入

另一种选择是进行页面级嵌入,即嵌入整个页面的文本。让我们理解实践中的权衡:

  • 页面级:实现简单,嵌入和数据库构建更快,对于宽泛的问题和较短的文档可能有效。
  • 块级:提供更精确的检索(精确的表格/段落匹配),上下文更细粒度,对于复杂文档的聚焦问题更佳。借助ADE,我们甚至可以为复杂表格提供单元格级的追溯。

在实验中,你将使用块级方法,但在生产中,你可以根据用例进行调整。

预处理第三步:将向量存储到向量数据库

我们使用ChromaDB,因为它非常适合学习和原型设计。

  • 一行安装和持久存储:ChromaDB自动将向量保存到你的磁盘。如果你关闭笔记本明天再回来,你的数据仍然存在,无需重新嵌入所有内容。这对于高效迭代至关重要。
  • 快速相似性搜索:ChromaDB底层使用HNSW索引(分层可导航小世界图),这是一种用于近似最近邻搜索的先进算法,即使有数千个向量,核心检索也能在毫秒内完成。
  • 本地和生产环境API相同:ChromaDB有客户端-服务器模式。你在本地使用的相同API在生产中与远程ChromaDB服务器一起工作。这在扩展时无需改变思维模式,非常强大。
  • 丰富的元数据支持:对于每个块,你可以存储有助于过滤的元数据,允许你根据其元数据中的特定条件检索项目。

在实验中,你将把块类型、页码和边界框坐标作为每个块的元数据进行存储。当你向ChromaDB添加一个块时,需要指定一个ID,你将使用从ADE提供的确切chunk_id

请注意,在第6课中,我们将用AWS Bedrock知识库替换它,但概念保持相同。

构建检索函数

设置好向量数据库后,你将编写一个在检索步骤中使用的函数。让我们逐步了解当你针对“苹果公司2023年的净销售额是多少?”这个问题运行它时会发生什么。

  1. 嵌入问题:问题将被转换为嵌入向量,使用与预处理阶段相同的嵌入模型和向量大小。
  2. 搜索:你将把查询向量传递给ChromaDB,并说“找到与这个查询向量最接近的顶部块”。默认情况下,k=3,所以我们想要三个最相似的块。你可以根据用例调整此参数。
  3. 评分:ChromaDB还会返回距离度量。距离度量可以转换为相似度:相似度 = 1 - 距离。相似度越高意味着匹配越好。这是一个你可以调整的参数,以进一步微调你的RAG引擎。
  4. 过滤:然后,你将根据相似度阈值移除结果。之后,你可以进行可视化。
  5. 显示:最后,你将显示返回结果的块文本、ID、分数、页码和类型。你还将使用边界框的坐标来显示追溯图像。

追溯图像:建立信任的关键

这是ADE我最喜欢的功能之一,也是Landing AI区别于普通文档AI系统的地方。对于ADE提取的每个块,你都可以生成一个追溯图像。这是一个PNG文件,显示了原始PDF中该特定块的视觉裁剪。

让我告诉你为什么这在实践中如此关键。

  • 建立巨大信任:用户不只是盲目接受答案或被LLM幻觉愚弄。他们正在验证答案。当他们验证了几个答案并看到它们正确时,他们就会信任系统处理未来的查询。这确保了系统的采用。
  • 提供审计追踪:追溯图像为你提供了纸质记录,并有机会引入人工干预以降低风险。想象一下,一位财务分析师使用你的系统提取季度报告的数据。六个月后,如果审计员问“这个数字是从哪里来的?”,他们可以说“它来自Q3 10-K文件的第28页,表3,第5行,第6列。这是视觉证明。”这非常强大。

整合到完整的RAG管道

最后,你将把所学内容整合到一个完整的RAG管道中。为此,你将使用LangChain进行编排,利用一个预构建的链来结合我们RAG管道的检索和生成阶段。

你将从ChromaDB数据库创建一个检索器对象。检索器是LangChain的组件,用于获取信息并将其作为附加上下文插入提示中。有时,检索器可能会提供太多块,考虑到LLM有限的上下文窗口,LangChain可以将多个块组合到单个提示中,或者迭代地在多个提示中序列化许多块。

总结

在本节课中,我们一起学习了如何构建一个基于智能体化文档抽取(ADE)的检索增强生成(RAG)应用。我们从理解传统关键词搜索的局限性开始,介绍了RAG管道的三个核心阶段:预处理、检索和生成。我们详细探讨了使用ADE进行解析、将文本块嵌入为向量、使用ChromaDB进行存储和检索的步骤,并重点强调了ADE的追溯图像功能在建立信任和提供审计追踪方面的重要性。最后,我们概述了如何利用LangChain将所有这些组件整合成一个完整的、可查询文档的RAG系统。通过本课的学习,你已经为构建和扩展实用的文档智能应用打下了坚实的基础。

012:面向RAG的智能体化文档抽取 🧪

在本实验中,你将应用从文档中解析和提取信息的经验,构建一个检索增强生成(RAG)系统。实验结束时,你将实现一个能够回答文档相关问题的完整工作流程。

我们将使用前几节课中介绍过的一些库,包括 OpenAI、Langchain 和 Pillow。我们将使用 OpenAI 的两个模型:text-embedding-3-small 作为嵌入模型,用于将解析后的文本块转换为向量;以及 gpt-3.5-turbo 作为语言模型,用于根据检索到的信息生成答案。我们将使用 Chroma DB 作为向量数据库,并使用 Langchain 将数据库检索到的文本块连接到提示词中,供语言模型使用。我们还提供了一个实用工具,用于可视化生成答案时所使用的文本块。

让我们先看看本实验要处理的文档:苹果公司的 10-K 报告。这是一份公开报告,美国公司必须向美国证券交易委员会(SEC)等监管机构提交,以供审计。它提供了公司在整个财年内的详细业务信息。我们已经为你使用 Landing AI 的 ADE 工具处理了这份文档。

你可以在课程文件夹的 ade_outputs 目录下找到 ade_parse_results.mdade_parse_results.json 文件。回忆上一课的内容,解析会生成一个包含文档所有数据的 Markdown 文件,以及一个包含其元数据的 JSON 文件。让我们加载 Markdown 内容并预览第一页。

正如你在上一个实验中看到的,Markdown 文件包含 HTML 锚标签片段,这些标签指示了每个文本块的唯一标识符。我们可以使用这个 ID 将 Markdown 文件中的每个文本块链接到 JSON 文件中的元数据。

现在,让我们加载文本块并检查第一个块的结构。每个文本块有五个关键字段:

  • chunk_id:唯一标识符。
  • chunk_type:内容类型(文本、表格、图形等)。
  • text:实际内容,包括带有该块唯一 ID 的 HTML 锚标签。
  • bbox:文本块的边界框坐标。
  • page:该文本块来自哪一页。

边界框至关重要,这正是 Landing AI 的独特之处。每个文本块都能追溯到其在原始文档中的确切位置。当我们稍后检索一个文本块时,可以显示它来自哪里。

接下来,让我们设置向量数据库。如你所见,我们已经定义了数据存储位置。这个路径指向我们课程目录中一个名为 chroma_db 的文件夹,我们将集合命名为 ade_documents。这里将存储所有文档的嵌入向量。

然后,我们指定要使用的嵌入模型。这里我们使用 OpenAI 的 text-embedding-3-small 模型将文本转换为嵌入向量。

接着,这行代码实例化我们的 Chroma DB 客户端。注意,我们在这里使用了持久化客户端,这很重要,意味着我们的数据将保存到磁盘。

最后,这行代码创建我们的集合。我们使用 get_or_create_collection 方法,这非常方便。如果集合已存在,它会加载;如果不存在,则创建一个新的。至此,设置完成,我们可以开始用文本块填充数据库了。

我们已经为你将文本块加载到了向量数据库中。所以,如果你检查现有块的数量,会发现集合中已经有 453 个。但假设这是你第一次将 ADE 文本块加载到向量数据库集合中,以下是流程:

首先,我们提取文本并跳过任何空块。
然后,使用 OpenAI 的 API 生成嵌入向量。
接着,准备元数据,我们提取块类型、页码和边界框。
最后,将所有内容添加到 Chroma 集合中,包括文本、块 ID、元数据和刚生成的嵌入向量。

如你所见,由于文本块已预先加载,我们向集合中添加了 0 个新块。

现在数据库中有了数据和元数据,我们可以查询向量以检索相关的文本块。我们编写一个名为 rag_core 的函数来帮助我们完成这个任务。该函数接受四个参数:

  • question:用户的自然语言输入问题。
  • top_k:从向量搜索中返回的结果数量,默认为 3。
  • threshold:结果中包含的最小相似度分数,默认为 0.25。
  • show_images:是否显示来自原始 PDF 的可视化定位,默认为 True。

让我们分解一下这个函数的功能:

  1. 嵌入问题:使用与处理文本块相同的嵌入模型,将你的问题转换为向量。
  2. 查询数据库:查询 Chroma DB,找到与你的问题向量最接近的 top_k 个向量。
  3. 解析结果:从响应中提取文档、元数据、距离和 ID。
  4. 阈值过滤:根据阈值进行过滤,只显示高于最小相似度分数的结果。
  5. 可视化:使用我们的辅助工具,从原始 PDF 中提取并高亮显示文本块,以精确展示信息来源。

让我们测试一下查询功能。我们将询问苹果公司 2023 年的净销售额,这是我们之前用正则表达式尝试过的同一个问题。注意,我们设置 top_k=5 以获取更多结果,threshold=0.32 以过滤掉较弱的匹配项。

让我们看看这五个结果。问题是:“苹果公司 2023 年的净销售额是多少?” 请记住,这些是基于语义相似性的答案,因此可能不完美。

  • 第一个回复清晰地按产品和服务分类,给出了过去三年的净销售额明细。
  • 第二、第三和第五个结果提供了关于 2023 年净销售额的间接信息,但没有直接回答问题。
  • 第四个结果与第一个答案相同。

因此,在实际应用中,你可以调整参数以获得真正需要的答案。

如果你想问关于表格的问题呢?财务数据通常出现在许多表格中,这是混合搜索可以发挥作用的地方。

  1. 向量化问题:我们将用户输入“苹果公司 2023 年的总收入是多少?”进行向量化。我们仍然使用语义相似性,因此会将“总收入”的含义与“净销售额”进行匹配。
  2. 使用元数据过滤:在这种情况下,我们只查找 chunk_type 为“表格”的文本块。从结果中可以看到,Markdown 中包含了表格标签,这意味着我们正在从表格中提取信息。你可以随意使用辅助方法来可视化这个文本块,也可以尝试其他过滤器,如页码和块类型。

现在我们已经可以检索相关文本块,接下来可以将其整合到一个完整的 RAG 流程中。我们可以从 Chroma DB 数据库创建一个检索器对象,然后将其插入到 Langchain 链中。

接着,我们构建实际的 RAG 链,这需要三个组件:

  1. 提示词模板:我们定义一个系统提示词,指示 LLM 使用检索到的上下文来回答问题。context 占位符是 Langchain 将注入检索到的文本块的地方,input 是用户问题输入的地方。
  2. 语言模型:我们将初始化 LLM gpt-3.5-turbo,它将基于检索到的上下文生成答案。
  3. RAG 链:我们使用 Langchain 的 create_retrieval_chain 来连接检索器和我们的提示词与 LLM。这个链自动处理流程:接收用户问题 -> 检索相关文本块 -> 将其注入提示词 -> 生成答案。

我们可以通过直接检查检索到的文本来回答那个问题,但对于需要比较多个文本块信息的问题呢?例如,“比较 iPhone 销售额在 2022 年和 2023 年之间的总收入趋势”。这正是语言模型可以基于多个检索到的数据块进行推理的地方。

因此,除了简单的查找,你还可以使用这种方法:

  • 在多个文档中查找差异。
  • 获取法律文件中某个修正案的最新版本,而不仅仅是该修正案的所有版本。
  • 获取关于特定主题的、有来源依据的摘要。

你可以分析研究论文、报告和监管文件,以了解药物商业化的信息。

总而言之,通过结合嵌入技术、向量数据库和 RAG,你可以将原始提取的数据转化为来自多个文档和文件的准确见解,从而实现更快、更智能的文档理解。

在下一课中,我们将回顾如何在云端部署 ADE,通过事件驱动架构扩展我们的 RAG 应用,该架构能在新文档出现时自动触发 ADE 进行文档处理。我们下节课见。

013:在AWS上构建基于智能体化文档抽取的RAG管道 🚀

在本节课中,我们将学习如何将之前学到的文档处理工具和技术,在AWS云平台上付诸实践。我们将实现一个事件驱动的管道,自动触发文档解析,并将处理后的文档加载到知识库中,最终构建一个能够回答问题的智能体化RAG应用。

概述

上一节我们介绍了文档处理的核心工具。本节中,我们来看看如何将这些工具部署到AWS云上,构建一个生产就绪、可扩展的自动化系统。我们将使用无服务器架构和事件驱动设计,实现从文档上传到智能问答的完整流程。

从本地到云端:架构演进

在之前的课程中,RAG管道的前期阶段是在本地环境中完成的。以下是本地流程:

  • 原始文档存储在本地。
  • 文档被传递给本地运行的ADdeE解析器,使用本地计算和内存资源。
  • 解析后的输出被传递给嵌入器,我们使用了OpenAI的嵌入模型。
  • 嵌入向量最终被存入本地的ChromaDB数据库。

现在,我们将把所有本地组件替换为AWS服务。这将使管道具备云端的生产就绪能力,并能轻松应对海量文档的扩展需求。

以下是云端架构的对应变化:

  • 存储:将文档上传至S3桶(云对象存储),替代本地存储。
  • 计算:使用Lambda函数运行解析逻辑,替代本地机器资源。Lambda提供了云端无服务器的隔离计算环境,并会在新文档上传到S3时自动触发。
  • 嵌入与向量数据库:使用Amazon Bedrock服务,它提供了无服务器的嵌入模型和知识库功能。

对于用户查询部分,之前我们使用LangChain定义了一个从向量数据库提取信息的检索对象。在AWS上,我们将使用Strands Agent(一个原生支持AWS的开源生产级智能体框架)来构建智能体,并为其配备连接到知识库的检索工具。我们还将使用Amazon Bedrock为智能体提供LLM访问权限,以及Amazon Bedrock Agent Core Memory,以便创建能够记住用户交互上下文的智能体。

核心架构与概念解析

架构总览 🏗️

我们的架构包含基于AI的组件(如Landing AI的ADdeE解析器和Strands Agent框架)以及以下主要的AWS组件:Amazon S3、AWS Lambda和Amazon Bedrock。

之前提到AWS Lambda和Amazon Bedrock提供无服务器服务。无服务器意味着两件事:

  1. 你无需配置或管理任何服务器。AWS负责基础设施和安全更新。
  2. 你只需为代码实际运行的时间付费,空闲时间不计费。

这也意味着AWS会根据需求自动扩展你的应用,无论是有10个并发用户还是10000个。由于无需配置基础设施,你可以快速原型化和部署新功能。

事件驱动架构 ⚡

我们架构的第二个特点是包含事件触发组件。当新文档上传到S3时,ADdeE解析器会自动运行。这是如何实现的呢?

这被称为事件驱动架构,一种现代设计模式,其中系统是解耦的,并通过发送和接收事件(本质上是通知某事已发生)进行通信。

让我将其分解为三个步骤:

  1. 事件生产者:这些是像S3桶这样的组件,当相关操作发生时发出事件。在你的案例中,当你上传文件到S3时,它会发出一个“文件已上传”事件。
  2. 事件通道或代理:像Amazon EventBridge这样的服务,将这些事件路由给感兴趣的各方。可以把它看作一个通知系统,知道谁需要被告知什么。AWS上还有其他消息服务,如用于发布/订阅模式的Amazon SNS和用于队列的Amazon SQS,它们支持更简单的模式。EventBridge更适合具有更复杂路由的事件驱动架构。
  3. 事件消费者:像AWS Lambda这样的服务,订阅并对特定事件做出反应。你的包含ADdeE解析器的Lambda函数正在监听那个“文件已上传”事件,并在事件到达时自动开始处理。

一个有用的类比是:与其不断“拉取”(例如,每隔几秒检查S3是否有新文件),事件驱动的方法更像是“推送”通知。S3在有趣事件发生的瞬间通知Lambda,Lambda立即做出反应。

因此,当你将文档上传到S3时,S3创建一个事件,EventBridge路由它,你的Lambda函数自动运行ADdeE解析器。无需手动触发。

深入AWS核心组件 🔧

现在,让我们分解系统的每个组件,了解其具体角色。我们有三个主要的AWS组件:Amazon S3、AWS Lambda和Amazon Bedrock。让我们深入了解每一个。

1. Amazon S3 (简单存储服务)

Amazon S3是AWS提供的可扩展对象存储服务,适用于任何类型和大小的文件。可以把它想象成一个无限的数字文件柜,可以从互联网任何地方访问。无论你添加多少文件,它永远不会耗尽空间。这将作为你数据的中央存储库。

以下是S3的使用方式:

  • 你将原始输入数据(如PDF、图像或文本文件)存储在专用的S3桶中。
  • 然后,你将AI处理的结果(摘要、解析后的文档、转换后的数据)保存回这些桶内有组织的文件夹中。

快速说明:是你在S3中创建用于组织文件的顶级容器,类似于你电脑上的主文件夹。

2. AWS Lambda

AWS Lambda是一项无服务器计算服务,无需你管理任何服务器即可运行代码。可以把它想象成一个兼职的机器人助手,或一个按需函数,它并非一直运行,只在需要时才启动行动。

Lambda执行小段代码以响应特定事件,如S3上传、API调用或计划触发器。在你的案例中,当文档上传到S3时,Lambda将自动运行你的ADdeE解析逻辑。你只需为消耗的计算时间付费(以毫秒计)。因此,如果你的Lambda函数只运行了2毫秒,你只需支付那2毫秒的计算费用。

为了让Lambda访问其他AWS服务(如从S3读取),它需要适当的权限。这就是IAM(身份和访问管理)的用武之地。IAM是控制谁或什么可以访问你AWS资源的安全框架。

当你创建Lambda函数时,需要设置两样东西:一个IAM角色和一个IAM策略。让我解释一下区别:

  • 角色代表“谁”可以访问。可以把角色想象成一个工牌或职位头衔。它是你的Lambda函数在运行时承担的身份。角色向其他AWS服务证明:“我是S3到Bedrock处理器函数”或“我是文档解析器”。像Lambda这样的服务没有用户名或密码,相反,它们承担角色来向其他AWS服务证明其身份。
  • IAM策略则规定“它能做什么”。角色本身并不保证任何权限,这就是策略的作用。可以把策略看作写在该工牌上的规则列表。它精确指定了该角色被允许执行的操作。策略是一个JSON文档,定义了诸如s3:GetObject(意味着你可以从S3读取文件)或s3:PutObject(意味着你可以写入文件到S3)等操作。

总结一下:当你创建Lambda函数时,你必须创建一个IAM角色(身份/工牌),将一个IAM策略(规则)附加到该角色,并将该角色分配给你的Lambda函数。关键要点是:角色定义它是谁,策略规定它能做什么

3. Amazon Bedrock

Amazon Bedrock是一项完全托管的服务,通过单一API提供对基础模型(如AWS的Claude或Nova等大语言模型)以及嵌入模型的访问。可以把它看作一个预训练AI模型的菜单,你无需从头开始训练和托管自己的模型,只需选择需要的模型并使用它。

Amazon Bedrock为你架构的三个关键部分提供支持:

  1. 知识库:这是你的解析文档被自动嵌入并存储为向量的地方。Bedrock处理嵌入(将文本转换为数值表示)并提供语义搜索能力,以便你的智能体可以检索相关信息。
  2. 智能体运行时:Bedrock为你的智能体的推理和响应提供支持的基础模型。当你的智能体收到查询时,它使用Bedrock的LLM生成智能、有根据的答案。
  3. Agent Core Memory (智能体核心内存):这是存储你智能体内存的地方,包括对话历史、用户偏好和语义事实。它允许你的智能体记住跨交互的上下文,使其感觉更像一个真正的助手。

请记住,Bedrock本质上是无服务器的,因此它会自动扩展,并且你只需为实际使用量付费。

智能体组件:Strands Agent 🤖

现在,让我们来看看你架构中的智能体组件:Strands Agent。它是一个开源SDK。

Strands Agent是一个AWS开源框架,专门设计用于在笔记本和生产环境中构建智能体。它有助于简化编排:

  • 无缝集成:Strands Agent与AWS资源(如S3、Bedrock和其他工具)无缝集成,无需复杂的手动编码。
  • 简化配置:它有助于创建智能体定义。使用Strands Agent,你可以指定使用哪些Bedrock模型、你的智能体可以访问哪些工具以及内存如何工作。这使得你的智能体配置清晰、可维护且易于修改。
  • 企业就绪:最后但同样重要的是,它是企业就绪的。Strands Agent是生产级的,它内置了用于跟踪和日志记录、性能监控和错误处理的工具,并且支持灵活的部署模式。

端到端流程演练 🔄

现在让我们看看所有这些组件如何协同工作。让我们一步步走过这个过程。

步骤1:上传文档
在实验中,你将处理医学研究论文。你将一个PDF上传到S3的input/medical文件夹。S3上传事件触发你的Lambda函数。Lambda运行ADdeE解析器来解析和结构化内容。解析后的输出以两种格式上传回S3的output/medical文件夹:一个用于解析内容的Markdown文件,以及一个包含用于视觉定位的块信息(如块类型和边界框坐标)的JSON文件。这一切都是自动发生的。你只需上传文件,事件驱动架构会处理其余部分。

步骤2:将解析后的文档摄取到知识库
你开始将Markdown文件从S3的output/medical文件夹摄取到知识库中。Amazon Bedrock读取文件,为每个文本块生成嵌入,并将它们存储在向量数据库中。一旦摄取完成,你的知识库就变得可搜索了。智能体可以查询这个知识库来检索相关信息。请注意,你也可以为实现从S3到知识库的自动摄取任务实现一个单独的Lambda函数,但在实验中,为简化起见,我们只实现一个用于解析的Lambda函数。

步骤3:创建基于知识的搜索工具
这个工具将你的智能体连接到知识库。当智能体需要从你的文档中获取信息时,它会调用这个工具,该工具查询向量数据库并返回最相关的内容。这使你的智能体能够基于你上传的文档回答问题。

步骤4:设置智能体内存
Amazon Bedrock Agent Core Memory提供三种类型的长期内存:

  1. 用户偏好:存储喜好、厌恶和个人背景。
  2. 语义内存:存储事实、实体和关系。
  3. 摘要内存:存储对话摘要和关键点。

重要的是,内存会在会话间持久化,因此你的智能体可以记住过去的交互并提供个性化的响应。

步骤5:构建智能体本身
你将配置系统提示(关于智能体应如何行为的指令)、你创建的基于知识的搜索工具、由Agent Core Memory启用的内存,以及托管在Bedrock上的LLM模型。一旦一切配置完成,你的智能体就可以与用户交互了。

步骤6:与你的智能体聊天
这是一个例子。用户可能会说:“我喜欢金枪鱼寿司。”智能体可能回应:“明白了。我记住了这个偏好。”在以后的会话中,你可能会问:“我今天午餐应该吃什么?”智能体可能回应:“寿司怎么样?你提到过你喜欢金枪鱼。”关键特性是智能体记住了你。它不仅仅是在一个会话中回答孤立的问题,而是在构建上下文并随时间了解你的偏好。

总结

本节课中,我们一起学习了如何在AWS上构建一个完整的、基于智能体化文档抽取的RAG管道。我们了解了如何利用Amazon S3进行文档存储,使用AWS Lambda实现事件驱动的无服务器计算来自动解析文档,并借助Amazon Bedrock的知识库、LLM和智能体内存功能,通过Strands Agent框架构建一个能够记忆上下文、进行个性化交互的智能体。这套架构结合了无服务器的弹性伸缩、事件驱动的自动化以及智能体的上下文感知能力,为构建生产级的文档智能应用提供了强大而灵活的解决方案。

现在,是时候切换到实验笔记本,开始动手构建了!

014:使用Strands Agents构建研究论文聊天机器人

概述

在本实验中,我们将构建一个完整的文档智能处理流水线。该流水线部署在AWS上,结合了Landing AI的自动化文档解析功能与对话式AI智能体。我们将学习如何设置Lambda函数、配置S3触发器、将文档索引到知识库,并最终创建一个具备记忆和视觉溯源能力的智能聊天机器人。


实验架构与数据流回顾

在上一节中,我们介绍了系统的整体架构。本节中,我们来详细看看数据在系统中的流动过程。

  1. 用户将PDF文件上传到S3存储桶的input文件夹。
  2. 当新文件到达时,S3会自动触发一个Lambda函数。
  3. Lambda函数使用Landing AI API将PDF解析为结构化的Markdown文本。
  4. 解析后的Markdown、视觉溯源数据以及独立的文本块(chunk)被保存到S3的output文件夹。
  5. Bedrock知识库为文档建立索引,以支持语义搜索。
  6. 用户向具备记忆功能的Strands Agent提问,以维持对话上下文。

实验前提条件

本实验假设您已具备一个包含inputoutput文件夹的S3存储桶,以及一个连接到S3output/medical_chunks文件夹的Bedrock知识库。如果您希望亲自尝试本实验,我们将提供相关链接,指导您如何创建AWS账户、设置这些资源并学习AWS基础知识。


第一步:安装所需软件包

以下是本实验将用到的核心Python包及其作用:

  • boto3:官方的AWS Python SDK,用于以编程方式与AWS服务交互。
  • python-dotenv:从.env文件加载敏感配置信息。
  • pymupdf:用于为PDF页面添加高亮注释,实现视觉溯源。
  • Pillow:将PDF页面渲染为图像。
  • bedrock-agent-core:为智能体提供记忆管理功能。
  • strands-agent:一个用于构建AI智能体的框架。

安装命令如下:

pip install boto3 python-dotenv pymupdf Pillow bedrock-agent-core strands-agent

第二步:配置环境变量与AWS客户端

为了安全地管理凭证,我们将从.env文件加载环境变量,而不是将其硬编码在笔记本中。

以下是一个.env文件的示例模板:

AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_REGION=us-east-1
LANDINGAI_API_KEY=your_landingai_key

环境配置完成后,我们需要建立与所需AWS服务的连接。boto3库通过创建“客户端”来实现这一点。

我们将为以下服务创建客户端:

  • S3客户端:用于上传PDF、下载输出文件和管理存储桶。
  • Lambda客户端:用于部署Lambda函数、更新代码和配置触发器。
  • IAM客户端:用于为Lambda函数创建具有适当权限的角色。
  • Logs客户端:用于通过CloudWatch日志监控Lambda执行和调试错误。
  • Bedrock Agent Runtime客户端:用于查询知识库以进行文档搜索。
  • Bedrock Runtime客户端:用于直接调用云端模型。

第三步:构建完整流水线路线图

我们的AWS客户端已准备就绪,现在可以开始构建完整的流水线。以下是我们的实现路线图:

第一部分:设置Lambda函数(步骤3-5)
我们将分三步设置Lambda函数:

  1. 打包代码:将Python文件及其依赖项捆绑到一个zip文件夹中。
  2. 创建角色:定义允许Lambda访问S3以下载和上传文件的权限。
  3. 部署函数:将打包好的代码上传到AWS Lambda。

第二部分:设置触发器(步骤6)
接下来,我们将配置S3,使其在上传新文件到input文件夹时自动调用我们的Lambda函数。

第三部分:构建智能体(步骤7-12)
最后,我们将上传医学研究论文PDF,将解析后的文档索引到Bedrock知识库,并使用Strands Agent构建一个智能体。

为了保持本教程专注于核心概念,我们已在lambda_helpers.py中创建了辅助函数来处理底层的AWS操作。我们将在实验过程中解释每个辅助函数的逻辑。


第四步:创建部署包(步骤3)

什么是Lambda部署包?要创建AWS Lambda函数,您需要将源代码及其所有依赖项捆绑到一个zip文件中。这个包包含了Lambda运行代码所需的一切。

部署包包含:

  • 您的源代码ade_s3_handler.py,其中包含Lambda函数被调用时执行的ADE解析逻辑。
  • 安装的依赖项:您的源代码导入的所有pip包。

以下是部署包的结构示例:

deployment_package.zip
├── ade_s3_handler.py
├── landingai/
├── boto3/
└── ...其他依赖项

我们将使用create_deployment_package辅助函数来构建此包。该函数在后台执行以下操作:创建一个临时目录,使用pip将包安装到该目录,将您的源代码文件复制到临时目录,从该目录中的所有内容创建zip文件,并清理临时目录。

在进入下一步之前,让我们了解一下在Lambda内部运行的代码逻辑。下图展示了完整的流程:

以下是逐步发生的情况:

  1. 接收事件:当PDF文件上传到S3的input文件夹时,S3事件会触发Lambda函数。
  2. ADE处理函数:从事件中提取文件键(key)。
  3. 检查与验证:处理函数检查是否为PDF文件、跳过文件夹,并验证输出是否已存在。
  4. 下载PDF:将PDF下载到Lambda的临时目录。
  5. 调用ADE API:将PDF发送到ADE API进行解析,返回Markdown文本和文本块。
  6. 上传结果:将结果以三种格式上传到S3的output文件夹:
    • 一个Markdown文件,用于存储解析后的内容。
    • 一个包含所有文本块信息(用于视觉溯源)的JSON文件。
    • 独立的文本块JSON文件,用于优化知识库索引。

为了帮助您理解每个输出文件的内容,假设您上传了input/medical_research_paper.pdf,在ADE处理完成后,S3的output文件夹中将包含:

  • markdown/:包含完整文档的可读格式,其中包含链接文本到块ID的锚点标签。
  • grounding_json/:一个包含所有文本块及其边界框坐标等元数据的单一文件。
  • medical_chunks/:每个文本块一个文件,针对向量数据库索引进行了优化。每个文件都自包含文本、位置和源元数据。

由于我们正在实现一个RAG(检索增强生成)流水线,我们将专注于仅使用output/medical_chunks文件夹进行Bedrock知识库索引和生成注释图像。其他文件夹可用于不同的实验和下游用例。

第五步:创建IAM角色(步骤4)

什么是IAM角色?Lambda函数在默认没有任何权限的隔离容器中运行。IAM角色授予函数使用临时凭证访问特定AWS服务的权限。

我们将使用create_or_update_lambda_role辅助函数来创建一个具有Lambda函数所需权限的角色。该角色包括以下权限:

  • S3权限GetObject(从输入文件夹读取PDF)、PutObject(将Markdown文件写入输出文件夹)、HeadObject(检查输出文件夹是否已存在)。
  • 日志权限CreateLogGroup(为此函数创建CloudWatch日志组)、CreateLogStream(为每次执行创建日志流)、PutLogEvents(写入日志条目以进行调试)。

第六步:部署Lambda函数(步骤5)

现在我们已经拥有了所需的两部分:部署包(我们的代码)和IAM角色(我们的权限)。让我们使用deploy_lambda_function辅助函数来部署Lambda函数。

部署还包括一些重要的配置选项:

  • 环境变量:我们的代码在运行时可以访问的配置值。
  • 超时:最大执行时间设置为900秒(15分钟),用于处理较大的PDF。
  • 内存大小:分配的RAM量,我们将设置为1024 MB。

第七步:设置S3触发器(步骤6)

我们的Lambda函数已部署,但还不会自动运行。我们需要告诉S3在文件上传时触发Lambda函数。S3可以在对象被创建、修改或删除时向Lambda发送事件。我们将配置为当文件上传到input文件夹时调用我们的函数。setup_s3_trigger辅助函数处理此配置。

第八步:上传文档进行处理(步骤7)

基础设施现已准备就绪。现在,让我们上传我们的医学PDF文档,并观察流水线的运行。下图展示了PDF文件如何从您本地的medical文件夹流向S3输入存储桶,被自动触发的Lambda函数使用ADE处理,并产生三种类型的输出文件。

我们将使用upload_folder_to_s3辅助函数上传本地文档。当Lambda函数处理我们的文档时,我们可以实时监控其进度。此辅助函数监视CloudWatch日志以显示处理状态。Lambda会自动写入日志,我们只需要读取它们。要停止监控,请按Esc键,然后双击I。您可以选择显示所有输出文件,但在本视频中,我们直接按Note

第九步:连接到Bedrock知识库(步骤8)

我们的文档现已解析并存储在S3中。下一步是通过将它们摄取到Bedrock知识库中使其可搜索。知识库是一个支持语义搜索的向量数据库。

首先,让我们验证我们的知识库是否可用且配置正确。我们使用Bedrock Agent客户端列出所有知识库及其数据源。请注意,知识库已在AWS控制台中预先配置,指向我们的S3 output/medical_chunks文件夹作为数据源,使用Amazon Titan创建向量嵌入,并将向量存储在OpenSearch Serverless中以进行快速相似性搜索。此信息未在此处打印,但我想让您了解我们在本实验中使用的配置。

第十步:将文档摄取到知识库(步骤9)

现在,让我们将解析后的文档从S3同步到知识库中。这个过程称为“摄取”。

摄取过程中会发生什么?

  1. 知识库读取S3 output/medical_chunks文件夹中所有新的或修改过的JSON文件。
  2. 它为每个文本块创建向量嵌入。
  3. 这些向量被存储在数据库中,以便进行快速相似性搜索。

摄取完成后,我们可以用自然语言问题查询知识库,并找到最相关的文档部分。start_ingestion_job API启动异步处理,它立即返回作业ID,实际工作在后台进行。

第十一步:创建具备视觉溯源的搜索工具(步骤10)

我们的文档已在知识库中建立索引,现在可以为我们的智能体创建一个搜索工具。但我们要添加一些特别的东西:Landing AI的视觉溯源功能。

下图展示了我们搜索工具的代码逻辑:

让我带您了解这个逻辑。该工具遵循以下模式:
当用户提交一个查询(例如“什么有助于缓解感冒症状?”)时:

  1. 检索:通过查询知识库(使用混合搜索,结合关键词匹配和语义相似性)来检索相关结果。
  2. 检查:对于每个结果,检查它是否来自medical_chunks文件夹的块JSON文件。
  3. 解析:解析块JSON以获取元数据,如块ID、块类型、页码和边界框。
  4. 生成图像:动态生成裁剪后的块图像,并将其上传到S3,返回一个预签名URL。
  5. 格式化响应:智能体将格式化响应,为您提供来源、块ID、页码、块类型、裁剪图像URL和内容。

在创建智能体之前,让我们验证一下我们的搜索工具是否正常工作。一个简单的测试查询知识库并显示第一个结果。让我们搜索“common cold symptoms”。您可以看到知识库搜索工作正常。我们现在可以打印这些测试结果。当您点击预签名URL时,您可以看到为可追溯性和可审计性动态创建的块图像。对于受严格监管的组织和高风险操作,这可以整合到您的RAG系统或任何下游应用程序中。

第十二步:为智能体创建记忆(步骤11)

我们的搜索工具正在工作。现在,让我们为智能体创建记忆,以便它能记住对话并随着时间学习用户偏好。AWS Bedrock Agent Core提供了三种记忆策略,每种服务于不同的目的:

  • 会话摘要:总结过去的会话。
  • 用户偏好:随着时间学习用户偏好。
  • 语义记忆:提取和存储事实。

我们将配置所有三种策略,为智能体提供全面的记忆能力。我们将首先检查是否已为智能体创建了记忆,否则将创建一个新的记忆。创建记忆后,我们需要设置一个会话管理器,为每次对话组织信息。每次对话需要两个标识符:

  • 参与者ID:谁在使用智能体,这支持跨会话的个性化。
  • 会话ID:此特定对话的唯一标识符。

第十三步:创建Strands智能体(步骤12)

我们现在已准备好所有组件:一个具备视觉溯源的搜索工具,以及用于在对话间维护上下文的记忆。让我们将所有内容整合到一个Strands智能体中。

智能体配置如下:

  • 模型:使用Claude via Bedrock作为底层的LLM。
  • 系统提示:定义智能体个性和行为的指令。
  • 会话管理器:用于记住偏好、历史摘要和事实的记忆。
  • 工具:我们之前创建的搜索知识库函数。

请注意,系统提示明确指示智能体在其响应中包含视觉溯源信息,如页码、位置坐标和注释图像。

第十四步:交互式聊天(步骤13)

您的医学文档智能体现在已准备就绪。让我们启动一个交互式聊天会话。让我们从“维生素C治疗感冒的效果如何?”开始。这里显示它使用了search_knowledge_base工具,并返回了一个症状列表。以及信息源,我们可以在其中找到注释图像。

我现在告诉它我更喜欢简短的回答,然后退出。让我们再次运行它并问同样的问题。您可以看到,它根据我之前的偏好返回了更简洁的答案。要退出,请输入exitquitbye


总结

恭喜您完成本实验!您已经构建了一个完整的、可用于生产的文档智能处理流水线,其中包含以下关键组件:

  • 自动化文档处理:当PDF上传到S3时自动解析的Lambda函数。
  • 语义搜索:用于智能文档检索的Bedrock知识库。
  • 视觉溯源:提供精确页码位置和高亮图像的可追溯答案。
  • 对话记忆:能记住偏好和对话历史的智能体。
  • 独立文本块存储:为更好的知识库索引而优化的块文件。

您现在可以扩展此流水线以处理其他文档类型(如Excel、PowerPoint甚至图像),为智能体添加更多工具,或随着需求增长与其他AWS服务集成。

感谢您付出的时间、展现的好奇心和参与的热情。我们非常期待看到您接下来将创造什么。下次见!😊

015:总结 🎯

在本课程中,我们学习了如何构建一个智能体化的文档处理系统,使你能够解析和提取PDF等文件中的信息。

课程内容回顾 📚

上一节我们探讨了如何将应用迁移到云端并使其事件驱动。现在,让我们对整个课程的核心内容进行总结。

以下是我们在本课程中学习的关键步骤与技术:

  1. 从图像到文本的转换:你学习了如何通过OCR(光学字符识别)技术将图像文件转换为可处理的文本。

    # 示例:使用OCR库提取文本
    text = ocr_engine.extract_text(image_file)
    
  2. 布局分析与逻辑排序:你探索了如何使用边界框(Bounding Boxes)分析文档的物理布局,并将识别出的文本块按逻辑顺序进行重组。

  3. 视觉语言模型的应用:你使用了视觉语言模型(VLMs)来理解文本及其在文档中的上下文关系。

  4. 智能体化文档理解:你采用了ADE(一种“视觉优先”的智能体生成方法)来处理复杂文档,该方法优先考虑视觉结构。

  5. 构建检索增强生成应用:你基于以上技术,构建了一个RAG(检索增强生成)应用程序,用于回答关于非结构化文档的问题。

  6. 云端部署与事件驱动:最后,你探索了如何将应用程序迁移到云端,并将其改造为事件驱动的架构,以提高可扩展性和响应能力。

总结与展望 🌟

本节课中,我们一起学习了从基础的OCR文本提取,到利用智能体进行高级文档理解和构建问答应用的完整流程。你掌握了处理非结构化文档并将其转化为可查询知识的核心技能。

我期待看到你运用这些知识,构建出属于自己的创新应用。🚀

posted @ 2026-03-26 08:18  布客飞龙II  阅读(0)  评论(0)    收藏  举报