Elasticsearch:使用 Azure AI 文檔智能解析 PDF 文本和表格數據

作者:來自 Elastic?James Williams

了解如何使用 Azure AI 文檔智能解析包含文本和表格數據的 PDF 文檔。

Azure AI 文檔智能是一個強大的工具,用于從 PDF 中提取結構化數據。它可以有效地提取文本和表格數據。提取的數據可以索引到 Elastic Cloud Serverless,以支持 RAG(Retrieval Augmented Generation - 檢索增強生成)。

在這篇博客中,我們將通過攝取四份最新的 Elastic N.V. 季度報告來演示 Azure AI 文檔智能的強大功能。這些 PDF 文檔的頁數從 43 頁到 196 頁不等,每個 PDF 都包含文本和表格數據。我們將使用以下提示測試表格數據的檢索:比較/對比 Q2-2025、Q1-2025、Q4-2024 和 Q3-2024 的訂閱收入?

這個提示比較復雜,因為它需要來自四個不同 PDF 的上下文,這些 PDF 中的相關信息以表格格式呈現。

讓我們通過一個端到端的參考示例來了解,這個示例由兩個主要部分組成:

Python 筆記本

  1. 下載四個季度的 Elastic N.V. 10-Q 文件 PDF
  2. 使用 Azure AI 文檔智能解析每個 PDF 文件中的文本和表格數據
  3. 將文本和表格數據輸出到 JSON 文件
  4. 將 JSON 文件攝取到 Elastic Cloud Serverless

Elastic Cloud Serverless

  1. 為 PDF 文本 + 表格數據創建向量嵌入
  2. 為 RAG 提供向量搜索數據庫查詢
  3. 預配置的 OpenAI 連接器用于 LLM 集成
  4. A/B 測試界面用于與 10-Q 文件進行對話

前提條件

此筆記本中的代碼塊需要 Azure AI Document Intelligence 和 Elasticsearch 的 API 密鑰。Azure AI Document Intelligence 的最佳起點是創建一個 Document Intelligence 資源。對于 Elastic Cloud Serverless,請參考入門指南。你需要 Python 3.9+ 來運行這些代碼塊。

創建 .env 文件

將 Azure AI Document Intelligence 和 Elastic Cloud Serverless 的密鑰放入 .env 文件中。

AZURE_AI_DOCUMENT_INTELLIGENCE_ENDPOINT=YOUR_AZURE_RESOURCE_ENDPOINT
AZURE_AI_DOCUMENT_INTELLIGENCE_API_KEY=YOUR_AZURE_RESOURCE_API_KEYES_URL=YOUR_ES_URL
ES_API_KEY=YOUR_ES_API_KEY

安裝 Python 包

!pip install elasticsearch python-dotenv tqdm azure-core azure-ai-documentintelligence requests httpx

創建輸入和輸出文件夾

import osinput_folder_pdf = "./pdf"
output_folder_pdf = "./json"folders = [input_folder_pdf, output_folder_pdf]def create_folders_if_not_exist(folders):for folder in folders:os.makedirs(folder, exist_ok=True)print(f"Folder '{folder}' created or already exists.")create_folders_if_not_exist(folders)

下載 PDF 文件

下載四個最近的 Elastic 10-Q 季度報告。如果你已經有了 PDF 文件,可以將它們放在 ‘./pdf’ 文件夾中。

import os
import requestsdef download_pdf(url, directory='./pdf', filename=None):if not os.path.exists(directory):os.makedirs(directory)response = requests.get(url)if response.status_code == 200:if filename is None:filename = url.split('/')[-1]filepath = os.path.join(directory, filename)with open(filepath, 'wb') as file:file.write(response.content)print(f"Downloaded {filepath}")else:print(f"Failed to download file from {url}")print("Downloading 4 recent 10-Q reports for Elastic NV.")
base_url = 'https://s201.q4cdn.com/217177842/files/doc_financials'
download_pdf(f'{base_url}/2025/q2/e5aa7a0a-6f56-468d-a5bd-661792773d71.pdf',      filename='elastic-10Q-Q2-2025.pdf')
download_pdf(f'{base_url}/2025/q1/18656e06-8107-4423-8e2b-6f2945438053.pdf', filename='elastic-10Q-Q1-2025.pdf')
download_pdf(f'{base_url}/2024/q4/9949f03b-09fb-4941-b105-62a304dc1411.pdf', filename='elastic-10Q-Q4-2024.pdf')
download_pdf(f'{base_url}/2024/q3/7e60e3bd-ff50-4ae8-ab12-5b3ae19420e6.pdf', filename='elastic-10Q-Q3-2024.pdf')

