当前位置: 首页 > news >正文

提取标准 OCR 遗漏的图表数据:Elastic Agent Builder 和 LlamaParse 在一个管道中

作者:来自 Elastic Jeffrey Rengifo

构建一个端到端流水线,该流水线从复杂 PDF文档 中提取结构化数据(包括图表中的数值),并将其导入 Elasticsearch ,支持使用 ES|QL 的 agent 查询就绪。

Agent Builder 现在已正式发布。通过注册 Elastic Cloud Trial 开始使用,并查看 Agent Builder 文档 这里。


企业级文档很难处理。包含多栏表格、扫描页面、混合布局以及嵌入图表的 PDF 会让简单的文本提取失效。标准处理流水线会遗漏这些文档中隐藏的结构化数据,这意味着你的 agent 在基于不完整上下文进行推理。大多数 agent 框架使用基础 OCR 工具处理 PDF,它们在纯文本文档中表现尚可,但在更复杂场景下表现不足。

为了解决这个问题,你需要一个系统,能够从这些文档中准确提取结构化数据,并自动将其索引,以便 agent 使用。Elastic Agent Builder 和 LlamaParse Extract API 的组合覆盖了数据处理与推理两个环节。LlamaParse Extract 负责文档复杂性处理:它将 schema 定义的提取模型应用到原始 PDF 上,并返回结构化 JSON。Elastic Agent Builder 负责编排:通过 Elastic Workflows 调用 LlamaParse Extract API,将结果写入 Elasticsearch,并让 agent 能够通过 Elasticsearch Query Language (ES|QL) 或语义查询对这些数据进行推理。

本文将讲解完整实现流程,从 schema 定义到可运行的 agent。完整代码可在配套 notebook 中查看 companion notebook。

何时应该使用 LlamaParse Extract 而不是 LlamaParse?

当你需要返回结构化的 JSON 具体字段时,应使用 LlamaParse Extract。当你需要完整文档内容用于 RAG pipeline 时,应使用 LlamaParse。

LlamaCloud 提供两种文档处理工具。LlamaParse 将文档转换为 LLM-ready 格式(如 Markdown),保留完整文档布局。它用于 RAG(Retrieval Augmented Generation)pipeline,当你希望将整个文档内容送入向量库或 LLM 上下文窗口时使用。LlamaParse Extract 则相反,它基于开发者定义的 schema,只返回你指定的字段,并以校验过的 JSON 形式输出。它还可以提取图表、图形以及文档中的视觉元素数据,这是标准文本解析完全无法覆盖的能力。

LlamaParse / LlamaParse Extract
输出格式Markdown(完整文档)结构化 JSON(基于 schema 定义的字段)
最适合场景RAG pipelines,LLM 上下文窗口搜索索引、数据库、类型化字段
是否支持图表/图形
输入PDFPDF + 开发者定义的 schema

对于这个用例来说,Extract 是更好的选择。我们需要具体的数值(GDP 占比百分比、投资增长率)以及叙述性总结,并将其作为类型化的 Elasticsearch 字段进行索引。schema 会告诉提取模型具体要寻找什么,包括那些只存在于 PDF 可视化图表中的数值。

如果你的用例是将完整文档内容输入到 RAG pipeline,那么 LlamaParse 是正确工具。如果你需要用于搜索索引或数据库的结构化记录,尤其是当数据存在于图表或表格中时,应使用 Extract。

LlamaParse Extract 和 Elastic Agent Builder 流水线做了什么?

这个流水线的工作方式如下:用户发送一个问题,并附带一个 PDF 的 URL。agent 会调用一个 workflow 工具,将 PDF 上传到 LlamaParse,通过 LlamaParse Extract API 执行基于 schema 的结构化提取,然后把结构化结果索引到 Elasticsearch 中。

然后 agent 会使用 ES|QL 对该索引进行查询,以回答这个问题。

为了演示这一点,我们使用 世界银行全球经济展望(2026年1月) 报告作为源文档。该 agent 将能够直接从该 PDF 中回答关于前沿市场、经济指标以及政策建议的问题。

如何为 LlamaParse Extract 构建一个 Elastic Workflows 工具

