cd ../back to blog
$Guide//June 4, 2026//7 min read

視覺與文件理解:用一套 OpenAI 形狀打到 Claude 與 Gemini

用一個 image_url 部分,以 URL 或 base64 把影像送進 Claude 與 Gemini 做 OCR、圖表與掃描文件理解。影像以輸入權杖計費,失敗呼叫免費。

有相當多所謂的「文件 AI」,其實只是把一張圖片送進對話模型,再讀它 回給你的內容。一張收據、一張儀表板截圖、一頁掃描的 PDF、一張白板 照片 — 現代的 Claude 與 Gemini 模型都能原生讀懂,不需要另外的 OCR 引擎。麻煩通常出在接線上:每一家供應商都有自己一套附加影像的方式, 而把程式碼從一家移植到另一家很惱人。

透過 Brievio,你只用一種請求形狀 — OpenAI Chat Completions 的 content 陣列,搭配一個 image_url 部分 — 它就能一致地打到 Claude Opus 4.7、Sonnet 4.6、Haiku 4.5,以及 Gemini 2.5 Pro / Flash。這些都是貨真價實的第一方模型,原生視覺能力 如實保留,所以 Claude 讀得好的那張 JPEG,Claude 真的會去讀。這篇談的 是影像輸入(理解),而非影像生成:URL、base64、多影像提示,以及實務 工作中常見的 OCR / 圖表 / 掃描文件等情境。

最單純的情況:以 URL 送出影像

如果你的影像已經放在一個公開的 HTTPS URL 上,就把它當成一個 image_url 部分,擺在你的文字旁邊。把 claude-sonnet-4-6 換成 gemini-2.5-pro,請求 本體完全不變 — 這份可攜性正是重點所在:

image_url.py
# 以 URL 送出一張影像。同一套 OpenAI chat 形狀,打到的是真正的模型。
from openai import OpenAI

client = OpenAI(
    api_key="sk-brievio-...",
    base_url="https://api.brievio.com/v1",
)

resp = client.chat.completions.create(
    model="claude-sonnet-4-6",     # 或 gemini-2.5-pro — 請求形狀完全相同
    max_tokens=500,
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "這張圖表呈現什麼?用一句話講出趨勢。"},
                {
                    "type": "image_url",
                    "image_url": {"url": "https://example.com/q3-revenue.png"},
                },
            ],
        }
    ],
)

print(resp.choices[0].message.content)
# 影像以「輸入」權杖計費 — 讀 resp.usage.prompt_tokens 就能看到成本。

有一件事要趁早記牢:影像會耗用輸入權杖。模型不會 免費看到像素 — 它會把影像切成方塊,每一塊都像文字一樣計費。Brievio 回報誠實的權杖數,所以影像成本會原原本本地顯示在 resp.usage.prompt_tokens 裡,跟上游供應商的收費一致。一張 全螢幕截圖,依解析度而定,可能耗用數百到數千個輸入權杖。請把它當成 一段脈絡來編列預算,而不是把它當成免費贈品。

Base64:你實際上會用到的情況

在正式環境裡,影像很少是公開 URL — 它往往是使用者剛上傳的檔案、來自 掃描器的緩衝區、一個私有的 S3 物件。對這些,就把位元組以 base64 的 data: URL 內嵌進去。模型分辨不出差別;你的位元組也永遠 不必對外公開可達:

base64_upload.py
# 多數正式環境的影像不是公開 URL。把它們以 base64 data URL 內嵌進去。
import base64
from openai import OpenAI

client = OpenAI(api_key="sk-brievio-...", base_url="https://api.brievio.com/v1")

def data_url(path: str, media_type: str = "image/png") -> str:
    with open(path, "rb") as f:
        b64 = base64.standard_b64encode(f.read()).decode("utf-8")
    return f"data:{media_type};base64,{b64}"

resp = client.chat.completions.create(
    model="gemini-2.5-flash",      # 做 OCR / 收據又便宜又快
    max_tokens=800,
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "把每一個明細項目與總計擷取成 JSON。鍵: items[]、total。"},
                {"type": "image_url", "image_url": {"url": data_url("receipt.jpg", "image/jpeg")}},
            ],
        }
    ],
)

print(resp.choices[0].message.content)
# 提示: data URL 會讓請求本體比原始檔案膨脹約 33%。請把影像維持在
# 合理大小 — 一張 2-3MP 的截圖對文字辨識綽綽有餘;你很少需要 12MP。

