首页 / 文章 / 用同一套Python代码向OpenAI、Claude和Gemini API发送图片与PDF
← 返回
AI技术

用同一套Python代码向OpenAI、Claude和Gemini API发送图片与PDF

✍️ zhirenhun 📅 2026/7/28 👁 129 阅读 ⏱ 18 分钟
用同一套Python代码向OpenAI、Claude和Gemini API发送图片与PDF

在实际开发中,我们经常需要向不同的AI模型发送非文本输入(图片、PDF),但各大厂商API的接口设计各异。本文将展示如何编写一个通用的Python函数,用统一的方式向OpenAI、Claude和Gemini发送图像与PDF文件,并提取文本响应。

核心思路

1. **统一输入格式**:将图片或PDF统一转换为Base64编码的字符串,并附上MIME类型。 2. **封装API调用**:为每个模型编写适配器,由主函数根据`provider`参数选择调用的API端点。 3. **处理多模态内容**:在消息体中以`content`列表的形式同时传递文本提示和文件数据。

文件编码函数

首先,我们编写一个通用的方法,将任何本地文件转换为Base64字符串:

python
import base64
from pathlib import Path

def encode_file_to_base64(file_path: str) -> str: with open(file_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") ```

对于PDF文件,建议先将其转换为图片(例如每页转为JPEG),再按图片处理。下面是一个简单的PDF转图片示例(需要安装`pdf2image`):

python
from pdf2image import convert_from_path
import io
from PIL import Image

def pdf_to_images(pdf_path: str, dpi: int = 200): images = convert_from_path(pdf_path, dpi=dpi) return images # 返回PIL Image列表 ```

构建多模态消息体

不同API对多模态内容的格式有细微差异,但都可以利用“消息列表”结构。下面展示一个统一的构建函数:

python
def build_multimodal_message(prompt: str, base64_data: str, mime_type: str):
    # 通用格式:将文本与base64数据组合
    return {
        "role": "user",
        "content": [
            {"type": "text", "text": prompt},
            {
                "type": "image_url" if mime_type.startswith("image/") else "file",
                "image_url" if mime_type.startswith("image/") else "file": {
                    "url": f"data:{mime_type};base64,{base64_data}"
                }
            }
        ]
    }

注意:OpenAI和Claude支持`image_url`格式,Gemini则使用`inline_data`。可以在适配器中分别处理。

各API适配器示例

#### OpenAI (gpt-4o / gpt-4-turbo)

python
def call_openai(api_key, model, messages):
    from openai import OpenAI
    client = OpenAI(api_key=api_key)
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        max_tokens=1024
    )
    return response.choices[0].message.content

#### Anthropic Claude (claude-3-opus-20240229)

Claude的API使用`anthropic`库,且要求`content`列表中的`source`字段:

python
def call_claude(api_key, model, messages):
    import anthropic
    client = anthropic.Anthropic(api_key=api_key)
    # 将通用消息转换为Claude格式
    claude_messages = []
    for msg in messages:
        if isinstance(msg["content"], list):
            # 重新构建为Claude可接受的结构
            claude_messages.append({
                "role": msg["role"],
                "content": [
                    {"type": "text", "text": item["text"]} if item["type"] == "text"
                    else {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": item["image_url"]["url"].split(";")[0].replace("data:", ""),
                            "data": item["image_url"]["url"].split(",")[1]
                        }
                    }
                    for item in msg["content"]
                ]
            })
        else:
            claude_messages.append(msg)
    response = client.messages.create(
        model=model,
        max_tokens=1024,
        messages=claude_messages
    )
    return response.content[0].text

#### Google Gemini (gemini-1.5-pro)

Gemini使用`google-generativeai`库,内联数据需放在`parts`中:

python
def call_gemini(api_key, model, messages):
    import google.generativeai as genai
    genai.configure(api_key=api_key)
    model = genai.GenerativeModel(model)
    # 提取最新用户消息
    last_user_msg = messages[-1]["content"]
    text_part = ""
    image_parts = []
    for item in last_user_msg:
        if item["type"] == "text":
            text_part = item["text"]
        elif item["type"] == "image_url":
            # Gemini需要单独提取base64
            raw_url = item["image_url"]["url"]
            mime_type = raw_url.split(";")[0].replace("data:", "")
            base64_data = raw_url.split(",")[1]
            image_parts.append({
                "mime_type": mime_type,
                "data": base64_data
            })
    # 构建content
    content_parts = [text_part]
    for img in image_parts:
        content_parts.append(genai.upload_file_from_bytes(
            io.BytesIO(base64.b64decode(img["data"])),
            mime_type=img["mime_type"]
        ))
    response = model.generate_content(content_parts)
    return response.text

统一调用函数

最后,用一个入口函数根据`provider`路由:

python
def send_multimodal(provider: str, api_key: str, model: str, prompt: str, file_path: str):
    if file_path.lower().endswith(".pdf"):
        # 将PDF第一页转为JPEG
        images = pdf_to_images(file_path)
        img_buffer = io.BytesIO()
        images[0].save(img_buffer, format="JPEG")
        base64_data = base64.b64encode(img_buffer.getvalue()).decode("utf-8")
        mime_type = "image/jpeg"
    else:
        base64_data = encode_file_to_base64(file_path)
        mime_type = f"image/{Path(file_path).suffix[1:]}"  # 简易判断

msg = build_multimodal_message(prompt, base64_data, mime_type) messages = [msg]

if provider == "openai": return call_openai(api_key, model, messages) elif provider == "claude": return call_claude(api_key, model, messages) elif provider == "gemini": return call_gemini(api_key, model, messages) else: raise ValueError("Unsupported provider") ```

使用示例

python
result = send_multimodal(
    provider="openai",
    api_key="sk-...",
    model="gpt-4o",
    prompt="请描述这张图片中的内容",
    file_path="photo.jpg"
)
print(result)

——

出处:How to send images and PDFs to OpenAI, Claude, and Gemini APIs using the same Python code

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

← 上一篇
我原本计划了10个LLM评估实验,但只跑了1个——而这1个就够了。
下一篇 →
从 GitHub Issue 到 Pull Request:无人值守运行 Claude Code

📌 相关推荐

停止相信仅文本代理排行榜:来自 Cua-Bench 和 Factorio 的教训
2026/8/26
Agent Memory 有两种不同含义,回答引擎给出的却是错误的那一种
2026/8/26
LLM的止境:AI辅助VAPT流水线的确定性评分
2026/8/22
← 返回文章列表