跳至主要內容
技術

多模態輸入:圖片、PDF 與文件處理

多模態輸入:圖片、PDF 與文件處理
Claude API & Agent SDK 完全指南 第 6 / 15 篇

本篇是「Claude API & Agent SDK 完全指南」系列的第 6 / 15 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。

前幾章我們把 Claude 當純文字模型來用:送進去的是文字,出來的也是文字。但 Claude 的能力遠不止於此。

從 Claude 3 Sonnet 開始,Claude 就是一個真正的多模態模型(multimodal model)。你可以送圖片進去,Claude 看得懂。你可以丟 PDF,Claude 讀得了。你可以同時傳五張截圖,Claude 可以跨圖分析。

這一章我要帶你搞清楚多模態輸入的完整用法,包括技術細節、成本考量,以及我在生產環境中真正使用的做法。

什麼是多模態(Multimodal)?

所謂「多模態」,就是模型能處理不同種類的輸入,而不僅限於文字。

對 Claude 來說,目前支援的輸入模態是:

  • 文字(一直支援)
  • 圖片(JPEG、PNG、GIF、WebP)
  • PDF 文件

值得注意的是,截至 2026 年,Claude 不支援影片輸入。你沒辦法把 mp4 傳給 Claude 分析。如果你的使用場景需要影片分析,目前的做法是把影片截成一系列關鍵幀圖片,再批次傳入——這個方法雖然繁瑣,但可行。

圖片輸入的兩種方式

Claude API 接受圖片的方式有兩種:base64 編碼URL 直接引用。兩種方式各有適用場景。

方式一:Base64 編碼

把圖片轉成 base64 字串,直接嵌入 API 請求裡。

適合:

  • 圖片存在本地(不需要先上傳到某個地方)
  • 圖片是動態生成的(例如截圖)
  • 你希望請求完全自包含,不依賴外部 URL

缺點:

  • base64 會讓 payload 體積增大約 33%
  • 長圖片會讓請求 JSON 變得很龐大
import anthropic
import base64
from pathlib import Path

client = anthropic.Anthropic()

# 讀取圖片並轉換為 base64
image_path = Path("screenshot.png")
image_data = base64.standard_b64encode(image_path.read_bytes()).decode("utf-8")

message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/png",  # 必須正確指定
                        "data": image_data,
                    },
                },
                {
                    "type": "text",
                    "text": "這張截圖裡有什麼錯誤訊息?請詳細說明問題所在。"
                }
            ],
        }
    ],
)

print(message.content[0].text)

media_type 的值必須與實際圖片格式對應:

  • image/jpeg
  • image/png
  • image/gif
  • image/webp

方式二:URL 直接引用

如果圖片已經有一個公開可訪問的 URL,可以直接傳 URL,讓 Claude 自己去抓。

message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "url",
                        "url": "https://example.com/chart.png",
                    },
                },
                {
                    "type": "text",
                    "text": "這張圖表顯示了什麼趨勢?"
                }
            ],
        }
    ],
)

URL 方式的限制

  • URL 必須是公開可訪問的(Claude 伺服器要能下載到)
  • 不支援需要認證的 URL(例如需要登入的 S3 bucket)
  • 不支援本地網路 URL

我在生產環境的習慣是:如果圖片已經在公開 CDN 上,用 URL;如果是用戶上傳的動態圖片或本地生成的,用 base64。

圖片大小與 Token 計算

這裡有個很多人忽略的重點:圖片會消耗 token,而且消耗量與圖片大小成正比

Claude 在處理圖片時,內部會把圖片切分成 tiles(磁磚),每個 tile 大約消耗 1500-1600 個 token。

計算規則:

  1. 圖片先會縮放到最長邊不超過 1568px
  2. 縮放後,每個 512x512 的 tile 消耗約 1600 tokens
  3. 還有一個固定的基礎成本(base cost)約 2500 tokens

舉個例子:一張 1000x1000 的圖片,大約消耗 4000-5000 tokens。