使用 Azure AI Document Intelligence 解析 PDF

在解析 PDF 文件的代碼塊中有很多內容。以下是簡要總結:

  1. 設置 Azure AI Document Intelligence 導入和環境變量
  2. 使用 AnalyzeResult 解析 PDF 段落
  3. 使用 AnalyzeResult 解析 PDF 表格
  4. 結合 PDF 段落和表格數據
  5. 通過對每個 PDF 文件執行 1-4 步,整合所有結果并將其存儲為 JSON

設置 Azure AI Document Intelligence 導入和環境變量

最重要的導入是 AnalyzeResult。這個類表示文檔分析的結果,并包含關于文檔的詳細信息。我們關心的細節包括頁面、段落和表格。

import os
from azure.core.credentials import AzureKeyCredential
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.ai.documentintelligence.models import AnalyzeResult
from azure.ai.documentintelligence.models import AnalyzeDocumentRequest
import json
from dotenv import load_dotenv
from tqdm import tqdmload_dotenv()AZURE_AI_DOCUMENT_INTELLIGENCE_ENDPOINT =  os.getenv('AZURE_AI_DOCUMENT_INTELLIGENCE_ENDPOINT')
AZURE_AI_DOCUMENT_INTELLIGENCE_API_KEY = os.getenv('AZURE_AI_DOCUMENT_INTELLIGENCE_API_KEY')

使用 AnalyzeResult 解析 PDF 段落

從每個頁面提取段落文本。不要提取表格數據。

def parse_paragraphs(analyze_result):table_offsets = []page_content = {}for paragraph in analyze_result.paragraphs:  for span in paragraph.spans:if span.offset not in table_offsets:for region in paragraph.bounding_regions:page_number = region.page_numberif page_number not in page_content:page_content[page_number] = []page_content[page_number].append({"content_text": paragraph.content})return page_content, table_offsets

使用 AnalyzeResult 解析 PDF 表格

從每個頁面提取表格內容。不要提取段落文本。這個技術最有趣的副作用是,無需轉換表格數據。LLM 知道如何讀取看起來像 “單元格 [0, 1]:表格數據……” 的文本。

def parse_tables(analyze_result, table_offsets):page_content = {}for table in analyze_result.tables:table_data = []for region in table.bounding_regions:page_number = region.page_numberfor cell in table.cells:for span in cell.spans:table_offsets.append(span.offset)table_data.append(f"Cell [{cell.row_index}, {cell.column_index}]: {cell.content}")if page_number not in page_content:page_content[page_number] = []page_content[page_number].append({"content_text": "\n".join(table_data)})return page_content

結合 PDF 段落和表格數據

在頁面級別進行預處理分塊以保留上下文,這樣我們可以輕松手動驗證 RAG 檢索。稍后,你將看到,這種預處理分塊不會對 RAG 輸出產生負面影響。

def combine_paragraphs_tables(filepath, paragraph_content, table_content):page_content_concatenated = {}structured_data = []# Combine paragraph and table contentfor p_number in set(paragraph_content.keys()).union(table_content.keys()):concatenated_text = ""if p_number in paragraph_content:for content in paragraph_content[p_number]:concatenated_text += content["content_text"] + "\n"if p_number in table_content:for content in table_content[p_number]:concatenated_text += content["content_text"] + "\n"page_content_concatenated[p_number] = concatenated_text.strip()# Append a single item per page to the structured_data listfor p_number, concatenated_text in page_content_concatenated.items():structured_data.append({"page_number": p_number,"content_text": concatenated_text,"pdf_file": os.path.basename(filepath)})return structured_data