Workflow tool 在 Elastic Agent Builder 中作为基于 YAML 定义的自动化运行。当被触发时,它会执行六个步骤。关于将 Agent Builder 和 Workflows 连接起来 的通用流程,该文章有更详细的搭建说明。这里我们重点讲 LlamaParse Extract 的特定集成方式。

  1. 上传 PDF:从提供的 URL 将 PDF 上传到 LlamaCloud。该 workflow 接收pdf_url作为输入;你的 LlamaCloud API key、project ID 和 configuration ID 作为常量进行配置。

  2. 创建提取任务:使用已保存的配置,在已上传文件上启动 Extract v2 任务。

  3. 等待:暂停 15 秒,给 LlamaParse Extract 留出处理文档的时间。

  4. 轮询直到完成:while循环中每 10 秒轮询一次提取状态,直到状态变为COMPLETED,最多循环 19 次(约 3 分钟上限)。

  5. 索引:将 PDF 中所有提取字段作为单个 Elasticsearch 文档写入。

  6. 验证:使用document_id以及查询 term 查询 在 Elasticsearch 中执行搜索,以确认文档已成功索引。

前置条件

  • Elasticsearch 9.3+,并启用 Workflows

  • LlamaParse Cloud 账号,并具备 API key API key

  • Python 3.1x

使用 Elastic Agent Builder 实现 LlamaParse Extract:分步说明

下面的代码展示了实现的关键步骤。完整版本(包含所有细节)请参考配套 notebook companion notebook。

配置环境

import os import json from dotenv import load_dotenv load_dotenv() ELASTICSEARCH_URL = os.getenv("ELASTICSEARCH_URL") ELASTICSEARCH_API_KEY = os.getenv("ELASTICSEARCH_API_KEY") LLAMA_CLOUD_API_KEY = os.getenv("LLAMA_CLOUD_API_KEY") KIBANA_URL = os.getenv("KIBANA_URL")

定义提取 schema

LlamaParse Extract 是基于 schema 驱动的。你需要定义一个 Pydantic 模型,用来描述希望提取的字段,而 LlamaParse Extract 会基于该模型从原始 PDF 中引导提取。这正是它在复杂文档中保持可靠性的关键。与其 “期望” LLM 找到正确的值,不如明确告诉它需要找什么。

如上所述,agent 需要处理两类问题:

  • 结构化问题:“2025 年前沿市场 GDP 占比是多少?” 这类问题需要可用 ES|QL 过滤的数值字段。

  • 探索性问题:“前沿市场的主要风险是什么?” 这类问题需要可进行语义检索的叙述性文本字段。

这个 schema 只捕捉支持这两类问题所需的最小信息集合。description字段不是文档说明,而是对提取模型的指令,因此越具体,提取质量越高。其中两个字段(frontier_market_gdp_share_pctfrontier_market_investment_growth_2020s_pct)直接来自报告中的柱状图(图 A 和图 C),这些数据是标准文本提取工具无法捕获的。

class EconomicReportSummary(BaseModel): report_title: str = Field(description="Full title of the report") publication_date: str = Field( description="Publication date in YYYY-MM format, e.g. '2026-01'" ) frontier_market_gdp_share_pct: float = Field( description="Frontier markets' share of global GDP in 2025 as a percentage, " "extracted from Figure ES.A bar chart" ) frontier_market_investment_growth_2020s_pct: float = Field( description="Average annual per capita investment growth for frontier markets " "in the early 2020s (2020-24), extracted from Figure ES.C bar chart, as a percentage" ) executive_summary: str = Field( description="Concise summary of the report's main findings and conclusions" ) key_vulnerabilities: str = Field( description="Main vulnerabilities and risks facing frontier markets, as a paragraph" ) policy_recommendations: str = Field( description="Key policy recommendations for frontier market policymakers, as a paragraph" )

创建 Extract 配置

LlamaExtract v2 用 “已保存配置(saved configurations)” 替代了旧的 extraction agents,本质上是一个可复用的参数集合,用来将你的 schema 与 extraction tier 绑定在一起。首先获取你的 project ID,然后通过POST创建配置。保存打印出来的两个 ID,因为你会在 workflow YAML 中用到它们。

import requests LLAMA_CLOUD_BASE_URL = "https://api.cloud.llamaindex.ai" llama_headers = { "Authorization": f"Bearer {LLAMA_CLOUD_API_KEY}", "Content-Type": "application/json", } # Fetch the project ID (uses the first available project) projects_response = requests.get( f"{LLAMA_CLOUD_BASE_URL}/api/v1/projects", headers=llama_headers, ) projects_response.raise_for_status() PROJECT_ID = projects_response.json()[0]["id"] print(f"Project ID: {PROJECT_ID}") # Create a saved Extract v2 configuration with our schema config_response = requests.post( f"{LLAMA_CLOUD_BASE_URL}/api/v1/beta/configurations", headers=llama_headers, params={"project_id": PROJECT_ID}, json={ "name": "global-economic-extractor", "parameters": { "product_type": "extract_v2", "data_schema": EconomicReportSummary.model_json_schema(), "extraction_target": "per_doc", "tier": "agentic", }, }, ) config_response.raise_for_status() CONFIGURATION_ID = config_response.json()["id"] print(f"Configuration ID: {CONFIGURATION_ID}")