實際影響:如果你的應用需要同時傳多張圖片,token 成本會快速累積。以 Claude Opus 4.5 為例,1000 tokens 輸入大約 $0.015。一張中等大小的圖片就可能消耗 $0.05-0.10。

我的建議:在傳圖片前先對圖片做壓縮。對截圖類的分析任務,把圖片壓到 800x600 以下通常不影響分析品質,但可以省下 50% 以上的 token。

from PIL import Image
import io

def compress_image_for_api(image_path: str, max_size: int = 1000) -> tuple[bytes, str]:
    """壓縮圖片以降低 token 消耗,同時保留分析品質"""
    img = Image.open(image_path)

    # 計算縮放比例,長邊不超過 max_size
    ratio = min(max_size / img.width, max_size / img.height, 1.0)
    if ratio < 1.0:
        new_size = (int(img.width * ratio), int(img.height * ratio))
        img = img.resize(new_size, Image.LANCZOS)

    # 轉換為 JPEG(通常比 PNG 小很多)
    output = io.BytesIO()
    img.convert("RGB").save(output, format="JPEG", quality=85)

    return output.getvalue(), "image/jpeg"

PDF 文件上傳與分析

PDF 支援是 Claude 相當強大的功能之一。你可以直接把 PDF 丟給 Claude,它能讀懂裡面的文字、表格,甚至是掃描版 PDF(帶有圖片的頁面)。

PDF 的傳入方式和圖片一樣,支援 base64 和 URL 兩種:

import anthropic
import base64
from pathlib import Path

client = anthropic.Anthropic()

# 讀取 PDF 並轉換為 base64
pdf_path = Path("contract.pdf")
pdf_data = base64.standard_b64encode(pdf_path.read_bytes()).decode("utf-8")

message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=2048,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "document",
                    "source": {
                        "type": "base64",
                        "media_type": "application/pdf",
                        "data": pdf_data,
                    },
                },
                {
                    "type": "text",
                    "text": "請摘要這份合約的主要條款,特別是付款條件和違約責任。"
                }
            ],
        }
    ],
)

print(message.content[0].text)

注意幾個細節:

  • PDF 用的是 type: "document" 而不是 type: "image"
  • media_typeapplication/pdf
  • PDF 的 token 消耗基本上跟文字提取後的文字量成正比,掃描版 PDF(全圖片)會比文字版 PDF 貴很多

PDF 分析的限制

Claude 在處理 PDF 時有幾個需要知道的限制:

  1. 頁數限制:目前 Claude 能處理的 PDF 上限大約是 100 頁,超過的頁面會被截斷
  2. 檔案大小:單個 PDF 不超過 32MB(base64 之前的原始大小)
  3. 掃描版 PDF:能讀,但品質取決於掃描品質;低解析度的掃描文件可能識別率不佳
  4. 加密 PDF:無法處理密碼保護的 PDF

TypeScript 範例

前面的例子都是 Python,但我知道很多人用 TypeScript 開發應用。這裡給一個完整的 TypeScript 範例:

import Anthropic from '@anthropic-ai/sdk';
import * as fs from 'fs';
import * as path from 'path';

const client = new Anthropic();

async function analyzeImageWithClaude(imagePath: string): Promise<string> {
  const imageBuffer = fs.readFileSync(imagePath);
  const base64Image = imageBuffer.toString('base64');

  // 根據副檔名決定 media_type
  const ext = path.extname(imagePath).toLowerCase();
  const mediaTypeMap: Record<string, string> = {
    '.jpg': 'image/jpeg',
    '.jpeg': 'image/jpeg',
    '.png': 'image/png',
    '.gif': 'image/gif',
    '.webp': 'image/webp',
  };

  const mediaType = mediaTypeMap[ext] ?? 'image/jpeg';

  const message = await client.messages.create({
    model: 'claude-opus-4-5',
    max_tokens: 1024,
    messages: [
      {
        role: 'user',
        content: [
          {
            type: 'image',
            source: {
              type: 'base64',
              media_type: mediaType as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp',
              data: base64Image,
            },
          },
          {
            type: 'text',
            text: '請描述這張圖片的內容,並指出任何值得注意的細節。',
          },
        ],
      },
    ],
  });

  return (message.content[0] as { type: 'text'; text: string }).text;
}

