OCR+LLM自動化發票處理:復刻Uber TextSense流程(Colab實戰)

OCR+LLM自動化發票處理:復刻Uber TextSense流程(Colab實戰)

文章目录

  • 瞭解Uber的“TextSense”系統
  • 我們的計劃:複製核心工作流程
  • 動手實踐:逐步構建POC
  • 小結

如何複製Uber的發票處理系統?

手動錄入發票資料是一項緩慢且容易出錯的任務,企業幾十年來一直難以應對。最近,Uber 工程部門公佈了他們如何透過“TextSense”平臺應對這一挑戰,這是一個用於 GenAI 發票處理的複雜系統。該系統展示了智慧文件處理的強大功能,將光學字元識別 (OCR) 與大型語言模型 (LLM) 相結合,實現了高度準確的自動化資料提取。這種先進的方法對於小型專案來說似乎遙不可及。然而,其核心原理現在已面向大眾。本指南將向您展示如何複製 Uber 系統的基本工作流程。我們將使用簡單而強大的工具來建立一個自動化發票資料提取的系統。

瞭解Uber的“TextSense”系統

在構建我們的版本之前,瞭解其靈感來源將大有裨益。Uber 的目標是自動化處理數百萬份文件,從發票到收據。他們的“TextSense”平臺(在其工程部落格中詳細介紹)是一個專為此目的而設計的強大多階段流程。

圖中顯示了完整的文件處理流程。對於處理任何文件,在呼叫 LLM 之前通常進行預處理。

“TextSense”系統

Source: Uber

該系統的核心工作分為三個主要階段:

  1. 數字化(透過 OCR):首先,系統獲取文件,例如 PDF 或發票圖片。它使用先進的 OCR 引擎“讀取”文件,並將所有可視文字轉換為機器可讀的文字。原始文字是下一步的基礎。
  2. 智慧提取(透過 LLM):OCR 流程生成的原始文字通常雜亂無章且缺乏結構化。GenAI 的魔力就在於此。Uber 將這些文字輸入到一個大型語言模型中。LLM 就像位瞭解發票上下文的專家。它可以識別並提取特定的資訊,例如“發票號碼”、“總金額”和“供應商名稱”,並將它們組織成結構化格式,例如 JSON。
  3. 驗證(人機互動):沒有完美的 AI。為了確保 100% 的準確性,Uber 實施了人機互動 AI 系統。此驗證步驟將原始文件與 AI 提取的資料一起呈現給人工操作員。操作員可以快速確認資料正確無誤,或根據需要進行微調。這種反饋迴圈也有助於模型的持續改進。

OCR 與 AI 和人工監督的結合,使其系統高效可靠。下圖詳細解釋了 TextSense 的工作流程,正如上文所述。

TextSense 的工作流程

Source: Uber

我們的計劃:複製核心工作流程

我們的目標並非重建 Uber 的整個生產級平臺。相反,我們將以一種簡化、易於訪問的方式複製其核心智慧。我們將在一個 Google Colab 筆記本中構建 GenAI 發票處理 POC。

我們的計劃遵循相同的邏輯步驟:

  1. 提取文件:我們將建立一種簡單的方法,將 PDF 發票直接上傳到我們的筆記本。
  2. 執行 OCR:我們將使用強大的開源 OCR 引擎 Tesseract 從上傳的發票中提取所有文字。
  3. 使用 AI 提取實體:我們將使用 Google Gemini API 執行自動資料提取。我們將設計一個特定的提示,指示模型提取我們需要的關鍵欄位。
  4. 建立驗證 UI:我們將使用 ipywidgets 構建一個簡單的互動式介面,作為我們的人機互動 AI 系統,以便快速驗證提取的資料。

核心工作流程

這種方法為我們提供了一種強大且經濟的智慧文件處理方法,無需複雜的基礎架構。

動手實踐:逐步構建POC

讓我們開始構建我們的系統。您可以在新的 Google Colab notebook 中按照以下步驟操作。

步驟 1:設定環境

首先,我們需要安裝必要的 Python 庫。此命令會安裝用於處理 PDF(PyMuPDF)、執行 OCR(pytesseract)、與 Gemini API 互動以及構建 UI(ipywidgets)的軟體包。它還會安裝 Tesseract OCR 引擎本身。

!pip install -q -U google-generativeai PyMuPDF pytesseract pandas ipywidgets
!apt-get -qq install tesseract-ocr

