Skip to content

[Bug] 历史中的 base64 图片导致内存耗尽(MemoryError):图片体积对上下文压缩不可见 + 轮数裁剪默认关闭 #10092

Description

@MostimaBridges

问题描述

在 v4.28.1 上,一个长期对话在上下文变长后,机器人开始持续报错,最终连纯文本消息也无法处理:

LLM 响应错误: All chat models failed: MemoryError:

随后同一会话中,用户发送任意纯文本(如「好多问题」)都会失败,且错误信息为空:

[2026-09-15 10:29:56.833] [Core][ERRO][v4.28.1] [agent_sub_stages.internal:541]: Error occurred while processing agent:

对用户可见的两条报错文本分别是:

LLM 响应错误: All chat models failed: MemoryError:
Error occurred while processing agent request:

MemoryError 由 AstrBot 自身进程抛出(不是服务商返回的),说明进程内存被耗尽。而发送纯文本也会失败这一事实说明问题不在当前消息,而在每轮都要重建的会话历史。

复核 v4.28.1 代码后,确认历史中的 base64 图片会持续累积,且现有的两道"安全网"都拦不住它。

1. 图片体积对 token 计数完全不可见

astrbot/core/agent/context/token_counter.py:34

IMAGE_TOKEN_ESTIMATE = 765

EstimateTokenCounter.count_tokens() 对每个 ImageURLPart 一律累加该常量:

elif isinstance(part, ImageURLPart):
    total += IMAGE_TOKEN_ESTIMATE

一张 3 KB 的图与一张 30 MB 的图,token 计数完全相同。上下文压缩的触发条件依赖该计数,因此图片再大也不会触发压缩

2. 轮数裁剪默认关闭

astrbot/core/config/agent_runner.py:114

max_turns = compression_config.get("max_turns", -1)

astrbot/core/agent/context/manager.py:60

# 1. 基于轮次的截断 (Enforce max turns)
if self.config.enforce_max_turns != -1:
    result = self.truncator.truncate_by_turns(...)

默认值为 -1(不限制),该整段被跳过。于是两道限制同时失效:轮数不裁剪,token 压缩又看不见图片体积。

3. base64 确实会进入持久化历史

astrbot/core/provider/entities.py:192ProviderRequest.assemble_context() 把图片引用内联为 base64 data URI:

# 3. Read image references without resizing or transcoding.
...
image_data = await resolve_image_ref_to_base64_data(image_url)
content_blocks.append(
    {"type": "image_url", "image_url": {"url": image_data.to_data_url()}},
)

同一函数中的注释明确说明这些 data URI 也会进入持久化历史:

# Capture bytes before event cleanup. Extra image paths must also
# reach providers and persisted history as portable data URIs.

astrbot/core/agent/runners/tool_loop_agent_runner.py:339/344/351 调用该方法生成运行期消息,随后由 dump_messages_with_checkpoints(all_messages) 写入会话历史;下一轮又由 astrbot/core/astr_main_agent.py:1358 原样读回:

req.contexts = json.loads(req.conversation.history)

4. 放大效应

每个请求路径上,同一份历史会被反复复制:SQLite 读出字符串 → json.loads 构建对象图 → 序列化为 HTTP 请求体 → provider SDK 再包一层。一份数十 MB 的 base64 历史,峰值内存可达数倍。

5. 加重问题的几处实现

  • 内存耗尽被当作模型故障重试。 tool_loop_agent_runner.py:625 使用 except Exception 捕获异常后 continue,会对同一份超大 payload 依次尝试全部 fallback provider,越重试越吃内存;最终在 :642 抛出 All chat models failed: {type}: {e}

  • 错误信息丢失异常类型。 agent_sub_stages/internal.py:541

    logger.error(f"Error occurred while processing agent: {e}")

    以及紧随其后的 error_text 同样只拼 {e}MemoryError() 无参数、str() 为空串,用户因此只看到 Error occurred while processing agent request:,完全无法判断是内存问题。

  • WebUI 预览会原样返回整份历史。 astrbot/dashboard/services/conversation_service.py:143get_conversation_detail() 直接返回 conversation.history,无分页与体积控制;超大会话会触发 astrbot/dashboard/api/app.py:178 的全局兜底,表现为 Internal server error

  • Dashboard 与机器人在同一进程。 astrbot/core/core_lifecycle.py:358asyncio.gather(*self.curr_tasks) 同时运行包括 Hypercorn 在内的所有任务,因此在 WebUI 打开超大会话可以把机器人本体一起拖垮。

补充一点:is_recoverable_image_error() 的设计是正确的——它明确把资源耗尽排除在"可跳过"之外(OSError + errno.ENOMEM 时返回 False),但上层又用 except ExceptionMemoryError 捞了回来。

如何复现

  1. 使用内置 Agent,保持 agent_runner.config.compression 的默认值(max_turns 未设置)。
  2. 在同一会话中多轮发送图片(尤其大图),穿插纯文本消息。
  3. 观察 req.contexts:每轮都会携带此前全部轮次的图片 data URI,且长度持续增长。
  4. 当累计体积超过可用内存时,任意消息(包括纯文本)都会抛出 MemoryError

本机观测:data/data_v4.db 体积 141 MB,文件修改时间为 2026-09-15 09:18:29,而首次 MemoryError 出现在 09:19:51。单会话的体积明细仍在统计中。

期望行为

  • 图片占用应纳入上下文体积核算,例如按 data URI 实际长度参与 token 估算,或提供独立的字节预算,使压缩/裁剪能够及时触发。
  • max_turns 建议有非无限的安全默认值,或至少在启用图片的场景下生效。
  • 资源耗尽不应进入 fallback 重试except Exception 应排除 MemoryError 等资源类异常,直接向上抛出。
  • 错误信息应保留异常类型{e} 对无参异常会得到空串,建议改为 {type(e).__name__}: {e} 或等价形式。
  • WebUI 会话预览应做分页或体积限制,避免单个超大会话影响服务端。

相关 issue

环境

  • AstrBot 版本:v4.28.1
  • 操作系统:Windows
  • 部署方式:Windows 源码部署(uv / venv)
  • 消息平台适配器:Onebot v11(NapCat)

错误日志

LLM 响应错误: All chat models failed: MemoryError:
[2026-09-15 10:29:56.787] [Core] [DBUG] [agent_sub_stages.internal:193]: ready to request llm provider
[2026-09-15 10:29:56.787] [Core] [DBUG] [agent_sub_stages.internal:221]: acquired session lock for llm request
[2026-09-15 10:29:56.833] [Core][ERRO][v4.28.1] [agent_sub_stages.internal:541]: Error occurred while processing agent:

检查清单

  • 我已在 Issue 列表中搜索过相关问题仍无法解决,或该问题从未被报告过。
  • 我已尝试过禁用所有插件,排除了可能是因插件导致的问题。
  • 我报告的问题与 AstrBot 本体相关,而非在报告某一插件的问题。
  • 我已阅读并同意本项目的贡献者行为准则。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions