【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)

以下是转换结果的示例:

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

同意,该文本对人类阅读不友好。但它非常适合 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"
}

大多数 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)

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

然后我们需要定义提示模板,稍后将提供给 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()
    },
)

呵呵,这不像 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.

您可以看到提示更加详细和信息丰富。“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})

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)

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

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

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"

大多数字段都是不言自明的。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

上述命令将创建具有指定配置的新 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

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

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

5)测试API

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

paka function list

如果所有步骤都成功,则该函数应显示在标记为“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

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

{"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"}

6)监控

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

小节

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

相关实践学习
阿里云百炼xAnalyticDB PostgreSQL构建AIGC应用
通过该实验体验在阿里云百炼中构建企业专属知识库构建及应用全流程。同时体验使用ADB-PG向量检索引擎提供专属安全存储,保障企业数据隐私安全。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
目录
相关文章
|
2月前
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
618 2
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
LLM群体智能崛起,数学性能暴增11.6%!谷歌DeepMind四大机构联手新作
【10月更文挑战第17天】近日,谷歌、DeepMind等四大机构联合发布论文,展示大型语言模型(LLMs)在数学问题解决上的显著进步。通过引入元认知知识,研究人员开发了提示引导的交互程序,使LLMs能为数学问题分配合理技能标签并进行语义聚类。实验结果显示,GPT-4在GSM8K和MATH数据集上的准确性分别提升了11.6%和7.52%,展现出巨大潜力。这一成果不仅为AI领域提供了新思路,也为数学教育带来了启示。
43 4
|
8天前
|
人工智能 文字识别 数据挖掘
MarkItDown:微软开源的多格式转Markdown工具,支持将PDF、Word、图像和音频等文件转换为Markdown格式
MarkItDown 是微软开源的多功能文档转换工具,支持将 PDF、PPT、Word、Excel、图像、音频等多种格式的文件转换为 Markdown 格式,具备 OCR 文字识别、语音转文字和元数据提取等功能。
74 9
MarkItDown:微软开源的多格式转Markdown工具,支持将PDF、Word、图像和音频等文件转换为Markdown格式
|
13天前
|
开发框架 人工智能 安全
Promptic:轻量级 LLM 应用开发框架,提供完善的底层功能,使开发者更专注于构建上层功能
Promptic 是一个轻量级的 LLM 应用开发框架,支持通过一行代码切换不同的 LLM 服务提供商。它提供了类型安全的输出、流式支持、内置对话记忆、错误处理和重试等功能,帮助开发者专注于构建功能,而不是底层的复杂性。
49 6
Promptic:轻量级 LLM 应用开发框架,提供完善的底层功能,使开发者更专注于构建上层功能
|
11天前
|
JavaScript
jquery图片和pdf文件预览插件
EZView.js是一款jquery图片和pdf文件预览插件。EZView.js可以为图片和pdf格式文件生成在线预览效果。支持的文件格式有pdf、jpg、 png、jpeg、gif。
41 16
|
10天前
|
弹性计算 自然语言处理 数据库
通过阿里云Milvus和LangChain快速构建LLM问答系统
本文介绍如何通过整合阿里云Milvus、阿里云DashScope Embedding模型与阿里云PAI(EAS)模型服务,构建一个由LLM(大型语言模型)驱动的问题解答应用,并着重演示了如何搭建基于这些技术的RAG对话系统。
|
1月前
|
JSON 数据可视化 NoSQL
基于LLM Graph Transformer的知识图谱构建技术研究:LangChain框架下转换机制实践
本文介绍了LangChain的LLM Graph Transformer框架,探讨了文本到图谱转换的双模式实现机制。基于工具的模式利用结构化输出和函数调用,简化了提示工程并支持属性提取;基于提示的模式则为不支持工具调用的模型提供了备选方案。通过精确定义图谱模式(包括节点类型、关系类型及其约束),显著提升了提取结果的一致性和可靠性。LLM Graph Transformer为非结构化数据的结构化表示提供了可靠的技术方案,支持RAG应用和复杂查询处理。
135 2
基于LLM Graph Transformer的知识图谱构建技术研究:LangChain框架下转换机制实践
|
2月前
|
存储 人工智能 算法
精通RAG架构:从0到1,基于LLM+RAG构建生产级企业知识库
为了帮助更多人掌握大模型技术,尼恩和他的团队编写了《LLM大模型学习圣经》系列文档,包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构,基于LLM+RAG构建生产级企业知识库》和《从0到1吃透大模型的顶级架构》。这些文档不仅系统地讲解了大模型的核心技术,还提供了实战案例和配套视频,帮助读者快速上手。
精通RAG架构:从0到1,基于LLM+RAG构建生产级企业知识库
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
LLM群体智能崛起,数学性能暴增11.6%!谷歌DeepMind四大机构联手新作
【10月更文挑战第16天】最新研究显示,大型语言模型(LLMs)在数学问题解决上取得显著进展。谷歌、DeepMind等机构的研究人员通过引入元认知知识,使LLMs能更好地理解和解决数学问题,其在GSM8K和MATH数据集上的准确率分别提升了11.6%和7.52%。这一成果不仅为AI领域开辟了新路径,也为数学教育带来了新的可能性。
47 3
|
2月前
|
Java Apache Maven
将word文档转换成pdf文件方法
在Java中,将Word文档转换为PDF文件可采用多种方法:1) 使用Apache POI和iText库,适合处理基本转换需求;2) Aspose.Words for Java,提供更高级的功能和性能;3) 利用LibreOffice命令行工具,适用于需要开源解决方案的场景。每种方法都有其适用范围,可根据具体需求选择。

热门文章

最新文章