Skip to content

[Feature Request] 分段回复缺少发送管控:连续消息场景下严重刷屏,且在途分段无法被新消息打断 #10722

Open

Description

@bhdhdjdjzjizi-arch

功能描述

功能背景

AstrBot 的「分段回复」(segmented_reply)会将大模型的一次长回复切分为多条短消息,按间隔逐条发送,以模拟真人的碎片化发送节奏。该功能在单轮问答场景下体验良好,但在真实聊天(尤其是群聊)的高频交互中,由于发送侧缺少队列管控,会产生明显的刷屏与消息错位问题。以下是在长期实际使用中稳定复现的痛点。

问题描述

1. 刷屏的乘数效应

分段回复开启后,一次回复会被拆成数条乃至十余条消息单独发送。当对方在短时间内连续发送 2~3 条消息时,每条消息都会各自触发一次完整的长回复并被再次切分,最终在同一会话内短时间倾泻二三十条消息。消息量随「触发次数 × 单次分段数」线性放大,在群聊中直接构成刷屏。

2. 在途分段不可中断,过期回复仍在继续发送

分段发送是一个持续数秒到数十秒的过程。在此期间,用户很可能已经补充了新信息、更正了上一个问题,甚至已经得到了想要的答案。但当前实现不会取消尚未发送的分段,机器人仍会把针对旧问题的内容逐条发完,既与用户的新意图脱节,也阻塞了后续回复。

3. 多个回复的分段相互交错,顺序错乱

当多个回复任务并发存在时,不同回复的分段会交叉抵达:旧回复的后半段可能出现在新回复的中间,对话顺序被彻底打乱,阅读体验很差,用户也难以判断每条消息到底在回答哪个问题。

复现场景

前置条件:开启分段回复,其余切分参数保持默认。

  1. 用户发送一个会触发长回复的问题 A(回复被切分为约 10 段,逐段发送);
  2. 分段尚未发完时,用户紧接着连续发送消息 B、C 进行补充或更正;
  3. 观察结果:B、C 各自生成长回复并被切分,加上 A 的剩余分段,短时间内同一会话出现二三十条消息,且来自不同回复的分段相互交错、无法中断。

实际影响

  • 群聊场景下直接刷屏,打扰其他成员,容易招致群成员与管理员反感;
  • 回复与问题错位,用户更正过的问题仍被旧答案「追着发」,可用性明显下降;
  • 分段越多,平台侧消息 API 调用次数越多,平白增加账号触发频率限制与风控的风险;
  • 上述问题会随使用频率升高而放大,恰恰在机器人最活跃的群里最严重。

期望行为 / 改进建议(供参考)

  1. 在途分段可取消(latest-wins):同一会话中,新的用户消息到达时,默认终止该会话尚未发送完毕的分段队列,不再发送过期内容;
  2. 输入侧防抖合并:对短时间内(阈值可配置,例如 1.5~3 秒)同一用户连续发送的多条消息先合并、再触发一次推理,从源头避免「N 条消息 → N 次长回复」;
  3. 单次回复分段数上限:为分段回复增加最大分段数配置(如 segmented_reply 的 max_segments),超出的部分合并为一条发送,或在回复总长度超过阈值时整体降级为单条发送;
  4. 会话内回复串行化:同一会话的多次回复严格按触发顺序串行发送,禁止不同回复的分段相互交错;
  5. 分段总时长约束:限制单个回复全部分段的总发送时长,避免一条回复长期占用会话通道;
  6. 以上行为建议均做成可配置项(开关 + 阈值),默认值偏保守(启用打断与上限),以便兼容已有用户的使用习惯。

环境信息

  • AstrBot 版本:v4.29.1(Docker 自托管)
  • 接入平台:QQ(aiocqhttp / NapCat),群聊与私聊均可复现
  • 相关设置:分段回复已开启,切分与间隔参数为默认

使用场景

No response

检查清单

  • 我已在 Issue 列表中搜索过相关功能,确认该功能想法从未被提出过。
  • 我建议的功能与 AstrBot 本体相关,而非在建议某一插件的功能。
  • 我已阅读并同意本项目的贡献者行为准则。
  • (可选)我愿意提交 PR 以帮助实现该功能。

Activity

  1. LIghtJUNction commented on Oct 25, 2026

    @LIghtJUNction
    Contributor

    源码审查确认分段发送目前是“一个 RespondStage 任务串行 sleep + send”,没有会话级发送协调:

    • astrbot/core/pipeline/respond/stage.py 的 RespondStage.process() 在每个组件前执行 await asyncio.sleep(i),随后直接 await event.send(...);没有 generation/取消标志、debounce、队列或 per-session lock。新事件会启动另一条 pipeline,旧任务仍会继续发送剩余片段。
    • event.clear_result() 只在本次循环结束后调用,不能撤销已排队的 sleep/send;异常只记录日志,未向协调层报告部分发送。

    建议把延迟发送抽到按 UMO(至少 platform id + message type + session id)管理的 coordinator:新一代响应到达时使旧 generation 失效并取消其 sleep;同一会话的发送通过队列/锁串行,跨会话互不阻塞。取消和进程退出时要 await 清理任务,避免悬挂 asyncio task;保留配置开关以兼容需要“完整播报”的场景,并设置最大分段数/总等待时间,防止超长回复拖住资源。

    预期效果是同一会话不会出现旧回复片段与新回复交错,取消后的片段不会继续发送,其他会话仍可并发;平台发送失败应能结束/标记当前 generation,而不是静默留下半条队列。建议测试:两轮响应交错时的顺序、sleep 中收到新事件的取消、最新一轮优先策略、适配器发送异常后的清理、进程 shutdown 任务回收,以及普通非分段回复回归。

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions