【LLM】基于LLama构建智能助理实现与PDF文件智能对话

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
简介: 【4月更文挑战第12天】构建智能助理服务,实现与PDF的自由对话

[toc]


前言

本文将演示如何利用 LLM 从 PDF 发票中提取数据。我将构建一个 FastAPI 服务器,该服务器将接受 PDF 文件并以 JSON 格式返回提取的数据。

我们将涵盖:

  • LangChan 用于构建 API
  • Paka,用于将 API 部署到 AWS 并水平扩展它

Paka 使用单命令方法简化了大型语言模型 (LLM) 应用程序的部署和管理。

以前,将自由格式文本转换为结构化格式通常需要我编写自定义脚本。这涉及使用 Python 或 NodeJS 等编程语言来解析文本并提取相关信息。这种方法的一个大问题是我需要为不同类型的文档编写不同的脚本。

LLM 的出现使得使用单个模型从不同的文档中提取信息成为可能。在本文中,我将向您展示如何使用 LLM 从 PDF 发票中提取信息。

我对这个项目的一些目标是:

  • 使用 HuggingFace 的开源模型 (llama2-7B),避免使用 OpenAI API 或任何其他云 AI API。
  • 构建生产就绪型 API。这意味着 API 应该能够同时处理多个请求,并且应该能够水平扩展。

一、PDF样例

我们将以 Linode 发票为例。下面是发票示例:
image.png

我们将从此发票中提取以下信息:

  • Invoice Number/ID
  • Invoice Date
  • Company Name
  • Company Address
  • Company Tax ID
  • Customer Name
  • Customer Address
  • Invoice Amount

二、构建API服务

1.PDF预处理

由于 LLM 需要文本输入,因此 PDF 文件最初必须转换为文本。对于这个任务,我们可以使用 pypdf 库或 LangChain 的 pypdf 包装器 - PyPDFLoader

from langchain_community.document_loaders import PyPDFLoader

pdf_loader = PyPDFLoader(pdf_path)
pages = pdf_loader.load_and_split()
page_content = pages[0].page_content

print(page_content)
AI 代码解读

以下是转换结果的示例:

Page 1 of 1
Invoice Date: 2024-01-01T08:29:56
Remit to:
Akamai Technologies, Inc.
249 Arch St.
Philadelphia, PA 19106
USA
Tax ID(s):
United States EIN: 04-3432319Invoice To:
John Doe
1 Hacker Way
Menlo Park, CA
94025
Invoice: #25470322
Description From To Quantity Region Unit
PriceAmount TaxTotal
Nanode 1GB
debian-us-west
(51912110)2023-11-30
21:002023-12-31
20:59Fremont, CA
(us-west)0.0075 $5.00 $0.00$5.00
145 Broadway, Cambridge, MA 02142
USA
P:855-4-LINODE (855-454-6633) F:609-380-7200 W:https://www.linode.com
Subtotal (USD) $5.00
Tax Subtotal (USD) $0.00
Total (USD) $5.00
This invoice may include Linode Compute Instances that have been powered off as the data is maintained and
resources are still reserved. If you no longer need powered-down Linodes, you can remove the service
(https://www.linode.com/docs/products/platform/billing/guides/stop-billing/) from your account.
145 Broadway, Cambridge, MA 02142
USA
P:855-4-LINODE (855-454-6633) F:609-380-7200 W:https://www.linode.com
AI 代码解读

同意,该文本对人类阅读不友好。但它非常适合 LLM。

2.提取内容

我们不是使用 Python、NodeJs 或其他编程语言中的自定义脚本进行数据提取,而是通过精心制作的提示对 LLM 进行编程。一个好的提示是让 LLM 产生所需输出的关键。

对于我们的用例,我们可以编写这样的提示:

Extract all the following values: invoice number, invoice date, remit to company, remit to address, tax ID, invoice to customer, invoice to address, total amount from this invoice: <THE_INVOICE_TEXT>

根据型号的不同,此类提示可能有效,也可能无效。为了获得一个小型的、预先训练的、通用的模型,例如 llama2-7B,以产生一致的结果,我们最好使用 Few-Shot 提示技术。这是一种奇特的说法,我们应该提供我们想要的模型输出的示例。现在我们这样写模型提示:

Extract all the following values: invoice number, invoice date, remit to company, remit to address, tax ID, invoice to customer, invoice to address, total amount from this invoice: <THE_INVOICE_TEXT>

An example output:
{
  "invoice_number": "25470322",
  "invoice_date": "2024-01-01",
  "remit_to_company": "Akamai Technologies, Inc.",
  "remit_to_address": "249 Arch St. Philadelphia, PA 19106 USA",
  "tax_id": "United States EIN: 04-3432319",
  "invoice_to_customer": "John Doe",
  "invoice_to_address": "1 Hacker Way Menlo Park, CA 94025",
  "total_amount": "$5.00"
}
AI 代码解读

大多数 LLM 会欣赏这些示例并产生更准确和一致的结果。

但是,我们将使用 LangChain 方法处理此问题,而不是使用上述提示。虽然可以在没有LangChain的情况下完成这些任务,但它大大简化了LLM应用程序的开发。

使用 LangChain,我们用代码(Pydantic 模型)定义输出模式。

from langchain.output_parsers import PydanticOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field


class Invoice(BaseModel):
    number: str = Field(description="invoice number, e.g. #25470322")
    date: str = Field(description="invoice date, e.g. 2024-01-01T08:29:56")
    company: str = Field(description="remit to company, e.g. Akamai Technologies, Inc.")
    company_address: str = Field(
        description="remit to address, e.g. 249 Arch St. Philadelphia, PA 19106 USA"
    )
    tax_id: str = Field(description="tax ID/EIN number, e.g. 04-3432319")
    customer: str = Field(description="invoice to customer, e.g. John Doe")
    customer_address: str = Field(
        description="invoice to address, e.g. 123 Main St. Springfield, IL 62701 USA"
    )
    amount: str = Field(description="total amount from this invoice, e.g. $5.00")


invoice_parser = PydanticOutputParser(pydantic_object=Invoice)
AI 代码解读

写下带有详细信息的字段描述。稍后,描述将用于生成提示。

然后我们需要定义提示模板,稍后将提供给 LLM。

from langchain_core.prompts import PromptTemplate

template = """
Extract all the following values : invoice number, invoice date, remit to company, remit to address,
tax ID, invoice to customer, invoice to address, total amount from this invoice: {invoice_text}

{format_instructions}

Only returns the extracted JSON object, don't say anything else.
"""

prompt = PromptTemplate(
    template=template,
    input_variables=["invoice_text"],
    partial_variables={
        "format_instructions": invoice_parser.get_format_instructions()
    },
)
AI 代码解读

呵呵,这不像 Few-Shot 提示那么直观。但是 invoice_parser.get_format_instructions() 将生成一个更详细的示例供 LLM 使用。

使用 LangChain 构建的已完成提示如下所示:

Extract all the following values : 
...
...
...
The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.

Here is the output schema:

{"properties": {"number": {"title": "Number", "description": "invoice number, e.g. #25470322", "type": "string"}, "date": {"title": "Date", "description": "invoice date, e.g. 2024-01-01T08:29:56", "type": "string"}, "company": {"title": "Company
", "description": "remit to company, e.g. Akamai Technologies, Inc.", "type": "string"}, "company_address": {"title": "Company Address", "description": "remit to address, e.g. 249 Arch St. Philadelphia, PA 19106 USA", "type": "string"}, "tax_id"
: {"title": "Tax Id", "description": "tax ID/EIN number, e.g. 04-3432319", "type": "string"}, "customer": {"title": "Customer", "description": "invoice to customer, e.g. John Doe", "type": "string"}, "customer_address": {"title": "Customer Addre
ss", "description": "invoice to address, e.g. 123 Main St. Springfield, IL 62701 USA", "type": "string"}, "amount": {"title": "Amount", "description": "total amount from this invoice, e.g. $5.00", "type": "string"}}, "required": ["number", "date
", "company", "company_address", "tax_id", "customer", "customer_address", "amount"]}


Only returns the extracted JSON object, don't say anything else.
AI 代码解读

您可以看到提示更加详细和信息丰富。“Only returned the extracted JSON object, don't say anything else.” 是我添加的,以确保 LLM 不会输出任何其他内容。

现在,我们准备使用 LLM 进行信息提取。

llm = LlamaCpp(
    model_url=LLM_URL,
    temperature=0,
    streaming=False,
)

chain = prompt | llm | invoice_parser

result = chain.invoke({"invoice_text": page_content})
AI 代码解读

LlamaCpp 是 Llama2-7B 模型的客户端代理,该模型将由 Paka 托管在 AWS 中。LlamaCpp 在这里定义。当 Paka 部署 Llama2-7B 模型时,它使用很棒的 llama.cpp 项目和 llama-cpp-python 作为模型运行时。

该链是一个管道,包含提示符、LLM 和输出解析器。在此管道中,提示符被馈送到 LLM 中,输出分析器分析输出。除了在提示符中创建一次性示例外,invoice_parser还可以验证输出并返回 Pydantic 对象。

3.构建API服务

有了核心逻辑,我们的下一步是构建一个 API 端点,该端点接收 PDF 文件并以 JSON 格式提供结果。我们将使用 FastAPI 来完成此任务。

from fastapi import FastAPI, File, UploadFile
from uuid import uuid4

@app.post("/extract_invoice")
async def upload_file(file: UploadFile = File(...)) -> Any:
    unique_filename = str(uuid4())
    tmp_file_path = f"/tmp/{unique_filename}"

    try:
        with open(tmp_file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)

        return extract(tmp_file_path) # extract is the function that contains the LLM logic
    finally:
        if os.path.exists(tmp_file_path):
            os.remove(tmp_file_path)
AI 代码解读

代码非常简单。它接受一个文件,将其保存到临时位置,然后调用提取函数来提取发票数据。

4.部署API服务

我们只走了一半。正如所承诺的那样,我们的目标是开发一个生产就绪的 API,而不仅仅是在我的本地机器上运行的原型。这涉及将 API 和模型部署到云中,并确保它们可以水平扩展。此外,我们需要收集日志和指标以进行监控和分析。这是一项艰巨的工作,而且不如构建核心逻辑有趣。幸运的是,我们有 Paka 帮助我们完成这项任务。

但在深入研究部署之前,让我们试着回答这个问题:“为什么我们需要部署模型,而不仅仅是使用 OpenAI 或 Google 的 API?要部署模型的主要原因:

  • Cost: 使用 OpenAI API 可能会因为大量数据而变得昂贵。
  • Vendor lock-in: 您可能希望避免被束缚在特定的提供商身上。
  • Flexibility: 您可能更愿意根据自己的需求定制模型,或者从 HuggingFace 中心选择开源选项。
  • Control: 您可以完全控制系统的稳定性和可扩展性。
  • Privacy: 您可能不希望将敏感数据暴露给外部各方。

现在,让我们使用 Paka 将 API 部署到 AWS:

1)基础环境

pip install paka

# Ensure AWS credentials and CLI are set up. 
aws configure

# Install pack CLI and verify it is working (https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/)
pack --version

# Install pulumi CLI and verify it is working (https://www.pulumi.com/docs/install/)
pulumi version

# Ensure the Docker daemon is running
docker info
AI 代码解读

2)创建配置文件

使用 CPU 实例运行模型。我们可以创建一个包含以下内容的 cluster.yaml 文件:

aws:
  cluster:
    name: invoice-extraction
    region: us-west-2
    namespace: default
    nodeType: t2.medium
    minNodes: 2
    maxNodes: 4
  prometheus:
    enabled: false
  tracing:
    enabled: false
  modelGroups:
    - nodeType: c7a.xlarge
      minInstances: 1
      maxInstances: 3
      name: llama2-7b
      resourceRequest:
        cpu: 3600m
        memory: 6Gi
      autoScaleTriggers:
        - type: cpu
          metadata:
            type: Utilization
            value: "50"
AI 代码解读

大多数字段都是不言自明的。modelGroups 字段是我们定义模型组的地方。在本例中,我们定义了一个名为 llama2-7b 的模型组,其实例类型为 c7a.xlarge。autoScaleTriggers 字段是我们定义自动缩放触发器的位置。我们正在定义一个 CPU 触发器,该触发器将根据 CPU 利用率扩展实例。请注意,Paka 不支持将模型组扩展到零实例,因为冷启动时间太长。我们需要保持至少一个实例处于运行状态。

要使用 GPU 实例运行模型,下面是一个集群配置示例。

3)创建集群

现在,您可以使用以下命令预配集群:

# Provision the cluster and update ~/.kube/config
paka cluster up -f cluster.yaml -u
AI 代码解读

上述命令将创建具有指定配置的新 EKS 集群。它还将使用新的集群信息更新 ~/.kube/config 文件。Paka 从 HuggingFace 中心下载 llama2-7b 模型并将其部署到集群。

4)部署服务

现在,我们想将 FastAPI 应用部署到集群。我们可以通过运行以下命令来执行此操作:

# Change the directory to the source code directory
paka function deploy --name invoice-extraction --source . --entrypoint serve
AI 代码解读

FastAPI 应用部署为函数。这意味着它是无服务器的。只有当有请求时,才会调用该函数。

在后台,该命令将使用构建包构建 Docker 映像,然后将其推送到 Elastic Container Registry。然后,映像将作为函数部署到集群中。

5)测试API