把所有內容結合在一起

打開 ./pdf 文件夾中的每個 PDF,解析文本和表格數據,并將結果保存為 JSON 文件,該文件包含 page_numbercontent_textpdf_file 字段。content_text 字段表示每個頁面的段落和表格數據。

pdf_files = [os.path.join(input_folder_pdf, file)for file in os.listdir(input_folder_pdf)if file.endswith(".pdf")
]document_intelligence_client = DocumentIntelligenceClient(endpoint=AZURE_AI_DOCUMENT_INTELLIGENCE_ENDPOINT, credential=AzureKeyCredential(AZURE_AI_DOCUMENT_INTELLIGENCE_API_KEY),connection_timeout=600 
)for filepath in tqdm(pdf_files, desc="Parsing PDF files"):with open(filepath, "rb") as file:poller = document_intelligence_client.begin_analyze_document("prebuilt-layout",AnalyzeDocumentRequest(bytes_source=file.read()))analyze_result: AnalyzeResult = poller.result()paragraph_content, table_offsets = parse_paragraphs(analyze_result)table_content = parse_tables(analyze_result, table_offsets)structured_data = combine_paragraphs_tables(filepath, paragraph_content, table_content)# Convert the structured data to JSON formatjson_output = json.dumps(structured_data, indent=4)# Get the filename without the ".pdf" extensionfilename_without_ext = os.path.splitext(os.path.basename(filepath))[0]# Write the JSON output to a fileoutput_json_file = f"{output_folder_pdf}/{filename_without_ext}.json"with open(output_json_file, "w") as json_file:json_file.write(json_output)

加載數據到 Elastic Cloud Serverless

以下代碼塊處理:

  1. 設置 Elasticsearch 客戶端和環境變量的導入
  2. 在 Elastic Cloud Serverless 中創建索引
  3. ./json 目錄中的 JSON 文件加載到 pdf-chat 索引中

設置 Elasticsearch 客戶端和環境變量的導入

最重要的導入是 Elasticsearch。這個類負責連接到 Elastic Cloud Serverless,創建并填充 pdf-chat 索引。

import json
from dotenv import load_dotenv
from elasticsearch import Elasticsearch
from tqdm import tqdm
import osload_dotenv()ES_URL = os.getenv('ES_URL')
ES_API_KEY = os.getenv('ES_API_KEY')es = Elasticsearch(hosts=ES_URL,api_key=ES_API_KEY, request_timeout=300)

在 Elastic Cloud Serverless 中創建索引

此代碼塊創建一個名為 “pdf_chat” 的索引,并具有以下映射:

  • page_content - 用于通過全文搜索測試 RAG

  • page_content_sparse - 用于通過稀疏向量測試 RAG

  • page_content_dense - 用于通過密集向量測試 RAG

  • page_number - 對于構建引用很有用

  • pdf_file - 對于構建引用很有用

注意使用了 copy_tosemantic_textcopy_to 工具將 body_content 復制到兩個語義文本(semantic_text)字段。每個語義文本字段都映射到一個 ML 推理端點,一個用于稀疏向量,一個用于密集向量。由 Elastic 提供的 ML 推理會自動將每頁分成 250 個 token 的塊,并有 100 個 token 的重疊。

index_name= "pdf-chat"
index_body = {"mappings": {"properties": {"page_content": {"type": "text", "copy_to": ["page_content_sparse","page_content_dense"]},"page_content_sparse": {"type": "semantic_text", "inference_id": ".elser-2-elasticsearch"},"page_content_dense": {"type": "semantic_text", "inference_id": ".multilingual-e5-small-elasticsearch"},"page_number": {"type": "text"},"pdf_file": {"type": "text", "fields": {"keyword": {"type": "keyword"}}}}}
}if es.indices.exists(index=index_name):es.indices.delete(index=index_name)print(f"Index '{index_name}' deleted successfully.")response = es.indices.create(index=index_name, body=index_body)
if 'acknowledged' in response and response['acknowledged']:print(f"Index '{index_name}' created successfully.")
elif 'error' in response:print(f"Failed to create: '{index_name}'") print(f"Error: {response['error']['reason']}")
else:print(f"Index '{index_name}' already exists.")

