多模態傳圖報 400,先分清兩種:
image_url既不是公網 URL,也不是data:image/png;base64,...這種 data URI;或者你用了個根本不支持視覺的模型。前者用 Spring AI 的Media配Resource交給框架轉 base64,後者換成gpt-4o系列等支持視覺的模型。
Issue & Env(案發現場與環境)
- 業務場景:用 Spring AI 多模態消息給模型傳圖,請求直接彈 400。
- 運行環境:Spring AI 1.1.x,OpenAI Chat 接口;多模態圖片走
content陣列裡的image_url。 - 觸發條件:
image_url不是合法 URL 或 data URI,或者模型不支持圖片輸入。
StackTrace(堆疊日誌)
接口返回的第一種形態,值本身不合法:
Error code: 400 - {'error': {'message': 'Invalid content type. image_url must be a valid URL or data URI.', 'type': 'invalid_request_error', 'param': 'messages[0].content[1].image_url', 'code': None}}
第二種形態,模型不支持視覺:
Error code: 400 - {'error': {'message': "Unsupported value: 'image_url' is not supported with this model.", 'type': 'invalid_request_error', 'param': 'messages[0].content[1]', 'code': 'unsupported_value'}}
這兩條是 OpenAI 視覺接口的常見報錯形態,具體文案以實際接口返回為準。落到 Spring AI 側,它會把它包成不可重試例外往外拋,因為 4xx 預設不重試。
Reproduce(復現程式碼)
最典型的錯法,把本地文件路徑當成 URL:
String response = ChatClient.create(chatModel).prompt()
.user(u -> u.text("描述这张图片")
.media(MimeTypeUtils.IMAGE_PNG, "file:///Users/me/pic.png"))
.call()
.content();
或者手工拼 base64 時漏了前綴:
String b64 = Base64.getEncoder()
.encodeToString(Files.readAllBytes(Path.of("/tmp/pic.png")));
// 少了 data:image/png;base64, 前缀,接口认不出来
Root Cause(根因分析)
image_url 這個字段只認兩種值:公網可訪問的 http(s) URL,或 data:image/xxx;base64,... 形式的 data URI。本地文件路徑、file://、內網地址、裸 base64 字符串都不合法,校驗階段直接 400。
用 Media 傳 Resource 時,Spring AI 會讀資源,並按 MIME 類型轉成 data URI。所以 ClassPathResource、FileSystemResource 都能用,框架替你補了前綴和編碼。你要是手動拼 base64,卻忘了加 data:image/png;base64,,就等於把一個裸字符串塞進 image_url,報的正是第一條。
第二條是另一個維度的事:模型不支持視覺輸入。像 gpt-3.5 系列就不接受圖片,請求裡出現 image_url 會被判 unsupported_value。這跟圖片合不合法無關,純粹是模型選錯了。
還有個常被忽略的成本點:圖片進上下文會按 token 計費。大圖轉 base64 後請求體動輒幾 MB,既拖慢請求,也抬高 token 消耗。
The Fix(解決方案)
交給框架:Media + MimeTypeUtils + Resource
首選這條,讓 Spring AI 自己讀文件、轉 data URI:
String response = ChatClient.create(chatModel).prompt()
.user(u -> u.text("描述这张图片")
.media(MimeTypeUtils.IMAGE_PNG, new ClassPathResource("/images/pic.png")))
.call()
.content();
磁盤文件用 FileSystemResource,類路徑資源用 ClassPathResource。別把路徑字符串直接當 URL 傳。
手工拼 base64 別漏前綴
確實要自己編碼時,data URI 前綴必須帶上:
String dataUri = "data:image/png;base64," + b64;
MIME 類型要和圖片真實格式一致,png 配 image/png,jpg 配 image/jpeg。
換成支持視覺的模型
先確認模型的輸入模態裡有圖像。gpt-4o 系列支持視覺,gpt-3.5 系列不支持。別拿純文本模型試圖片。
大圖先壓縮
base64 會讓體積漲約三分之一,再加上按 token 計費,傳原圖很虧。先縮放、裁剪再傳。真需要原圖細節,就用支持 URL 的方式,讓服務端自己去拉取。
References(參考來源)
- Images and vision — OpenAI 官方指南openai-docs
- Multimodality API — Spring AI 官方文檔spring-docs
- OpenAI Chat(Multimodal / Vision)— Spring AI 官方文檔spring-docs
