问题出现得很突然,日志倒是留得挺全。用 requests 2.32 调自建中转服务的 /v1/chat/completions,把 4 张商品图各自 base64 后拼进 content 数组,模型只回答第 1 张和第 2 张,后两张完全没提到。同一个 body 直接打官方接口能识别 4 张,中转层返回的 usage 里 image token 也只有两份,日志显示 200。

刚开始盯着「请求体超过中转层 6MB 的上限,尾部被静默掐掉」,折腾了半个多小时,排掉了。

原因不止一个,得分开看。中转服务按 data: 前缀切分,base64 里的换行把串截断了。另一种情况是两条 image_url 之间没插 text 分隔,解析时被合并成一条。

少数情况下是base64 未去掉换行符,解码长度对不上就直接跳过该元素。

量出整个请求体的实际大小

python -c "import os;print(os.path.getsize('body.json'))"

接着把每张图压到 300KB 以内再拼接

python -c "from PIL import Image;im=Image.open('p3.png').convert('RGB');im.thumbnail((1024,1024));im.save('p3.jpg',quality=80)"

再往下确认 base64 串里没有换行和空格

python -c "import base64;s=base64.b64encode(open('p3.jpg','rb').read()).decode();print(len(s), '\n' in s, ' ' in s)"

等前面都确认过,再绕开拼接改成逐张请求后比较结果

curl http://127.0.0.1:8080/v1/chat/completions -H 'Content-Type: application/json' --data-binary @body.json

别只看报错消失,验证要多做一步。四张图的特征在回答里都能对应上,usage 里的 image token 是单张的四倍,中转日志显示请求体被完整接收。

中转层很少做边界保护,超限时掐掉尾巴不报错,客户端看到的是 200,这类问题只能靠对比 usage 和原始请求体大小来发现。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。

作者 9823189