DeepSeek Harness 图片超过 8 张必报 400:image count limit 怎么修
一段会话里陆陆续续贴了十几张截图,前面几轮都正常,突然从某一轮开始每条消息都 400,删掉刚发的那张也没用。 DeepSeek Harness(DSH)对图片有像素和字节限制,却没有张数限制;服务端那边按「每次请求最多 8 张」计数,历史图片又都会随上下文重发,于是一旦累计超限,整个会话就卡死。
DeepSeek Harness 判断是不是图片计数超限
一句话结论:会话里累计图片多、每轮都 400、且换模型/重试都无效,基本就是计数超限;如果只有带图的消息失败,纯文本正常,也可以确认。 三步核对:
- 数一下会话里出现过的图片附件总数,尤其是截图和粘贴的剪贴板图片;预期总数接近或超过 8。
- 发一条纯文本消息;预期也 400,说明历史仍在带图,若成功则只有带图请求超限。
- 新建会话贴一张图;预期正常,排除图片格式问题。
据讨论里的源码核对,llm-pi-ai 只有 adapter.ts:410-415 的像素/字节限制和 config.ts:167-173 的请求字节限制,没有计数上限;stream.ts:48-49 把 400 映射为 INVALID_REQUEST,且不在重试白名单(#9981)。模型图片能力的常规配置可以对照 模型图片能力。
为什么删掉最后一张图,DSH 仍然 400
一句话结论:请求带的是整段上下文,不是最后一轮;最后一张删了,前面的十几张还在。
- DSH 把历史消息按轮次组装成请求。
- 每个带图轮次的附件都会进入
parts。 - 服务端统计的是整个请求里的图片总数。
- 只要总数超过 8,任何一轮请求都超限。
- 删除最后一轮只减少一张,不足以回到上限内。
DeepSeek Harness 的三种裁剪与修复
一句话结论:只是偶尔超限就手工清理,团队经常贴图就改适配器,旧会话价值不高就重建。
修法一:从历史里移除旧图片
不装插件、不改代码时,最直接的办法是把旧轮次的图片附件去掉,只保留最近几张和文字结论。
- 备份会话目录;预期可以回滚到原始状态。
- 从最早带图的轮次开始,把图片替换为一句文字描述,例如「截图:报错为 X」;预期图片被移除、消息仍在。
- 每删一批就发一条纯文本消息;预期不再 400。
- 保留最近 2-3 轮的关键图片;预期模型仍能理解当前任务。
- 如果会话仍需长期使用,把关键图片另存到工作区,用路径引用代替内嵌;预期信息不丢。
修法二:改适配器,加 oldest-first 计数裁剪
根治点在 toPiContext 的替换循环,加一个 maxImages 谓词,超限时优先丢掉最旧的图片。 适合维护 fork 或提交上游。
- 在
adapter.ts:407的toPiContext里统计当前图片数量;预期序列化前就知道张数。 - 当数量超过配置的
maxImages(默认 8)时,从最旧的图片开始替换为文本占位;预期请求回到上限内。 - 把
maxImages做成 provider 级配置,不要写死 8;预期不同端点可以各自设置。 - 在请求日志里记录被裁剪的张数;预期用户能看到发生了什么。
- 补测试:7 张、8 张、12 张三种输入各一条;预期边界行为一致。
三种裁剪策略的取舍:丢最旧保留最近上下文,适合连续排障;丢非关键按附件类型或来源过滤,逻辑复杂但信息损失小;全部转文字最省 token,但模型看不到视觉细节。
修法三:重建一个精简会话
旧会话图片太多、清理成本高时,把结论和少量关键图迁到新会话,最快恢复工作。
- 在旧会话里导出最近几轮的文字结论和待办;预期工作状态以文字保留。
- 新建会话,粘贴文字背景,只带 1-2 张关键图;预期请求远低于上限。
- 先发纯文本,再发带图请求;预期两次都返回 200。
- 把旧会话归档;预期以后不会误点进去继续失败。
DSH 插件修复后的验证与避坑
一句话结论:构造一个 12 张图的会话,确认只发送 8 张、最近的关键图仍在,才算修好。 验证步骤:
- 构造一个包含 12 张图片的会话;预期请求里只发送 8 张,且不再 400。
- 检查被裁剪的图片是否按 oldest-first 顺序;预期最近的关键图仍在。
- 发一条纯文本消息;预期历史不再因为图片超限而失败。
- 切换模型再试一次;预期计数逻辑与 provider 无关。
避坑要点:
- 不要把 400 当成密钥或额度问题,图片超限的 400 没有额度提示。
- 压缩不会自动删图片,压缩后的历史仍可能带图,需要显式清理。
- 不同端点上限可能不同,
maxImages应可配置,不要写死。 - 旧轮次里的图片如果是工具返回的,替换成文本前先确认该结果没有其他消费者。
- 如果图片是唯一的证据,先保存到工作区再裁剪,不要把文件删掉。
- 裁剪后模型仍提示缺少视觉信息时,保留最近两轮并重新上传关键图。
- 多个 provider 共用同一会话时,按最严格的图片上限设置
maxImages。 - 讨论里没有官方修复版本,0.2.1-rc.2 仍可复现;升级后以 release notes 为准,别把某个 DSH插件 的旧行为当成已修。
DeepSeek Harness 图片 400 常见问题
Q:DeepSeek Harness 会话里图片一多就每轮 400,删掉最后一张也不行,为什么? A:DeepSeek Harness 每轮 400 是因为上限按整段请求里的图片总数计算,不是按最后一轮。历史图片仍随上下文一起发送,只要累计超过 8 张,每次请求都超限;需要从历史里移除部分图片,或让适配器在组装请求时做 oldest-first 裁剪。
Q:DeepSeek Harness 有 maxPixels 和 maxBytes,为什么还会被图片数量卡住? A:DeepSeek Harness 的 maxPixels 和 maxBytes 约束的是单张图片的像素与字节,不约束张数。八张很小的图片可以通过尺寸检查,但总数仍超过服务端上限;适配器没有计数谓词,于是把超限请求原样发出去,服务端返回 400。
Q:DeepSeek Harness 的图片 400 为什么不自动重试,切模型也没用? A:DeepSeek Harness 的图片 400 被映射为 INVALID_REQUEST,不在默认重试白名单里;切模型后如果仍带同样的历史图片,超限依旧存在。要恢复必须减少请求里的图片数量,或换一个支持更多图片的端点。
Q:怎么在不丢关键信息的前提下,让 DeepSeek Harness 旧会话重新可用? A:要让 DeepSeek Harness 旧会话重新可用,先把关键图片另存到工作区,用文字描述替代;再从最早的非关键轮次开始移除图片附件,保留最近几轮。也可以重建一个精简会话,把关键结论和少量图片带过去。
常见问题
DeepSeek Harness 每轮 400 是因为上限按整段请求里的图片总数计算,不是按最后一轮。历史图片仍随上下文一起发送,只要累计超过 8 张,每次请求都超限;需要从历史里移除部分图片,或让适配器在组装请求时做 oldest-first 裁剪。
DeepSeek Harness 的 maxPixels 和 maxBytes 约束的是单张图片的像素与字节,不约束张数。八张很小的图片可以通过尺寸检查,但总数仍超过服务端上限;适配器没有计数谓词,于是把超限请求原样发出去,服务端返回 400。
DeepSeek Harness 的图片 400 被映射为 INVALID_REQUEST,不在默认重试白名单里;切模型后如果仍带同样的历史图片,超限依旧存在。要恢复必须减少请求里的图片数量,或换一个支持更多图片的端点。
要让 DeepSeek Harness 旧会话重新可用,先把关键图片另存到工作区,用文字描述替代;再从最早的非关键轮次开始移除图片附件,保留最近几轮。也可以重建一个精简会话,把关键结论和少量图片带过去。
相关术语
- maxRequestImageBytes
- maxRequestImageBytes 是 DeepSeek Harness 对单次请求图片总字节的限制,属于尺寸型约束。它不会限制图片张数,因此不能防止「很多小图」触发服务端计数上限。— https://github.com/deepseek-ai/deepseek-harness/discussions/9981
- INVALID_REQUEST
- INVALID_REQUEST 是 DeepSeek Harness 对上游 400 的统一映射,表示请求本身不合法。它默认不重试,因为重发相同内容通常会得到相同结果。— https://github.com/deepseek-ai/deepseek-harness/discussions/9981
- oldest-first
- oldest-first 是 DeepSeek Harness 在组装图片上下文时的替换顺序,优先淘汰最旧的图片。它已有替换循环,但缺少张数上限判断。— https://github.com/deepseek-ai/deepseek-harness/discussions/9981
来源
- #9981 — More than 8 images per request wedges the session· deepseek-ai(GitHub Discussions)
- DeepSeek Harness 官方仓库· deepseek-ai(GitHub)