首先,我们需要获取 FastAPI 应用的 URL。我们可以通过运行以下命令来执行此操作:

paka function list
AI 代码解读

如果所有步骤都成功,则该函数应显示在标记为“READY”的列表中。默认情况下,可通过公共 REST API 终结点访问该函数,其格式通常类似于 http://invoice-extraction.default.50.112.90.64.sslip.io。

您可以通过使用 curl 或其他 HTTP 客户端向端点发送 POST 请求来测试 API。下面是一个使用 curl 的示例:

curl -X POST -H "Content-Type: multipart/form-data" -F "file=@/path/to/invoices/invoice-2024-02-29.pdf" http://invoice-extraction.default.xxxx.sslip.io/extract_invoice
AI 代码解读

如果发票提取成功,响应将显示结构化数据,如下所示:

{"number":"#25927345","date":"2024-01-31T05:07:53","company":"Akamai Technologies, Inc.","company_address":"249 Arch St. Philadelphia, PA 19106 USA","tax_id":"United States EIN: 04-3432319","customer":"John Doe","customer_address":"1 Hacker Way Menlo Park, CA  94025","amount":"$5.00"}
AI 代码解读

6)监控

出于监控目的,Paka 会自动将所有日志发送到 CloudWatch,以便直接在 CloudWatch 控制台中查看这些日志。此外,您可以在 cluster.yaml 中启用 Prometheus 来收集预定义的指标。