將 JSON 文件從 ./json 目錄加載到 pdf-chat 索引

此過程將花費幾分鐘時間,因為我們需要:

  • 加載 402 頁 PDF 數據

  • 為每個 page_content 塊創建稀疏文本嵌入

  • 為每個 page_content 塊創建密集文本嵌入

files = os.listdir(output_folder_pdf)
with tqdm(total=len(files), desc="Indexing PDF docs") as pbar_files:for file in files:with open(output_folder_pdf + "/" + file) as f:data = json.loads(f.read())with tqdm(total=len(data), desc=f"Processing {file}") as pbar_pages:for page in data:doc = {"page_content": page['content_text'],"page_number": page['page_number'],"pdf_file": page['pdf_file']}id = f"{page['pdf_file']}_{page['page_number']}"es.index(index=index_name, id=id, body=json.dumps(doc))pbar_pages.update(1)pbar_files.update(1)

最后還有一個代碼技巧需要提到。我們將通過以下命名約定設置 Elastic 文檔 ID:FILENAME_PAGENUMBER。這樣可以方便地查看與引用關聯的 PDF 文件和頁面號碼,在 Playground 中進行驗證。

Elastic Cloud Serverless

Elastic Cloud Serverless 是原型化新 Retrieval-Augmented Generation (RAG) 系統的絕佳選擇,因為它提供了完全托管的可擴展基礎設施,避免了手動集群管理的復雜性。它開箱即用地支持稀疏和密集向量搜索,使你能夠高效地實驗不同的檢索策略。借助內置的語義文本嵌入、相關性排名和混合搜索功能,Elastic Cloud Serverless 加速了搜索驅動應用程序的迭代周期。

借助 Azure AI Document Intelligence 和一些 Python 代碼,我們準備好了看看是否能讓 LLM 在真實數據的基礎上回答問題。讓我們打開 Playground,并使用不同的查詢策略進行一些手動 A/B 測試。

這個查詢將返回與全文搜索匹配的前十頁內容。

全文搜索差不多能夠提供正確的答案,但僅能提供四個季度中的三個季度的正確答案。這是可以理解的,因為我們將十整頁的數據塞入了 LLM 的上下文中。而且,我們沒有利用語義搜索。

這個查詢將返回匹配我們查詢的頁面中,使用強大的稀疏向量搜索的前兩個語義文本片段。

由 Elastic 的 ELSER 提供支持的稀疏向量搜索在從所有四個 PDF 文件中檢索表格數據方面做得非常好。我們可以通過打開與每個引用相關的 PDF 頁面號碼輕松核對答案。

Elastic 還提供了一個出色的稠密向量選項用于語義文本(E5)。E5 非常適用于多語言數據,并且在每秒高查詢量的用例中具有更低的推理延遲。這個查詢將返回與我們的用戶輸入匹配的前兩個語義文本片段。結果與稀疏搜索相同,但請注意兩個查詢的相似之處。唯一的區別是 “field” 名稱。

混合搜索

ELSER 對于這個用例非常有效,以至于我們不需要混合搜索。但是,如果我們想要,我們可以將稠密向量搜索和稀疏向量搜索結合到一個查詢中。然后,使用 RRF(Reciprocal rank fusion - 倒排排名融合)對結果進行重新排序。

我們學到了什么?

Azure AI Document Intelligence

  • 非常擅長解析 PDF 文件中的文本和表格數據。
  • 與 Elasticsearch Python 客戶端集成良好。

Elastic Serverless Cloud

  • 在數據攝取和查詢時,內置了用于稀疏和稠密向量嵌入的 ML 推理。
  • 擁有強大的 RAG A/B 測試工具,可用于確定最佳的檢索技術以適應特定的用例。

