系列导语 本文是【大模型API中转站】系列 篇。本系列致力于用最低的成本、最清晰的方法,帮你打通多模型 API 的任督二脉。建议先收藏,随用随查。 前面讲过 Gemini 多模态接入避坑,但很多人卡在同一个坑:"我把音频/视频 URL 传过去,模型却说看不到文件。" 这一期我们用真实抓包数据,把"传文件给 Gemini 到底该怎么写"一次性讲透——两个端点、两种正确写法、三种常见错误格式。

先给最忙的人一句话结论:别用 input_audio / video_url 这种你以为存在的格式——OpenAI 端点统一用 image_url{url} 传图/音/视频;Gemini 原生端点统一用 fileData{fileUri,mimeType} 这两种是实测唯一能把媒体喂进 Gemini 的写法。


1. 问题:不是 Gemini 读不了文件,是格式没对

很多人从 OpenAI 的文档里抄来音频传参结构,想当然地给音/视频也套一个"看起来对称"的格式:

// ❌ 想当然的写法(不工作)
{ "type": "input_audio", "input_audio": { "url": "https://你的存储/audio.mp3", "format": "audio/mp3" } }
{ "type": "video_url",   "video_url":   { "url": "https://你的存储/video.mp4" } }

发过去 HTTP 200、也有回复,但回复驴唇不对马嘴——模型在瞎编。一查 usage 就露馅:

prompt_tokens_details: { text_tokens: 7, audio_tokens: 0, image_tokens: 0 }

audio_tokens=0 说明媒体根本没进模型,中转网关在解析阶段就把这两个块丢了(详见第 5 节原理)。所以"Gemini 没有读取文件能力"是个误会——是块格式没被网关识别,文件压根没送到 Gemini。


2. 核心结论:两个端点,两种正确写法

端点 调用路径 媒体写法 mime
OpenAI 兼容 POST /v1/chat/completions image_url{url} 网关下载后自动探测
Gemini 原生 POST /v1beta/models/{model}:generateContent fileData{fileUri, mimeType} 需手动写

两个端点都能用任意公网可访问的音/视频/图片 URL,但取 URL 的人不同OpenAI 端点是中转网关把 URL 下载下来转 base64 再喂 Gemini;Gemini 原生端点是纯透传,由 Google 自己去抓取这个 URL。区别在字段名、mime 要不要手写、以及谁来下载。

实测模型用 gemini-2.5-flash(支持音视频多模态);gemini-2.5-progemini-3.x 同理。


3. OpenAI 端点:image_url 是万能媒体载体

OpenAI 协议里,image_url 这个块不止能传图片——中转网关会把它的 url 当通用文件源下载,而 Gemini 的 fileData 本来就吃任意媒体类型。所以图、音、视频全塞 image_url

3.1 音频(实测有效)

from openai import OpenAI
client = OpenAI(api_key="sk-你的中转Key", base_url="https://api.4sapi.com/v1")

resp = client.chat.completions.create(
    model="gemini-2.5-flash",
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "这段音频里有什么声音?用一句话说"},
        {"type": "image_url", "image_url": {"url": "https://你的存储/audio.mp3"}},
    ]}],
)
print(resp.choices[0].message.content)

实测返回里 audio_tokens 大于 0(不再是 0),模型也正确分析出了音频内容——音频确实进去了

3.2 视频(实测有效)

把上面的 url 换成 .mp4 即可:

messages=[{"role": "user", "content": [
    {"type": "text", "text": "这个视频里有什么?"},
    {"type": "image_url", "image_url": {"url": "https://你的存储/video.mp4"}},
]}]

实测 prompt_tokens 大幅上升(视频帧 + 音轨都被采样),模型准确描述出了视频画面内容——视频被真正解析了


4. Gemini 原生端点:用 fileData

如果你直连 Gemini 原生协议(generateContent),就没有 image_url 这个概念了,改用 Gemini 自己的 fileData