创建 Elasticsearch 索引

创建索引时,需要使用与 extraction schema 对应的 mapping。数值字段(例如frontier_market_gdp_share_pct)使用 number 类型,以支持 ES|QL 结构化过滤。publication_date字段使用 date 类型,以支持时间范围查询。叙述性字段(例如executive_summarykey_vulnerabilities)使用 text 类型,用于 全文检索。标识类字段(例如report_title)使用 keyword 类型。完整 mapping 定义可在 notebook 中查看。

构建 Agent Builder workflow 工具

将下面的 YAML 复制并粘贴到 Elastic UI 中的Elasticsearch > Workflows > Create a new Workflow。Elastic Workflows 文档 覆盖了完整 YAML schema 以及可用的 step types。

该 workflow 使用 LlamaCloud 的 REST API:通过 Files API(/api/v1/files/upload_from_url)从公开 URL 上传 PDF;并通过 Extract v2 API(/api/v2/extract)创建任务并轮询结果(/api/v2/extract/{id})。

name: LlamaParse Extract Economic Report Processor description: > Uploads a PDF from a URL to LlamaCloud, runs Extract v2, and indexes the structured results into Elasticsearch. enabled: true inputs: - name: pdf_url type: string description: Public URL of the PDF to process required: true consts: indexName: economic-reports llamaBaseUrl: https://api.cloud.llamaindex.ai projectId: <YOUR_PROJECT_ID> configurationId: <YOUR_CONFIGURATION_ID> documentId: global-economic-prospects-jan-2026 llamaCloudApiKey: llx-YOUR-API-KEY-HERE triggers: - type: manual steps: # Upload PDF from URL to LlamaCloud - name: upload_pdf type: http with: url: "{{ consts.llamaBaseUrl }}/api/v1/files/upload_from_url" method: PUT headers: Authorization: "Bearer {{ consts.llamaCloudApiKey }}" Content-Type: application/json body: | { "url": "{{ inputs.pdf_url }}" } # Create the Extract v2 job using our saved configuration - name: create_extraction_job type: http with: url: "{{ consts.llamaBaseUrl }}/api/v2/extract?project_id={{ consts.projectId }}" method: POST headers: Authorization: "Bearer {{ consts.llamaCloudApiKey }}" Content-Type: application/json body: | { "file_input": "{{ steps.upload_pdf.output.data.id }}", "configuration_id": "{{ consts.configurationId }}" } - name: wait_for_extraction type: wait with: duration: "15s" # Poll every 10s until status is COMPLETED (max ~3 min) - name: poll_until_done type: while condition: 'not steps.poll_get.output.data.status : "COMPLETED"' max-iterations: limit: 19 on-limit: fail steps: - name: poll_wait type: wait with: duration: "10s" - name: poll_get type: http with: url: "{{ consts.llamaBaseUrl }}/api/v2/extract/{{ steps.create_extraction_job.output.data.id }}?project_id={{ consts.projectId }}" method: GET headers: Authorization: "Bearer {{ consts.llamaCloudApiKey }}" Accept: application/json - name: index_extracted_data type: elasticsearch.index with: index: "{{ consts.indexName }}" id: "{{ consts.documentId }}" document: report_title: "{{ steps.poll_get.output.data.extract_result.report_title }}" publication_date: "{{ steps.poll_get.output.data.extract_result.publication_date }}" frontier_market_gdp_share_pct: "{{ steps.poll_get.output.data.extract_result.frontier_market_gdp_share_pct }}" frontier_market_investment_growth_2020s_pct: "{{ steps.poll_get.output.data.extract_result.frontier_market_investment_growth_2020s_pct }}" executive_summary: "{{ steps.poll_get.output.data.extract_result.executive_summary }}" key_vulnerabilities: "{{ steps.poll_get.output.data.extract_result.key_vulnerabilities }}" policy_recommendations: "{{ steps.poll_get.output.data.extract_result.policy_recommendations }}" refresh: wait_for - name: verify_document type: elasticsearch.search with: index: "{{ consts.indexName }}" query: term: _id: "{{ consts.documentId }}"

用你自己的llamaCloudApiKeyprojectIdconfigurationId更新consts部分。

与 Agent Builder 连接

保存 workflow 之后,使用 Agent Builder Kibana API 或 UI 创建两个工具和一个 agent。

import requests headers = { "Authorization": f"ApiKey {ELASTICSEARCH_API_KEY}", "kbn-xsrf": "true", "Content-Type": "application/json", } WORKFLOW_ID = "workflow-abcabc-0073-4a08-98a8-werwer" # Copy from UI after creating the workflow # Create the workflow tool workflow_tool_payload = { "id": "run_llamaextract_workflow", "type": "workflow", "description": ( "Triggers the LlamaParse Extract extraction workflow. " "Use this tool to extract structured data from a PDF URL and index it into Elasticsearch. " "Requires only the public URL of the PDF to process." ), "tags": ["llama-extract", "workflow"], "configuration": { "workflow_id": WORKFLOW_ID, }, } response = requests.post( f"{KIBANA_URL}/api/agent_builder/tools", headers=headers, json=workflow_tool_payload, ) print(f"Workflow tool: {response.status_code}") # Create the structured indicators query tool structured_query_payload = { "id": "query_structured_indicators", "type": "esql", "description": ( "Query structured economic indicators using exact filters on numeric fields. " "Use this for questions like 'Which reports show frontier market GDP share below 5%?' " "or 'Show investment growth for reports published after 2025-01'." ), "tags": ["economic-data", "llama-extract"], "configuration": { "query": ( "FROM economic-reports " "| WHERE frontier_market_gdp_share_pct <= ?max_gdp_share " "| KEEP report_title, publication_date, " "frontier_market_gdp_share_pct, frontier_market_investment_growth_2020s_pct " "| SORT publication_date DESC " "| LIMIT 10" ), "params": { "max_gdp_share": { "type": "double", "description": "Maximum frontier market GDP share percentage to filter by", } }, }, } response = requests.post( f"{KIBANA_URL}/api/agent_builder/tools", headers=headers, json=structured_query_payload, ) print(f"Structured query tool: {response.status_code}") # Create the narrative text search tool text_search_payload = { "id": "search_economic_narratives", "type": "esql", "description": ( "Search narrative content in economic reports. " "Use this for open-ended questions like 'What are the main risks for frontier markets?' " "or 'What does the report recommend for policymakers?'." ), "tags": ["economic-data", "llama-extract"], "configuration": { "query": ( "FROM economic-reports " "| WHERE MATCH(executive_summary, ?query) " "OR MATCH(key_vulnerabilities, ?query) " "OR MATCH(policy_recommendations, ?query) " "| KEEP report_title, executive_summary, key_vulnerabilities, policy_recommendations " "| LIMIT 5" ), "params": { "query": { "type": "keyword", "description": "The search query to find relevant narrative content", } }, }, } response = requests.post( f"{KIBANA_URL}/api/agent_builder/tools", headers=headers, json=text_search_payload, ) print(f"Text search tool: {response.status_code}") # Create the agent agent_payload = { "id": "economic-report-analyst", "name": "Economic Report Analyst", "description": "Extracts and analyzes economic reports from PDFs using LlamaParse Extract and Elasticsearch.", "labels": ["economics", "llama-extract"], "configuration": { "instructions": ( "You are an economic research assistant. You have three tools:\n" "1. run_llamaextract_workflow: Use this FIRST to extract and index data from a PDF.\n" "2. query_structured_indicators: Use this for structured questions that filter on " "numeric fields like GDP share or investment growth.\n" "3. search_economic_narratives: Use this for open-ended questions about risks, " "vulnerabilities, or policy recommendations.\n" "When presenting data, use clear formatting with bullet points or tables. " "Always cite the report title and publication date." ), "tools": [ { "tool_ids": [ "run_llamaextract_workflow", "query_structured_indicators", "search_economic_narratives", ] } ], }, } response = requests.post( f"{KIBANA_URL}/api/agent_builder/agents", headers=headers, json=agent_payload, ) print(f"Agent: {response.status_code}")

workflow ID 可以在 URL 中找到:

https://4622216ea8cd443ead5bef0a3de05135.us-central1.gcp.cloud.es.io/app/workflows/<WORKFLOW-ID>

通过 Agent Builder 测试 workflow

配置好 “经济报告分析员” agent 后,打开 Agent Builder 聊天窗口,并发送如下消息(请替换为你自己的实际 ID):

Process this PDF and extract its data: <your-pdf-url> Once extracted, answer: What are the main vulnerabilities facing frontier markets and what policy recommendations does the report suggest to address them?

Agent 会首先调用run_llamaextract_workflow,等待 workflow 完成,然后再使用search_economic_reports来检索并总结提取后的数据。

Process this PDF and extract its data: https://diaphysial-rafael-unscrupulously.ngrok-free.dev/Markets-Executive-Summary.pdf Once extracted, answer: What are the main vulnerabilities facing frontier markets and what policy recommendations does the report suggest to address them?