還有其他技術和方法可以用來解析 PDF 文件。如果你的組織完全使用 Azure,這種方法可以提供一個優秀的 RAG 系統。

想要獲得 Elastic 認證嗎?了解下次 Elasticsearch 工程師培訓的時間!

Elasticsearch 提供了許多新特性,幫助你為你的用例構建最佳的搜索解決方案。深入了解我們的示例筆記本,開始免費云試用,或者立即在本地機器上嘗試 Elastic。

原文:Parse PDF text and table data with Azure AI Document Intelligence - Elasticsearch Labs

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899640.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899640.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899640.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【ArcGIS操作】ArcGIS 進行空間聚類分析

ArcGIS 是一個強大的地理信息系統(GIS)軟件,主要用于地理數據的存儲、分析、可視化和制圖 啟動 ArcMap 在 Windows 中,點擊“開始”菜單,找到 ArcGIS文件夾,然后點擊 ArcMap 添加數據 添加數據 - 點擊工具…

RabbitMQ消息相關

MQ的模式: 基本消息模式:一個生產者,一個消費者work模式:一個生產者,多個消費者訂閱模式: fanout廣播模式:在Fanout模式中,一條消息,會被所有訂閱的隊列都消費。 在廣播…

緩存使用紀要

一、本地緩存:Caffeine 1、簡介 Caffeine是一種高性能、高命中率、內存占用低的本地緩存庫,簡單來說它是 Guava Cache 的優化加強版,是當下最流行、最佳(最優)緩存框架。 Spring5 即將放棄掉 Guava Cache 作為緩存機…

2025年3月29日筆記

問題&#xff1a;創建一個長度為99的整數數組&#xff0c;輸出數組的每個位置數字是幾&#xff1f; 解題思路&#xff1a; 1.因為題中沒有明確要求需要輸入,所以所有類型的答案都需要寫出 解法1&#xff1a; #include<iostream> #include<bits/stdc.h> using n…

hadoop相關面試題以及答案

什么是Hadoop&#xff1f;它的主要組件是什么&#xff1f; Hadoop是一個開源的分布式計算框架&#xff0c;用于處理大規模數據的存儲和計算。其主要組件包括Hadoop Distributed File System&#xff08;HDFS&#xff09;和MapReduce。 解釋HDFS的工作原理。 HDFS采用主從架構&…

微信小程序:數據拼接方法

1. 使用 concat() 方法拼接數組 // 在原有數組基礎上拼接新數組 Page({data: {originalArray: [1, 2, 3]},appendData() {const newData [4, 5, 6];const combinedArray this.data.originalArray.concat(newData);this.setData({originalArray: combinedArray});} }) 2. 使…

Python之貪心算法

Python實現貪心算法(Greedy Algorithm) 概念 貪心算法是一種在每一步選擇中都采取當前狀態下最優的選擇&#xff0c;從而希望導致結果是全局最優的算法策略。 基本特點 局部最優選擇&#xff1a;每一步都做出當前看起來最佳的選擇不可回退&#xff1a;一旦做出選擇&#xf…

【 <二> 丹方改良:Spring 時代的 JavaWeb】之 Spring Boot 中的 AOP:實現日志記錄與性能監控

<前文回顧> 點擊此處查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、開篇整…

TCP/IP協議簇

文章目錄 應用層http/httpsDNS補充 傳輸層TCP1. 序列號與確認機制2. 超時重傳3. 流量控制&#xff08;滑動窗口機制&#xff09;4. 擁塞控制5. 錯誤檢測與校驗6. 連接管理總結 網絡層ARP**ARP 的核心功能**ARP 的工作流程1. ARP 請求&#xff08;Broadcast&#xff09;2. ARP 緩…

SpringBoot分布式項目訂單管理實戰:Mybatis最佳實踐全解

一、架構設計與技術選型 典型分布式訂單系統架構&#xff1a; [網關層] → [訂單服務] ←→ [分布式緩存]↑ ↓ [用戶服務] [支付服務]↓ ↓ [MySQL集群] ← [分庫分表中間件]技術棧組合&#xff1a; Spring Boot 3.xMybatis-Plus 3.5.xShardingSpher…

