多模态输入能力让大模型不再局限于文字对话,用户可以直接上传一张截图、一张照片,再配上一句提问,模型就能结合画面内容给出回答。这种图文混合请求看似简单,但第一次上手时不少人会踩坑:图片传不上去、格式不被支持、报错信息看不懂。本文从消息结构讲起,带你看懂多模态请求的本质,再给出可以直接运行的代码示例和常见问题的排查思路。

一、多模态请求的消息结构是怎样的
要理解多模态输入,首先要明白主流大模型API的消息组织方式。绝大多数接口都采用一个名为messages的数组,数组里每个元素代表一条消息,包含role(角色)和content(内容)两个字段。在纯文本对话中,content就是一个字符串,而在多模态请求中,content升级为一个列表,列表中可以混合存放文本块和图片块。
这种设计的好处非常明显:模型可以根据需要灵活组合任意数量的文本和图片,一次请求可以传一张图也可以传十张图,图片之间还能穿插文字说明。比如你想让模型对比两张设计稿的差异,就可以先放第一张图,再放一段文字说明“这是旧版本”,接着放第二张图,最后提出具体问题。模型会按照内容的顺序理解上下文。
一个典型的多模态消息结构如下(以OpenAI风格为例):
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "这张图片里有什么内容?请详细描述。"
},
{
"type": "image_url",
"image_url": {
"url": "https://ipipp.com/images/sample.jpg"
}
}
]
}
]注意看content变成了列表,文本块用type: text标识,图片块用type: image_url标识。不同厂商的接口在字段命名上略有差异,但整体思路完全一致:都是把消息内容拆分成带类型的块,让模型知道每一块是文字还是图像。
二、Base64编码与图片URL两种传图方式对比
传图片给大模型有两种主流方式:一是提供图片的公开URL,二是把图片文件读入内存后进行Base64编码,直接嵌在请求体里发送。两种方式各有适用场景,选错了不仅影响速度,还可能直接导致请求失败。
图片URL方式最简单,只需要一个可公网访问的链接。服务端收到请求后会自行下载图片再进行推理,你的请求体非常轻量。但前提是这张图片的URL必须能被模型服务商的服务器访问到,如果你传的是内网地址、本地路径或者带鉴权的链接,就会下载失败。Base64方式则相反,它把图片完整编码后塞进请求体,不依赖外部网络,适合处理本地文件、用户刚上传还没落盘的图片,缺点是请求体会变得很大,一张几MB的高清图编码后体积还会膨胀约三分之一,网络传输和解析开销都不小。
下面是用Python把本地图片转成Base64并发起请求的完整示例:
import base64
import requests
# 读取本地图片并编码为Base64字符串
def image_to_base64(path):
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
api_key = "你的API密钥"
img_b64 = image_to_base64("test.jpg")
resp = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={
"Authorization": "Bearer " + api_key,
"Content-Type": "application/json"
},
json={
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "用一句话概括这张图片"},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64," + img_b64
}
}
]
}
],
"max_tokens": 500
}
)
print(resp.json()["choices"][0]["message"]["content"])注意Base64方式的URL前缀写法:data:image/jpeg;base64,后面紧跟编码字符串。图片格式要和实际文件一致,png图片就要写成image/png,写错了部分接口会直接报格式错误。如果图片比较大,建议先做压缩和等比缩放,长边控制在1024到2048像素之间通常就足够模型识别细节了,没必要传原图,能显著降低费用和延迟。
三、不同厂商接口的格式差异与常见报错排查
虽然各家多模态接口思路相同,但字段命名差异不小。OpenAI使用image_url包裹一个含url的对象;Anthropic的Claude则使用image类型,直接包含source字段,其中要明确标注type、media_type和data三个子字段;谷歌Gemini又是另一套写法,图片放在parts数组里用inline_data标识。切换接口时最容易出问题的就是这些细节,建议参考对应官方文档逐字段核对。
以Claude为例,它的图片消息结构是这样的:
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": img_b64 # 纯Base64字符串,不需要data前缀
}
},
{
"type": "text",
"text": "描述一下这张图"
}
]
}
]可以看到Claude直接用data字段放Base64字符串,不需要data:image/jpeg;base64,这个前缀,这和OpenAI的要求正好相反,是新人最常踩的坑之一。
再说说几个高频报错。第一种是400错误提示invalid image format,多半是图片格式不在支持列表里,主流接口一般支持jpeg、png、gif和webp,bmp、tiff、heic这些格式需要先转换。第二种是报图片过大,通常单张图片限制在20MB以内,超了就压缩或裁剪。第三种是URL方式报下载超时或403,这说明模型服务器访问不到你的图片,检查链接是否公开、是否有防盗链、是否做了IP限制。还有一种隐蔽的问题是Base64字符串里混入了换行符或者拼接了data:前缀却用了不支持该写法的接口,都会导致解析失败。
最后提醒一点成本控制:多模态请求中图片会按尺寸折算成token计费,一张高清大图折算的token可能比一段长文本还多。批量处理图片时,除了压缩图片尺寸,还可以考虑让模型只输出关键结论、限制max_tokens,这样整体开销能降下来不少。跑通第一个请求后,建议先用小图测试流程,确认无误后再接入生产环境,排查问题会轻松很多。