有兩個實務上的提醒。第一,base64 會為你的請求本體增加約 33% 的額外 負擔,而且有單張影像的大小上限(Anthropic 在 API 上把單張影像約略限制 在 5 MB;Gemini 也有自己的上限)。如果一張大型掃描檔回了 413,就把 它縮小 — 文字在比你想像低很多的解析度下依然清晰可讀。第二,送出正確 的 media_typeimage/pngimage/jpeg image/webp);類型不符是造成靜默解碼失敗的常見原因。當一個 請求在 Brievio 上以 4xx 或 5xx 失敗時,你不會被收費 — 失敗的呼叫是免費 的,所以你可以重試一張縮小後的影像,而不必付兩次錢。

多影像提示與掃描文件

content 陣列能容納任意多個 image_url 部分, 並與文字交錯排列。這就解鎖了真正實用的工作流程:比對前後對照的截圖、 讀一份多頁的掃描文件,或餵入一連串圖表並要求歸納出共同的脈絡。在兩個 模型家族上都見效的訣竅,是用一個小小的文字錨點為每張影像加上標籤,讓 模型能夠引用它:

multi_image.py
# 在同一個提示裡放多張影像 — 比對兩張截圖,或讀一份 4 頁的掃描檔。
content = [
    {"type": "text", "text": "這是一份掃描合約的第 1-3 頁。請整理出當事人、期間與終止條款,並為每一項標註頁碼。"},
]
for i, path in enumerate(["page1.png", "page2.png", "page3.png"], start=1):
    content.append({"type": "text", "text": f"--- 第 {i} 頁 ---"})
    content.append({"type": "image_url", "image_url": {"url": data_url(path)}})

resp = client.chat.completions.create(
    model="claude-opus-4-7",       # 面對密集文件,推理能力最強
    max_tokens=1200,
    messages=[{"role": "user", "content": content}],
)

print(resp.choices[0].message.content)
# 在每張影像前插入一個文字標籤(「--- 第 2 頁 ---」),能給模型一個
# 可引用的錨點,並明顯改善兩個家族在多影像情境下的定位準確度。

面對長文件,模型選擇上有個取捨。Gemini 2.5 Flash 又便宜又快,是大量 OCR、收據與表單擷取的絕佳預設選項。Claude Opus 4.7 對密集的多頁材料 推理得更深 — 合約、財務報表,以及任何需要它把好幾頁同時納入視野並相互 交叉參照的場合。Sonnet 4.6 與 Gemini 2.5 Pro 則介於兩者之間。除了那串 model 字串以外,你不必更動任何程式碼就能依任務路由;在線 清單請見 /models

這些模型擅長(與不擅長)什麼

原生視覺能把一大批真實任務處理得很好:

  • OCR 與轉寫 — 印刷體文字,以及意外地相當不錯的手寫 字。沒有 Tesseract 流程要維護。
  • 圖表與儀表板 — 從長條圖/折線圖讀出數值、歸納趨勢、 在截圖裡核對某個指標是否合理。
  • 結構化擷取 — 把收據、發票、表單、證件轉成 JSON。在 提示裡搭配一份嚴格的 schema,就能得到乾淨的輸出。
  • 介面與圖解理解 — 描述一個畫面、讀一個錯誤對話框、 解說一張架構圖。

也有一些誠實的限制。模型仍會偶爾誤讀單一個數字或一個密集的 表格儲存格,所以凡是讀錯數字代價高昂的場合,都請用 schema 或檢查碼來 驗證(例如,明細項目加總應該等於標示的總計)。低解析度影像中的細小 文字是最常見的失敗 — 給它一張高解析度的裁切圖。而且各模型之間的行為 確實有差異:某個模型能搞定的版面,另一個可能會卡住,這正是為什麼能夠 在同一個 API 背後切換模型 並在你自己的文件上做 A/B 測試,比任何單一基準測試都更有價值。

更進一步:視覺結合工具

視覺能與 API 的其他部分組合運用。你可以同時交給模型一張影像 以及一組工具,讓它讀一張截圖、然後拿擷取到的內容去呼叫一個 函式 —「讀這張發票,接著呼叫 create_expense(amount, vendor, date)。」那一層工具呼叫 在 Brievio 上同樣橫跨 Claude 與 Gemini 一致;那份 工具使用指南 講解了這套共通形狀。再結合視覺,它在一個請求裡就是一條相當完整的 文件處理流程。

重點整理

要做影像理解,你不需要另外的 OCR 服務,也不需要各家專屬的 SDK。在你 平常的對話請求上附加一個 image_url 部分 — URL 或 base64 data URL — 為多張影像加上標籤好讓模型能引用,再依任務路由到合適的 模型:大量、便宜的讀取交給 Gemini Flash,棘手的文件交給 Claude Opus。 記得影像以輸入權杖計費(誠實地顯示在 usage 裡),把解析度維持合理,驗證擷取出來的數字,並在一張 大型掃描檔被退回時善用「失敗呼叫免費」的重試。完整的請求參考與各模型 上限都在 文件裡 — 從那裡開始,一個下午就能讓文件流經 Claude 或 Gemini。