小节

本文演示了如何使用 LLM 从 PDF 发票中提取数据。我们构建了一个FastAPI服务器,能够接收PDF文件并以JSON格式返回信息。随后,我们使用 Paka 在 AWS 上部署了 API,并启用了水平扩展。

相关实践学习
阿里云百炼xAnalyticDB PostgreSQL构建AIGC应用
通过该实验体验在阿里云百炼中构建企业专属知识库构建及应用全流程。同时体验使用ADB-PG向量检索引擎提供专属安全存储,保障企业数据隐私安全。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
目录
打赏
0
0
0
0
52
分享
相关文章
不到100行代码,实现一个简易通用智能LLM Agent
本文将分享如何使用不到 100 行的 Python 代码,实现一个具备通用智能潜力的简易 LLM Agent。你将看到整个实现过程——从核心原理、提示(Prompt)调优、工具接口设计到主循环交互,并获得完整复现代码的详细讲解。
642 101
不到100行代码,实现一个简易通用智能LLM Agent
|
9天前
|
C#
【PDF提取内容改名】批量提取PDF指定区域内容重命名PDF文件,PDF自动提取内容命名的方案和详细步骤
本工具可批量提取PDF中的合同编号、日期、发票号等关键信息,支持PDF自定义区域提取并自动重命名文件,适用于合同管理、发票处理、文档归档和数据录入场景。基于iTextSharp库实现,提供完整代码示例与百度、腾讯网盘下载链接,助力高效处理PDF文档。
107 40
36.7K star!拖拽构建AI流程,这个开源LLM应用框架绝了!
`Flowise` 是一款革命性的低代码LLM应用构建工具,开发者通过可视化拖拽界面,就能快速搭建基于大语言模型的智能工作流。该项目在GitHub上线不到1年就斩获**36.7K星标**,被开发者誉为"AI时代的乐高积木"。
234 8
如何将 Swagger 文档导出为 PDF 文件
你会发现自己可能需要将 Swagger 文档导出为 PDF 或文件,以便于共享和存档。在这篇博文中,我们将指导你完成将 Swagger 文档导出为 PDF 格式的过程。
如何利用Python批量重命名PDF文件
本文介绍了如何使用Python提取PDF内容并用于文件重命名。通过安装Python环境、PyCharm编译器及Jupyter Notebook,结合tabula库实现PDF数据读取与处理,并提供代码示例与参考文献。
构建高性能LLM推理服务的完整方案:单GPU处理172个查询/秒、10万并发仅需15美元/小时
本文将通过系统性实验不同的优化技术来构建自定义LLaMA模型服务,目标是高效处理约102,000个并行查询请求,并通过对比分析确定最优解决方案。
42 0
构建高性能LLM推理服务的完整方案:单GPU处理172个查询/秒、10万并发仅需15美元/小时
使用CodeBuddy实现批量转换PPT、Excel、Word为PDF文件工具
通过 CodeBuddy 实现本地批量转换工具,让复杂的文档处理需求转化为 “需求描述→代码生成→一键运行” 的极简流程,真正实现 “技术为效率服务” 的目标。感兴趣的快来体验下把
109 10
Python爬虫结合API接口批量获取PDF文件
Python爬虫结合API接口批量获取PDF文件
Multimodal LLM训练-模型文件\训练数据加载逻辑源码分析
Multimodal LLM训练-模型文件\训练数据加载逻辑源码分析
125 17
LLM 不断提升智能下限,MCP 不断提升创意上限
LLM 是大脑,MCP 是手脚。LLM 不断提升智能下限,MCP 不断提升创意上限。所有的应用和软件都会被 AI 改造,将向所有的应用和软件都会被 MCP 改造的新范式演进。
196 25
AI助理
登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问

你好,我是AI助理

可以解答问题、推荐解决方案等