☰
大模型API多模态图片输入最小教程
2026/9/30 9:14:48 网站建设 项目流程

把image_url写成一个普通字符串,请求直接返回 400;模型明明收到了消息,却完全忽略你传的图片。接过多模态 API 的人对这两个现象都不陌生。问题通常不在模型本身,而在请求结构和几个容易填错的参数。这篇文章给一个可直接跑通的多模态图片输入最小脚本,再把本地图片、流式输出和排错一次讲清。

场景与前置条件

这篇文章要解决的是:用 OpenAI SDK 调用大模型时,怎样把一张图片作为输入交给模型,让它输出一段文本描述。这里说的不是图像生成。图像生成是模型画图给你,多模态理解是模型读图,也就是常说的图片输入。两者调用的端点和消息结构不同,不要混着用。

开工前,假设你手上已经有这四样东西:一个已经创建好的 API key,前缀通常是sk_live_;一个从模型广场详情页复制来的模型调用名,并且确认它带视觉能力;一个 Python 3.9 或以上的运行环境;以及通过pip install openai安装的 openai 包。操作系统可以是 macOS、Linux 或 Windows,环境行为没有特殊差异。图片不会以本地文件路径的形式直接传给接口,你需要先把它变成可访问的 URL,或者亲手转成 base64 data URI。这一点后面会展开。

环境准备

先装 SDK。终端里执行:

pipinstallopenai

装完以后,确认当前终端能读到环境变量SILVAMUX_API_KEY。为什么不用硬编码?因为密钥一旦写进源码,Git 提交记录会把它永久保留。即使后来删除文件,历史里依然找得到。对带sk_live_前缀的 key 来说,这个风险不低。CI 环境也更适合通过密钥注入,而不是改代码。

接下来是base_url。初始化 OpenAI client 时,base_url必须逐字符写成https://www.silvamux.com/api/v1。这个地址带www,少了www会解析到不存在的域名。很多首次接入的人就是卡在这一步,因为一些示例只写短域名,照抄过去就失败。把这两项准备好,后面代码就不会在连接层出问题。

最小可跑示例

下面这个脚本可以直接跑通。先看代码:

importosfromopenaiimportOpenAI client=OpenAI(api_key=os.environ['SILVAMUX_API_KEY'],base_url='https://www.silvamux.com/api/v1',)TINY_PNG='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII='response=client.chat.completions.create(model='glm-4.6',messages=[{'role':'system','content':'你是一个图片理解助手,只输出图片中能确认的信息。',},{'role':'user','content':[{'type':'text','text':'这张图片里有什么?'},{'type':'image_url','image_url':{'url':TINY_PNG},},],},],temperature=0.2,)print(response.choices[0].message.content)

这段代码先初始化 client,再调用大模型的对话补全接口。api_key不写具体密钥,从环境变量读取。base_url一旦写错,后面消息结构再标准也跑不通。

最关键的是user消息。content不是字符串,而是数组。数组里可以放多个片段,每个片段用type区分。text片段负责提问,image_url片段负责携带图片输入。image_url片段的值又是一个对象,里面才是url。很多人第一次写多模态请求时把这一层对象漏掉,接口会回一个 400。别急,看到 400 先去检查消息结构。

model填glm-4.6。这个调用名来自模型广场详情页,不要根据模型家族名自己拼版本。自己拼一个glm-4.6-vision或类似名字,通常会得到 404。temperature设 0.2 是为了让描述更稳,设 0 也可以。TINY_PNG是一张 1x1 的极小 PNG,用 data URI 表示,目的是验证整条链路。真的要做图片分析时,把它替换成真实图片 URL,或者使用下一节转好的本地图片 data URI。

逐步扩展

本地图片转 base64

线上 URL 适合测试,真实项目里经常要传本地文件。下面这个函数能把本地图片转成 data URI:

importbase64importmimetypesdefimage_to_data_uri(path:str)->str:mime=mimetypes.guess_type(path)[0]or'image/png'withopen(path,'rb')asf:encoded=base64.b64encode(f.read()).decode('ascii')returnf'data:{mime};base64,{encoded}'