步驟 2:配置Google Gemini API

接下來,您需要配置您的 Gemini API 金鑰。為了確保您的金鑰安全,我們將使用 Colab 內建的金鑰管理器。

  1. 從 Google AI Studio 獲取您的 API 金鑰。
  2. 在您的 Colab 筆記本中,點選左側邊欄上的金鑰圖示。
  3. 建立一個名為 GEMINI_API_KEY 的新金鑰,並將您的金鑰貼上為金鑰值。

以下程式碼將安全地訪問您的金鑰並配置 API。

import google.generativeai as genai
from google.colab import userdata
import fitz  # PyMuPDF
import pytesseract
from PIL import Image
import pandas as pd
import ipywidgets as widgets
from ipywidgets import Layout
from IPython.display import display, clear_output
import json
import io
# Configure the Gemini API
try:
   api_key = userdata.get(“GEMINI_API_KEY”)
   genai.configure(api_key=api_key)
   print("Gemini API configured successfully.")
except userdata.SecretNotFoundError:
   print("ERROR: Secret 'GEMINI_API_KEY' not found. Please follow the instructions to set it up.")

步驟 3:上傳並預處理PDF

此程式碼上傳一個發票 PDF 檔案。上傳 PDF 時,它會將每一頁轉換為高解析度影像,這是 OCR 的理想格式。

import fitz  # PyMuPDF
from PIL import Image
import io
import os
invoice_images = []
uploaded_file_name = "/content/sample-invoice.pdf"  # Replace with the actual path to your PDF file
# Ensure the file exists (optional but recommended)
if not os.path.exists(uploaded_file_name):
   print(f"ERROR: File not found at '{uploaded_file_name}'. Please update the file path.")
else:
   print(f"Processing '{uploaded_file_name}'...")
   # Convert PDF to images
   doc = fitz.open(uploaded_file_name)
   for page_num in range(len(doc)):
       page = doc.load_page(page_num)
       pix = page.get_pixmap(dpi=300) # Higher DPI for better OCR
       img = Image.open(io.BytesIO(pix.tobytes()))
       invoice_images.append(img)
   doc.close()
   print(f"Successfully converted {len(invoice_images)} page(s) to images.")
   # Display the first page as a preview
   if invoice_images:
       print("\n--- Invoice Preview (First Page) ---")
       display(invoice_images[0].resize((600, 800)))

輸出:

上傳並預處理PDF

步驟 4:使用Tesseract OCR提取原始文字

現在,我們對剛剛建立的影像執行 OCR 處理。所有頁面的文字將合併為一個字串。這就是我們將傳送給 Gemini 模型的上下文。此步驟是 AI OCR 工作流程中的關鍵部分。

full_invoice_text = ""
if not invoice_images:
   print("Please upload a PDF invoice in the step above first.")
else:
   print("Extracting text with OCR...")
   for i, img in enumerate(invoice_images):
       text = pytesseract.image_to_string(img)
       full_invoice_text += f"\n--- Page {i+1} ---\n{text}"
   print("OCR extraction complete.")
   print("\n--- Extracted Text (first 500 characters) ---")
   print(full_invoice_text[:500] + "...")

輸出:

使用Tesseract OCR提取原始文字

步驟 5:使用Gemini API進行智慧提取

GenAI 發票處理就在這裡。我們建立一個詳細的提示,告知 Gemini 模型其角色。我們指示它提取特定欄位並以清晰的 JSON 格式返回結果。請求 JSON 是一種強大的技術,可以使模型的輸出結構化且易於使用。

extracted_data = {}
if not full_invoice_text.strip():
   print("Cannot proceed. The extracted text is empty. Please check the PDF quality.")