async function analyzePdfWithClaude(pdfPath: string): Promise<string> {
  const pdfBuffer = fs.readFileSync(pdfPath);
  const base64Pdf = pdfBuffer.toString('base64');

  const message = await client.messages.create({
    model: 'claude-opus-4-5',
    max_tokens: 2048,
    messages: [
      {
        role: 'user',
        content: [
          {
            type: 'document',
            source: {
              type: 'base64',
              media_type: 'application/pdf',
              data: base64Pdf,
            },
          },
          {
            type: 'text',
            text: '請摘要這份文件的主要內容。',
          },
        ],
      },
    ],
  });

  return (message.content[0] as { type: 'text'; text: string }).text;
}

// 使用範例
(async () => {
  const imageAnalysis = await analyzeImageWithClaude('screenshot.png');
  console.log('圖片分析結果:', imageAnalysis);

  const pdfAnalysis = await analyzePdfWithClaude('report.pdf');
  console.log('PDF 分析結果:', pdfAnalysis);
})();

多張圖片同時分析

Claude 支援在單次請求裡傳入多張圖片。這個功能在某些場景非常有用,例如:

  • 比較兩個設計稿的差異
  • 分析一系列截圖找出 bug
  • 從多張商品圖片生成描述
import anthropic
import base64
from pathlib import Path

client = anthropic.Anthropic()

def load_image_as_base64(path: str) -> dict:
    """輔助函式:載入圖片並轉換為 API 格式"""
    img_path = Path(path)
    img_data = base64.standard_b64encode(img_path.read_bytes()).decode("utf-8")

    ext_to_media_type = {
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".png": "image/png",
        ".gif": "image/gif",
        ".webp": "image/webp",
    }
    media_type = ext_to_media_type.get(img_path.suffix.lower(), "image/jpeg")

    return {
        "type": "image",
        "source": {
            "type": "base64",
            "media_type": media_type,
            "data": img_data,
        }
    }

# 同時分析三張截圖
screenshots = ["before.png", "after.png", "error.png"]

content = []
for i, screenshot in enumerate(screenshots):
    content.append(load_image_as_base64(screenshot))
    content.append({
        "type": "text",
        "text": f"[圖片 {i+1}: {screenshot}]"
    })

content.append({
    "type": "text",
    "text": "以上三張截圖分別是:before(操作前)、after(操作後)、error(出現的錯誤)。請分析這個問題,找出 before 和 after 的差異,解釋錯誤可能的原因。"
})

message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=2048,
    messages=[
        {"role": "user", "content": content}
    ],
)

print(message.content[0].text)

實際應用場景

讓我分享幾個我在實際專案中用過的多模態應用場景:

截圖分析(Error Analysis)

這是我用得最多的場景。當使用者遇到錯誤時,讓他們截圖上傳,比手打錯誤訊息準確得多:

def analyze_error_screenshot(screenshot_base64: str) -> dict:
    """分析錯誤截圖,返回診斷結果和解決建議"""
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/png",
                            "data": screenshot_base64,
                        },
                    },
                    {
                        "type": "text",
                        "text": """你是一位技術支援工程師。請分析這張錯誤截圖:

1. 描述錯誤訊息的確切內容
2. 判斷錯誤的可能原因(列出 2-3 個)
3. 提供解決步驟(按優先順序排列)

請用繁體中文回答,格式清晰。"""
                    }
                ],
            }
        ],
    )

    return {"analysis": message.content[0].text}

文件 OCR 與結構提取

掃描版合約或表單的結構化資料提取:

