三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

AI多Agent协作系统实战(三十三):AI被一张截图噎住了:纯文本模型的Session里,不该有图片

AI多Agent协作系统实战(三十三):AI被一张截图噎住了:纯文本模型的Session里,不该有图片

一个凌晨2点的报错:unknown variant 'image_url', expected 'text'——我们的AI助手,被自己聊天记录里的一张截图噎死了。

事情是这样的

我们的系统里有四个AI"员工":小密(统筹)、小虾(开发)、小牛(测试)、小白(体验)。他们通过一个类似微信的收件箱机制协作——小密派任务,小虾开发,小牛测试,小密复核。

一切运转良好,直到那天凌晨。

任务派发出去后,小虾迟迟没有回复。去看它的会话界面,一条刺眼的红色报错循环刷屏:

错误: LLM request failed: provider rejected the request schema or tool payload.

翻译过来就是:大模型拒绝了我的请求。再往深挖,原始报错是:

400 Failed to deserialize the JSON body into the target type: messages[161]: unknown variant `image_url`, expected `text`

messages[161]——第161条消息。也就是说,我们的AI在跟大模型对话时,发过去的第161条消息是一张图片,而对方是纯文本模型,不认图片。

图片是怎么混进对话里的

小虾是开发Agent,它的工作流里有一步是"截图验证"——改完代码后用无头浏览器截图,确认页面效果。

截图是任务要求的证据,但它有个副作用:截图会以图片消息的形式,进入Agent的会话记录

我们的Agent用的是deepseek-v4-flash——一个纯文本模型。它不支持图片输入。但Agent框架并不管这些:你给了截图,我就把截图塞进消息流里发给模型。

于是一次、两次、三次……每次开发任务都攒一两张截图,会话记录越来越大。终于有一天,消息列表里第161条是一张图,模型直接拒绝解析整个请求。

不是某一张图特别大,是积累的图片让整个会话"变味"了。

排查过程:先看Session

报错出现后,第一反应是查Agent的会话状态(Session)。

小虾 Session: 4.2MB,27个会话文件

27个会话文件、4.2MB——对于一个纯文本模型来说,这是危险的信号。会话里塞满了截图、工具输出、长上下文。模型每次请求都要携带这4.2MB的"历史包袱",而其中还藏着模型根本不支持的图片。

查了模型配置、查了API密钥、查了网络……最后才在错误日志里看到那一行被淹没的原始报错:

messages[161]: unknown variant `image_url`

不是配置错了,不是密钥失效,是消息内容本身不合法——模型不认识图片这种消息类型。

修复:给Session定个"卫生标准"

问题清楚了,修复思路也就出来了——Session需要定期清理

我们写了一个自动清理脚本,规则很简单:

# 清理规则ifsession_size>3MB:# 会话太大 → 清delete(session)ifcontains_image(session):# 会话里有图片 → 清delete(session)

两个条件,命中任意一个就清理。清理掉之后,Agent下次启动会用干净的会话重新开始——它的指令在HEARTBEAT文件里(持久化),不依赖会话记忆,所以清理不会让它"失忆"。

然后把这个脚本挂到了每分钟运行的定时任务里。从此以后:

以前: Session积累图片 → 模型400拒绝 → Agent卡死 → 人工救火 现在: Session超3MB或含图 → 自动清理 → Agent永远用干净会话

这件事教给我们的三件事

第一,模型的能力边界是硬约束。不是所有模型都能看图。用纯文本模型,就要接受"图片不能进对话"这个事实。框架不会替你考虑这些——工具给了截图就塞进去,管你模型认不认。

第二,Session是会被"污染"的。会话记录不只是对话历史,它会积累工具输出、截图、中间过程。对支持图片的模型,这是上下文;对纯文本模型,这是毒药。长期运行的Agent,Session卫生和代码卫生一样重要。

第三,报错要看原始信息。provider rejected the request schema or tool payload这种报错模棱两可——真正有用的信息藏在rawError里:unknown variant 'image_url', expected 'text'框架层的报错是给运维看的,底层的原始报错才是给排查者看的。

结尾

那天凌晨,我们给AI做完"会话大扫除"之后,它立刻就活了过来——继续开发、继续测试、继续认真回复每一个任务。

后来我在清理脚本的注释里写了一句话:

对话记录里有截图,是给会看图的模型准备的。给纯文本模型的对话里塞图片,就像给一个不识字的人读报纸——不是他不想看,是这报纸递错了人。

工具不会替你想"这个模型认不认这张图"——这是你的事。

(完)


本文是"多Agent派发系统"系列第33篇。前情:一个换行符毁了一张任务表、为了省token拆了1613行代码、0字节的信任危机……技术事故都是相似的:看起来是灵异事件,查到底都是人的疏忽。

欢迎加入QQ频道共同交流。

← 返回列表