提示:在实际的使用中,https://raw.githubusercontent.com/Delacrobix/notebook-llamaindex-agent-builder/main/Markets-Executive-Summary.pdf 这个文件的路径可能导致失败。一种做法就是使用如下的命令在本地构建一个文件服务器:

  • python3 -m http.server 8000

  • ngrok http 8000

你可以参考 ngrok: deliver your apps, APIs, and AI on local and prod 来进行设置。我们可以得到一个公共的访问地址,比如:https://diaphysial-rafael-unscrupulously.ngrok-free.dev -> http://localhost:8000

结果:

What is the frontier market GDP share in 2025?

结论:从原始 PDF 到 agent 可用数据

LlamaParse Extract 解决了文档理解问题,它可以从包含图表和表格的 PDF 中提取结构化、基于 schema 的数据。Elastic Agent Builder 解决了编排问题,将提取、索引以及查询串联成 workflow,并提供 agent 可按需调用的 ES|QL 工具。两者结合在一起,就弥合了企业原始文档与 agent 可用数据之间的鸿沟。

这一模式不仅适用于经济报告。任何具有明确结构的企业文档(合同、技术规范、财务报表)都可以通过 Pydantic schema 建模,并用同样的 pipeline 进行处理。

下一步

  • 尝试配套 notebook

  • Elastic Agent Builder 文档

  • LlamaParse Extract 入门

  • Elastic Workflows 文档

  • Agent Builder Kibana API

原文:PDF data extraction with LlamaParse and Elastic Agent Builder - Elasticsearch Labs

http://www.jsqmd.com/news/1037627/

相关文章:

  • 豆包AI实操指南:长上下文、多模态与人格化协同工作法
  • AI落地18大组织路障:从数据主权到ROI认可的实战排雷图
  • 国产大模型合规接入指南:安全替代Claude的中文AI实践
  • 武汉黄金回收怎么选不踩坑?本地高口碑机构实测榜单 - 奢侈品回收测评
  • 2026合肥卖金避坑干货,别被表面报价迷惑,看实测结果 - 奢侈品回收评测
  • 大连黄金回收行业深度测评 合规资质检测技术计价模式全解析 - 奢侈品回收评测
  • 2026这6款硬核降AIGC软件大公开,一键把AIGC率降至安全线! - 降AI小能手
  • 轻量级移动端纺织品识别:MobileNetV2小样本文化图像分类实战
  • QMan API深度解析:帧队列管理与硬件加速优化实践
  • AI视频创作革命:用MoneyPrinterTurbo一键生成专业短视频
  • 无隐形扣费!北京名表回收老店测评,报价透明资质合规,全城上门就近变现 - 名奢变现站
  • GPT-5.5级大模型如何真正‘干活’:长上下文、工具调用与多步推理实战解析
  • 2026年6月五金货架厂家推荐指南 - 多才菠萝
  • 多模型协同工作流:GPT-4o/4-turbo/3.5分层决策实战指南
  • LLM能写高性能CUDA GEMM算子吗?揭秘cuBLAS级优化的真实边界
  • 福州闲置黄金变现门店实测汇总 计价透明商家整理参考 - 奢侈品回收评测
  • 刺绳品类选型技术解析及合规生产厂家实测分享 - 起跑123
  • 【计算机毕业设计案例】基于 Python 的员工薪酬统计分析管理系统的设计与实现 基于 Python 的多部门员工信息管控系统(程序+文档+讲解+定制)
  • 示例驱动的数据清洗:用脏数据+干净样本反向生成清洗代码
  • NXP FMan策略配置实战:XML定义网络流量分类与监管
  • 2026高清音视频产业链上游分析:无线投屏芯片
  • 为什么选择ReadCat:打造专属纯净阅读空间的完整指南
  • 上海专业装修公司排行:本土靠谱装企实力盘点 - 起跑123
  • Gemma 4 MoE + OpenClaw:本地AI智能体全栈落地实践
  • 2026年河南食品软包装定制与种子袋生产厂家深度选型指南|源头工厂直达 - 精选优质企业推荐官
  • RTX 4060本地部署Qwen3.5-9B量化推理全链路指南
  • 上海专业老房翻新装修公司排行 本土靠谱装企盘点 - 起跑123
  • 南通音响改装新发现:2026年6月热门之选,路虎音响改装/理想音响改装/宝马音响改装,音响改装旗舰店怎么选择 - 音响改装门店分享
  • 上海专业装修公司排行:5家本土实力装企深度解析 - 起跑123
  • 蓝牙HCI厂商特定命令深度解析:从MC71000实战到嵌入式开发进阶