def extract_invoice_data(invoice_image_base64: str) -> dict:
    """從發票圖片提取結構化資料"""
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/jpeg",
                            "data": invoice_image_base64,
                        },
                    },
                    {
                        "type": "text",
                        "text": """請從這張發票圖片中提取以下資訊,以 JSON 格式回答:

{
  "invoice_number": "發票號碼",
  "date": "日期(YYYY-MM-DD)",
  "vendor": "廠商名稱",
  "total_amount": "總金額(數字)",
  "currency": "幣別",
  "items": [
    {"description": "品項說明", "quantity": 數量, "unit_price": 單價, "amount": 金額}
  ]
}

只回答 JSON,不要其他文字。"""
                    }
                ],
            }
        ],
    )

    import json
    return json.loads(message.content[0].text)

設計評審

我在開發流程裡加入 Claude 做自動化設計審查:

def review_design_mockup(design_image_base64: str, design_brief: str) -> str:
    """根據設計簡報評審 UI mockup"""
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=2048,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/png",
                            "data": design_image_base64,
                        },
                    },
                    {
                        "type": "text",
                        "text": f"""你是一位資深 UX 設計師。

設計簡報:
{design_brief}

請針對這個 UI mockup 提供專業意見:
1. 是否符合設計簡報的需求?
2. 可用性問題(如果有)
3. 視覺層次是否清晰?
4. 改進建議(最多三點,按重要性排序)"""
                    }
                ],
            }
        ],
    )

    return message.content[0].text

Prompt 技巧:讓圖片分析更準確

幾個我實測有效的技巧:

1. 明確說明圖片的上下文

不要只說「分析這張圖片」,要告訴 Claude 這是什麼類型的圖片、你想知道什麼:

❌ 差:「請分析這張圖片。」
✅ 好:「這是一個 React 應用程式的截圖,頁面上出現了一個錯誤訊息。請識別錯誤訊息的完整內容,並推測可能的原因。」

2. 對多圖片請求,為每張圖片加標籤

在圖片之後加一個文字說明,讓 Claude 知道每張圖的角色:

content = [
    image_1_block,
    {"type": "text", "text": "[圖1: 設計稿 - 手機版]"},
    image_2_block,
    {"type": "text", "text": "[圖2: 設計稿 - 桌機版]"},
    {"type": "text", "text": "請比較這兩個版本的設計一致性..."}
]

3. 要求結構化輸出

對需要從圖片提取資訊的任務,要求 JSON 格式輸出,方便後續處理:

"請從這張報表截圖中提取所有數據,以 JSON 格式回答,不要其他說明文字。"

4. 分步驟引導複雜分析

對複雜的圖片分析任務,分步驟引導比一次性提問效果更好:

# Step 1: 描述
message1 = "首先,請描述你在這張架構圖中看到的所有元件和連接關係。"

# Step 2: 分析(帶入 step 1 的結果)
message2 = f"根據你的描述:{description}\n現在請分析這個架構有哪些潛在的單點故障(SPOF)。"

影像品質 vs Token 成本的 Tradeoff

我來給你一個實際的對比數據,讓你在專案中做出有依據的決策:

圖片尺寸大約 Token 消耗Claude Opus 4.5 成本
300x300~1,000 tokens~$0.015
800x600~3,000 tokens~$0.045
1200x900~5,500 tokens~$0.083
1920x1080~8,000 tokens~$0.120

對大多數截圖分析任務,800x600 的解析度已經足夠。把圖片壓縮到這個尺寸,可以在不影響分析品質的前提下,省下 60% 以上的圖片相關 token 費用。

我的實踐原則:

  • 文字識別(OCR 類任務):至少 150 DPI,文字要清晰可辨
  • 圖表分析:800px 寬度通常足夠
  • UI 截圖分析:直接原始尺寸,UI 細節很重要
  • 設計評審:直接原始尺寸,保留細節

多模態輸入讓 Claude 從「聊天機器人」變成真正能處理真實世界資料的助手。圖片和 PDF 的支援打開了大量原本無法自動化的使用場景。

不過,多模態請求的 token 消耗也比純文字請求高出不少。在下一章,我們來聊一個能大幅降低 API 成本的技術:Prompt Caching。如果你的應用有固定的 system prompt 或長文件,一個好的快取策略可以讓成本直接砍半。

留言討論

esc
輸入關鍵字搜尋文章...
查看收藏 →