调用时把它放在原本TINY_PNG出现的位置:

image_url={'url':image_to_data_uri('./cat.png')}

mimetypes.guess_type会根据扩展名猜 MIME 类型。猜不到时就回退到image/png。MIME 类型不要乱填。文件明明是 JPEG,你却声明成image/png,服务端可能按 PNG 去解码,最终得到损坏的像素。有时不会立刻报错,但识别结果会明显跑偏。

流式输出与末尾的 usage 块

如果图片识别结果很长,可以使用流式输出边读边显示。代码这样改:

messages=[{'role':'system','content':'你是一个图片理解助手。'},{'role':'user','content':[{'type':'text','text':'这张图片里有什么?'},{'type':'image_url','image_url':{'url':TINY_PNG}},]},]stream=client.chat.completions.create(model='glm-4.6',messages=messages,stream=True,)forchunkinstream:ifnotchunk.choices:continuedelta=chunk.choices[0].deltaifdeltaanddelta.content:print(delta.content,end='',flush=True)

流式传输时每个chunk只带一小段文本。末尾可能有一个choices为空但带有 usage 的 chunk。这个设计是为了把用量信息补在流的最后,不是异常。代码里必须先判断chunk.choices是否为空,否则直接访问chunk.choices[0]会触发索引错误。也不要只靠data: [DONE]判断结束,因为有些客户端对最后一块的处理时机不一致。

排错

401:密钥没有生效

报错文本通常长这样:

openai.AuthenticationError: Error code: 401 - {'error': {'message': 'invalid api key', 'type': 'gateway_error', 'code': 'AUTH_ERROR'}}

直接原因是当前进程读不到环境变量,或者 key 本身复制不完整。解决办法分两步:先确认创建时的 key 是不是以sk_live_开头;再确认当前终端里执行过 export。如果在 IDE 里运行,记得让 IDE 重启一次进程,否则环境变量不会生效。401 不应该靠重试解决,重试只是重复失败。

400:image_url 类型不对

这类错误常见的形态是状态码 400,error.code为BAD_REQUEST。八成原因是content用了字符串,或者image_url的值直接填成了 URL 字符串。多模态请求里content必须是数组,image_url必须是对象。两个条件少一个,服务端就会拒绝。修改方法就是回到最小示例,把消息结构原样复制,只替换图片的url值。

404:模型调用名是拼出来的

如果你看到 404 或类似model not found的返回,多半是把调用名写成了自己习惯的版本号。比如glm-4.6-vision。这类名字也许看起来合理,但平台不会自动做模糊匹配。调用名必须从模型广场的模型详情页直接复制。不要把上游厂商的名字照搬过来,两个命名体系不一定一致。

400:这个模型不支持图片输入

还有一种 400 会出现在模型调用名正确、但模型本身没有视觉能力的时候。接口会告诉你消息里不能带image_url。解决办法是把model换成一个明确标注支持图片输入的调用名。这个信息在模型详情页里能看到。不要猜,猜一次就浪费一次失败请求。

常见问题

问:图片输入和图像生成有什么区别?

答:图片输入是把图片交给模型去理解,返回的是文本;图像生成是模型根据文本生成图片。两者的消息结构和调用端点不同,这篇文章只谈前者。把图片输入的消息结构发给图像生成端点,通常也会收到参数错误。

问:为什么不能直接传本地文件路径?

答:OpenAI 兼容的image_url字段只接受远程 URL 或 data URI。直接写./cat.png,接口并不知道它是一个本地路径,也不会帮你去读文件系统。要传本地图片,就得先转成 base64 data URI,或者先把文件传到某个可访问的 HTTP 地址。

问:流式返回里为什么有一段 choices 为空?

答:那是服务端在流的末尾补充的用量信息。它保持 SSE 流的结构,但不再携带文本增量。客户端要兼容这个块,不能因为choices为空就当成错误。判断流程应该以流是否结束为准,不要依赖某一个特殊块。

以上示例在千木SilvaMux的 OpenAI 兼容端点上验证,模型调用名为 glm-4.6。模型调用名和当前可用模型以开发者文档为准。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询