else:
   # Instantiate the Gemini Pro model
   model = genai.GenerativeModel('gemini-2.5-pro')
   # Define the fields you want to extract
   fields_to_extract = "Invoice Number, Invoice Date, Due Date, Supplier Name, Supplier Address, Customer Name, Customer Address, Total Amount, Tax Amount"
   # Create the detailed prompt
   prompt = f"""
   You are an expert in invoice data extraction.
   Your task is to analyze the provided OCR text from an invoice and extract the following fields: {fields_to_extract}.
   Follow these rules strictly:
   1.  Return the output as a single, clean JSON object.
   2.  The keys of the JSON object must be exactly the field names provided.
   3.  If a field cannot be found in the text, its value in the JSON should be `null`.
   4.  Do not include any explanatory text, comments, or markdown formatting (like ```json) in your response. Only the JSON object is allowed.
   Here is the invoice text:
   ---
   {full_invoice_text}
   ---
   """
   print("Sending request to Gemini API...")
   try:
       # Call the API
       response = model.generate_content(prompt)
       # Robustly parse the JSON response
       response_text = response.text.strip()
       # Clean potential markdown formatting
       if response_text.startswith('```json'):
           response_text = response_text[7:-3].strip()
       extracted_data = json.loads(response_text)
       print("\n--- AI Extracted Data (JSON) ---")
       print(json.dumps(extracted_data, indent=2))
   except json.JSONDecodeError:
       print("\n--- ERROR ---")
       print("Failed to decode the model's response into JSON.")
       print("Model's Raw Response:", response.text)
   except Exception as e:
       print(f"\nAn unexpected error occurred: {e}")
       print("Model's Raw Response (if available):", getattr(response, 'text', 'N/A'))

輸出:

使用Gemini API進行智慧提取

步驟 6:構建人機互動 (HITL) 使用者介面

最後,我們構建了驗證介面。此程式碼在左側顯示發票圖片,並在右側建立一個可編輯的表單,表單中預先填充了來自 Gemini 的資料。使用者可以快速檢視資訊、進行必要的編輯並確認。

# UI Widgets
text_widgets = {}
if not extracted_data:
   print("No data was extracted by the AI. Cannot build verification UI.")
else:
   form_items = []
   # Create a text widget for each extracted field
   for key, value in extracted_data.items():
       text_widgets[key] = widgets.Text(
           value=str(value) if value is not None else "",
           description=key.replace('_', ' ').title() + ':',
           style={'description_width': 'initial'},
           layout=Layout(width='95%')
       )
       form_items.append(text_widgets[key])
   # The form container
   form = widgets.VBox(form_items, layout=Layout(padding='10px'))
   # Image container
   if invoice_images:
       img_byte_arr = io.BytesIO()
       invoice_images[0].save(img_byte_arr, format='PNG')
       image_widget = widgets.Image(
           value=img_byte_arr.getvalue(),
           format='png',
           width=500
       )
       image_box = widgets.HBox([image_widget], layout=Layout(justify_content='center'))
   else:
       image_box = widgets.HTML("No image to display.")
   # Confirmation button
   confirm_button = widgets.Button(description="Confirm and Save", button_style='success')
   output_area = widgets.Output()
   def on_confirm_button_clicked(b):
       with output_area:
           clear_output()
           final_data = {key: widget.value for key, widget in text_widgets.items()}
           # Create a pandas DataFrame
           df = pd.DataFrame([final_data])
           df['Source File'] = uploaded_file_name
           print("--- Verified and Finalized Data ---")
           display(df)
           # You can now save this DataFrame to CSV, etc.
           df.to_csv('verified_invoice.csv', index=False)
           print("\nData saved to 'verified_invoice.csv'")
   confirm_button.on_click(on_confirm_button_clicked)
   # Final UI Layout
   ui = widgets.HBox([
       widgets.VBox([widgets.HTML("<b>Invoice Image</b>"), image_box]),
       widgets.VBox([
           widgets.HTML("<b>Verify Extracted Data</b>"),
           form,
           widgets.HBox([confirm_button], layout=Layout(justify_content='flex-end')),
           output_area
       ], layout=Layout(flex='1'))
   ])
   print("--- Human-in-the-Loop (HITL) Verification ---")
   print("Review the data on the right. Make any corrections and click 'Confirm and Save'.")
   display(ui)

輸出:

構建人機互動 (HITL) 使用者介面

修改一些值然後儲存。

輸出:

構建人機互動 (HITL) 使用者介面

您可以在此處訪問完整程式碼:GitHubColab

小結

本 POC 成功證明了像 Uber 的“TextSense”這樣複雜系統背後的核心邏輯是可複製的。透過將開源 OCR 與像 Google Gemini 這樣強大的 LLM 相結合,您可以構建一個高效的 GenAI 發票處理系統。這種智慧文件處理方法顯著減少了人工工作量並提高了準確性。新增一個簡單的人機互動 AI 介面,確保最終資料的可靠性。

您可以在此基礎上進行擴充套件,新增更多欄位、改進驗證機制,並將其整合到更大的工作流程中。

評論留言