import requests

r = requests.post(
    "https://api.4sapi.com/v1beta/models/gemini-2.5-flash:generateContent",
    headers={"x-goog-api-key": "sk-你的中转Key", "content-type": "application/json"},
    json={"contents": [{"parts": [
        {"text": "这段音频里有什么声音?一句话"},
        {"fileData": {"mimeType": "audio/mp3", "fileUri": "https://你的存储/audio.mp3"}},
    ]}]},
)
print(r.json())

实测返回里 promptTokensDetails{"modality": "AUDIO", "tokenCount": 104},模型分析了音频内容。视频同理:{"fileData": {"mimeType": "video/mp4", "fileUri": "https://你的存储/video.mp4"}}

澄清(实测 + 源码核实):Gemini 原生 fileData.fileUri 直接吃公网 http/https 媒体直链——中转网关在原生路径上是纯透传(仅对 YouTube 链接补个 mimeType,并不下载),URL 是 Google 自己去抓取并处理的。所以 Files API uri、YouTube 链接、普通公网直链三种都能用(官方文档例子只列了前两种,但实测公网直链也接受)。 对比:OpenAI 端点image_url{url}中转网关下载后转 inlineData 再发——两条路径"谁下载 URL"不同,但对调用方都是一行 URL 的事。


5. 踩坑速查:这些写法为什么不工作

写法 结果 原因
input_audio{url} ❌ 被丢 OpenAI 标准 input_audio 只收 base64 data,没有 url 字段;网关解析时 data 为空直接跳过
video_url{url:...}(对象) ❌ 多数被丢 不少网关版本的 video_url 只认字符串 "video_url":"http://...",不认 {url} 对象
input_audio{data:base64} ✅ 可用 这才是 OpenAI 音频的标准写法(但要自己 base64 编码,URL 更省事)
image_url{url} ✅ 万能 唯一同时接受 {url} 对象、又被当通用文件源下载的块

一句话避坑:图/音/视频全走 image_url{url}(OpenAI 端点)或 fileData{fileUri}(原生端点),别去碰 input_audio / video_url 这些"看起来该有"的格式。


6. 完整可跑示例(OpenAI 端点,一个函数传任意媒体)

from openai import OpenAI
client = OpenAI(api_key="sk-你的中转Key", base_url="https://api.4sapi.com/v1")

def ask_with_media(prompt: str, media_url: str, model="gemini-2.5-flash"):
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": [
            {"type": "text", "text": prompt},
            {"type": "image_url", "image_url": {"url": media_url}},  # 图/音/视频通吃
        ]}],
    )
    print(resp.choices[0].message.content)
    print("[tokens]", resp.usage.prompt_tokens_details)  # 看 audio/image 是否>0 验证媒体进没进

ask_with_media("音频里有什么声音?", "https://你的存储/audio.mp3")
ask_with_media("视频讲了什么?",     "https://你的存储/video.mp4")
ask_with_media("图里是什么?",       "https://你的存储/image.jpg")

验证技巧:永远盯着返回 usage 里的 audio_tokens / image_tokens——大于 0 才说明媒体真的进了模型,等于 0 就是被丢了,别看模型那段一本正经的胡话。


7. 使用建议与注意事项


8. 总结与系列导航

一句话总结

传文件给 Gemini 不难,难在选对块格式。 OpenAI 端点万能键是 image_url{url}(网关下载转 base64),Gemini 原生端点是 fileData{fileUri,mimeType}(Google 自己抓取,公网直链 / YouTube / Files API uri 都行)。别用 input_audio{url} / video_url{url} 这种会被静默丢弃的格式——验证就看 usageaudio_tokens 有没有大于 0。

通过 4SAPI ,一个 Key 就能用统一格式(OpenAI 或 Gemini 原生任选)调全系多模态——协议转换、音视频 URL 直传、计费明细、各家模型统一接口全帮你包好,不用为每家改代码、来回切 SDK。