微服務架構中的精妙設計:環境和工程搭建

一.前期準備 1.1開發環境安裝 Oracle從JDK9開始每半年發布?個新版本, 新版本發布后, ?版本就不再進?維護. 但是會有?個?期維護的版本. ?前?期維護的版本有: JDK8, JDK11, JDK17, JDK21 在 JDK版本的選擇上&#xff0c;盡量選擇?期維護的版本. 為什么選擇JDK17? S…

Maven 構建配置文件詳解

Maven 構建配置文件詳解 引言 Maven 是一個強大的項目管理和構建自動化工具,廣泛應用于 Java 開發領域。在 Maven 項目中,配置文件扮演著至關重要的角色。本文將詳細介紹 Maven 構建配置文件的相關知識,包括配置文件的作用、結構、配置方法等,幫助讀者更好地理解和應用 M…

【YOLO系列】基于YOLOv8的無人機野生動物檢測

基于YOLOv8的無人機野生動物檢測 1.前言 在野生動物保護、生態研究和環境監測領域&#xff0c;及時、準確地檢測和識別野生動物對于保護生物多樣性、預防人類與野生動物的沖突以及制定科學的保護策略至關重要。傳統的野生動物監測方法通常依賴于地面巡邏、固定攝像頭或無線傳…

Hive UDF開發實戰:構建高性能JSON生成器

目錄 一、背景與需求場景 二、開發環境準備 2.1 基礎工具棧 2.2 Maven依賴配置 三、核心代碼實現

分布式特性對比

以下是關于 分片(Sharding)、一致性哈希、兩階段提交(2PC)、Paxos、Raft協議、數據局部性 的對比分析與關聯性總結,涵蓋核心機制、適用場景及相互關系: 一、概念對比與關聯 概念核心目標關鍵特性典型應用場景與其它技術的關聯分片(Sharding)數據水平拆分按規則(哈希、…

歷史分鐘高頻數據

外盤期貨高頻分鐘歷史回測行情數據下載 鏈接: https://pan.baidu.com/s/1RUbAMxfiSyBlXfrwT_0n2w?pwdhgya 提取碼: hgya通過美國期貨高頻交易所歷史行情可以看到很多細節比如品種之一&#xff1a;FGBX_1min (1)在2024-02-29 11:14:00關鍵交易時刻&#xff0c;一筆大規模訂單突…

final+模版設計模式的理解

模板設計模式在 Java 里是一種行為設計模式&#xff0c;它在抽象類里定義算法的骨架&#xff0c;把部分步驟的具體實現延遲到子類。如此一來&#xff0c;子類可以在不改變算法結構的基礎上&#xff0c;重新定義算法中的特定步驟。 模式組成 抽象類&#xff08;Abstract Class…

JAVA接口調用限速器

目錄 1、并發限速 2、串行限速 需求&#xff1a;批量調用第三方ERP接口&#xff0c;對方接口限流時&#xff0c;減緩調用速率。 1、并發限速 Slf4j RestController public class ApiCallTask {//第三方接口Resourceprivate ErpService erpService;//異步線程池Resourcepriv…

STM32 CAN控制器硬件資源與用法

1、硬件結構圖 以STM32F4為例&#xff0c;他有2個can控制器&#xff0c;分別為 CAN1 CAN2。 每個CAN控制器&#xff0c;都有3個發送郵箱、2個接收fifo&#xff0c;每個接收fifo又由3個接收郵箱組成。也即每個CAN控制器都有9個郵箱&#xff0c;其中3個供發送用&#xff0c;3個…

【C++ 繼承】—— 青花分水、和而不同,繼承中的“明明德”與“止于至善”

歡迎來到ZyyOvO的博客?&#xff0c;一個關于探索技術的角落&#xff0c;記錄學習的點滴&#x1f4d6;&#xff0c;分享實用的技巧&#x1f6e0;?&#xff0c;偶爾還有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原創??&#xff0c;感謝支持??&#xff01;請尊重原創&#x1…