Skip to content

上下文管理策略 UI 容易误导:LLM 压缩模式下“最大携带对话轮数”仍会先于压缩生效 #7916

@Sisyphbaous-DT-Project

Description

@Sisyphbaous-DT-Project

问题描述

在 AstrBot 的「上下文管理策略」配置中,当前 UI 会同时展示:

  • 最多携带对话轮数
  • 丢弃对话轮数
  • 超出模型上下文窗口时的处理方式:按轮次截断 / 由 LLM 压缩上下文
  • 压缩时保留最近对话轮数
  • 用于上下文压缩的模型提供商 ID

从界面直觉上看,用户很容易理解成:

如果我选择了“由 LLM 压缩上下文”,那么上面的“最多携带对话轮数 / 丢弃对话轮数”就不再是主要逻辑,系统会主要根据模型上下文窗口来触发压缩。

但实际源码逻辑看起来并不是这样。

当前流程大致是:

  1. 每次请求 LLM 前,先根据 max_context_length 做基于轮次的硬裁剪;
  2. 然后才根据 provider 的 max_context_tokens 判断上下文 token 是否接近模型窗口上限;
  3. 如果超过阈值,再执行所选策略;
  4. 如果选择了 LLM 压缩但没有配置 llm_compress_provider_id,则会回退为按轮次截断。

也就是说,即使用户选择了“由 LLM 压缩上下文”,max_context_length 仍然会先于 LLM 压缩生效。

这会带来一个比较反直觉的结果:

如果用户把“最多携带对话轮数”设置得很低,例如 1、3、5,那么上下文在进入 token 判断之前就已经被裁得很短,后续 LLM 压缩几乎没有机会触发。

这不是明显的代码错误,但目前 UI 文案会让用户误以为“压缩”和“按轮数截断”是二选一关系,而实际更像是串联关系:

先按轮次限制历史,再按模型窗口决定是否压缩。

相关源码位置

我理解到的相关逻辑大致在这些位置:

  • astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py

    • 读取 max_context_lengthdequeue_context_lengthcontext_limit_reached_strategyllm_compress_keep_recentllm_compress_provider_id 等配置。
  • astrbot/core/agent/context/config.py

    • enforce_max_turns 注释中提到会在 compression 之前执行。
  • astrbot/core/agent/context/manager.py

    • ContextManager.process() 中先执行基于轮次的截断,再执行基于 token 的压缩判断。
  • astrbot/core/agent/context/compressor.py

    • LLMSummaryCompressor / TruncateByTurnsCompressor 都是基于 token 使用率阈值触发。
    • 默认阈值为 0.82
  • astrbot/core/agent/context/truncator.py

    • 实际执行按轮次裁剪逻辑。

当前容易造成的误解

误解 1:选择 LLM 压缩后,最大携带对话轮数就不重要了

实际并不是。
max_context_length 依然会先执行。

误解 2:LLM 压缩一定会发生

实际不一定。
如果 max_context_length 设置较低,或者 provider 的 max_context_tokens 没有正确配置,压缩可能根本不会触发。

误解 3:选择“由 LLM 压缩上下文”但不选择压缩模型,也会自动压缩

实际会回退为按轮次截断。
UI 里虽然有提示,但不够醒目,用户很容易忽略。

建议改进

方案 1:调整 UI 文案,明确执行顺序

建议把当前字段文案改得更明确,例如:

  • “最多携带对话轮数”

    • 改为:压缩前最多保留对话轮数
    • 说明:无论选择截断还是 LLM 压缩,都会先按该值限制历史轮数;-1 表示不按轮数限制。
  • “丢弃对话轮数”

    • 改为:轮次超限时一次丢弃轮数
    • 说明:当超过“压缩前最多保留对话轮数”时,一次丢弃多少轮旧对话;同时也可能作为压缩不可用时的回退截断参数。
  • “超出模型上下文窗口时的处理方式”

    • 改为:模型上下文接近上限后的处理方式
    • 说明:该策略只会在完成轮次限制后,且上下文 token 接近模型窗口上限时触发。

方案 2:选择 LLM 压缩时增加明显提示

当用户选择“由 LLM 压缩上下文”时,如果:

  • llm_compress_provider_id 为空

建议在 UI 旁边显示醒目提示:

未选择上下文压缩模型,将回退为按轮次截断。

如果:

  • max_context_length 设置得很低,例如 1、3、5

建议提示:

当前最大携带轮数较低,历史可能在触发 LLM 压缩前就被裁剪,导致压缩很少触发。

方案 3:文档中增加一段执行顺序说明

建议在上下文管理文档中明确写出类似下面的逻辑:

AstrBot 的上下文处理顺序为:

1. 先根据“最多携带对话轮数”限制历史轮数;
2. 再统计剩余上下文 token;
3. 当 token 接近模型上下文窗口上限时,才触发“按轮次截断”或“LLM 压缩”策略;
4. 如果选择 LLM 压缩但未配置压缩模型,则回退为按轮次截断。

额外相关点:群聊上下文感知容易和上下文管理混淆

另外,「群聊上下文感知 / 原聊天记忆增强」与「上下文管理策略」也容易被用户混淆。

我的理解是:

  • 「上下文管理策略」管理的是正式 AI 会话历史,也就是 user / assistant 对话轮次;
  • 「群聊上下文感知」管理的是群聊中未必触发机器人的普通聊天记录,会把最近 N 条群聊消息作为背景注入到请求里;
  • 因此,“最多携带对话轮数 = 100”并不等于“模型能看到群聊最近 100 条普通消息”;
  • 如果开启群聊上下文感知,并设置最大消息数量为 300,则模型可能额外接收这 300 条群聊背景,带来明显 token 开销。

建议文档或 UI 也补充说明:

群聊上下文感知是额外的群聊背景注入机制,不属于普通对话轮次历史;它可能与正式会话上下文叠加消耗 token。

预期效果

希望用户能够从 UI 和文档中直观看懂:

最大携带轮数是压缩前的第一道限制;LLM 压缩不是替代轮次限制,而是在轮次限制之后、上下文接近模型窗口时才触发。

这样可以避免用户误以为选择 LLM 压缩后,轮次设置就不再影响上下文,也能减少因为配置过低导致“压缩永远不触发”的困惑。

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreThe bug / feature is about astrbot's core, backend

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions