Skip to content

[Bug] 群聊中空提及等待器会截获其他成员的消息(session_waiter 未绑定发送者) #9377

Description

@hedssaz

What happened / 发生了什么

群聊中,成员 A 发送仅包含 @机器人 的空提及后,AstrBot 会启动 60 秒的
session_waiter 等待补充内容。但该等待器只以 unified_msg_origin(群会话)作为
会话标识,不包含发送者 ID。

因此在等待窗口内,同群任意其他成员 B 发送的第一条非空消息会被等待器截获:

  1. B 的原始消息被 event.stop_event() 终止传播(其他插件也收不到这条消息);
  2. AstrBot 在消息段开头人为插入指向机器人的 At 组件;
  3. 浅复制后作为新事件重新投递,走完整流水线并触发 LLM 回复。

结果:一条完全没有提及机器人的普通群消息被当作 A 的补充输入,机器人向错误的
用户、以错误的上下文作出回复。

根因定位(已对照 master fb02c72 确认):

  • astrbot/core/utils/session_waiter.pyDefaultSessionFilter.filter() 仅返回
    event.unified_msg_origin,群聊中该值对所有成员相同;
  • astrbot/builtin_stars/astrbot/main.pyempty_mention_waiter 未传入
    session_filter,使用了上述默认值。

另外,仅含唤醒前缀的消息(is_wake_prefix_only 分支)会启动同一个等待器,
跨用户截获问题同样适用于该场景。

与文档矛盾docs/zh/dev/star/guides/session-control.md("自定义会话 ID 算子"
一节)写明「默认情况下,AstrBot 会话控制器会将基于 sender_id(发送人的 ID)作为
识别不同会话的标识,如果想将一整个群作为一个会话,则需要自定义会话 ID 算子」——
即文档承诺默认按发送人隔离,实现却是按群隔离,两者恰好相反。

回归引入点(git 考古):按发送人隔离是该功能的原始设计,当前行为是一次
后续改动引入的回归:

  • 24c20a19 / 0caff054(2025-03):会话控制诞生时 DefaultSessionFilter
    即为 return event.get_sender_id(),docstring 写明"返回发送者的 ID",
    文档与之匹配;
  • 4aa91ad5(2025-03-08):空提及功能的提交信息原文为"支持当消息只有@bot时,
    下一条发送人的消息直接唤醒机器人"——功能规格本身即按发送人;
  • cfae6550(2025-04-19,"perf: 修改默认会话过滤器标识符为umo"):两行改动
    将 key 从 get_sender_id() 换为 unified_msg_origin,文档与空提及调用点
    均未随之调整,跨用户截获自此引入。

需要说明:cfae6550(对应 #1326
动机原文为「避免默认标识符导致的跨会话问题」)修复的是 v3 key 的一个真实
缺陷——仅含 sender_id 的 key 会让同一用户在 A 群创建的等待器截获其在
B 群/私聊的消息(跨会话串线)。该 PR 当时未讨论群聊内多用户共享 key 的
副作用。因此简单 revert 会复活旧 bug;正确的 key 需要同时包含会话与
发送者两部分,下述两条修复路线均满足这一点。

连带问题(同一处代码,修复时建议一并考虑):

  • handle_session_control_agentevent.stop_event() 无条件执行,而等待器对
    message_str 为空的消息(纯图片/语音/文件/表情)直接 return 且不结束会话。
    因此等待窗口内任意成员发送的纯图片都会被静默吞掉,且等待器继续存活,
    60 秒内可反复发生(实测日志见下方"补充实验"段);
  • SessionWaiter._cleanup() 无条件 USER_SESSIONS.pop(self.session_id),同 key 的
    旧等待器超时清理时会把新注册的等待器一并移除。

Reproduce / 如何复现?

前提:platform_settings.unique_session 保持默认关闭(开启后 WakingCheckStage
会将发送者并入 session_id,本问题会被掩蔽——这也解释了为何部分部署观察不到)。

  1. 群聊中启用空提及等待(platform_settings.empty_mention_waiting,默认开启);
  2. 成员 A 发送仅含 @机器人 的消息(无正文);
  3. 在 60 秒等待窗口内(注意:窗口从机器人回复完成后开始计时),
    同群另一成员 B 发送任意带文字的普通消息(不 @ 机器人);
  4. 观察:B 的消息被添加机器人 At 后重新投递,触发 LLM 回复;B 的原始消息
    不再正常传播。

补充:第 3 步若 B 发送的是纯图片,则该消息被静默吞掉且等待器不结束。

AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器

  • AstrBot v4.25.5(已确认最新 v4.26.7 与 master fb02c72 中相关逻辑未变化)
  • 部署方式:Windows Docker Desktop 部署(Docker Desktop 4.74.0 (227015) Docker Engine 29.4.3,API 1.54)
  • 提供商:OpenAI 兼容接口
  • 消息平台:aiocqhttp(NapCat)

OS

Windows

Logs / 报错日志

以下为一次受控复现的完整日志窗口(两个测试账号:hedssaz/3315540459 为成员 A,
1/49983336 为成员 B)。由于根因已定位至源码且可按上述步骤稳定复现,此处为
问题时间窗内的完整 INFO 级日志;如需 Debug 级日志可随时补充。

对照组 —— 同一用户补充内容(设计内的正常路径):

[2026-07-24 20:09:43.625] event_bus: hedssaz/3315540459: [At:3878611411]      ← A 发空 @
[2026-07-24 20:09:47.674] respond.stage: Prepare to send - hedssaz/3315540459: [引用消息] 喵?这么晚还没睡呀~ ...   ← 60 秒窗口自此开始
[2026-07-24 20:10:45.696] event_bus: hedssaz/3315540459: 1                    ← A 自己补充“1”(无 At)
[2026-07-24 20:10:45.704] respond.stage: Prepare to send - hedssaz/3315540459:(空)
[2026-07-24 20:10:45.704] context_utils: astrbot - after_message_sent 终止了事件传播。   ← 原始事件被吞
[2026-07-24 20:10:45.705] event_bus: hedssaz/3315540459: [At:3878611411] 1    ← 内部插入 At 后重投
[2026-07-24 20:10:49.184] respond.stage: Prepare to send - hedssaz/3315540459: [引用消息] 喵?收到一个「1」!...

实验组 —— 另一用户的消息被截获(bug 本体):

[2026-07-24 20:10:57.240] event_bus: hedssaz/3315540459: [At:3878611411]      ← A 发空 @
[2026-07-24 20:11:02.085] respond.stage: Prepare to send - hedssaz/3315540459: [引用消息] 铃在这里呀~ ...   ← 窗口开始
[2026-07-24 20:11:05.896] event_bus: 1/49983336: 1                            ← B 发“1”,无任何 At 消息段
[2026-07-24 20:11:05.905] respond.stage: Prepare to send - 1/49983336:(空)
[2026-07-24 20:11:05.905] context_utils: astrbot - after_message_sent 终止了事件传播。   ← B 的原始事件被吞
[2026-07-24 20:11:05.905] event_bus: 1/49983336: [At:3878611411] 1            ← B 的消息被插入 At 后重投
[2026-07-24 20:11:05.906] respond.stage: Prepare to send - hedssaz/3315540459:(空)
[2026-07-24 20:11:05.906] context_utils: astrbot - after_message_sent 终止了事件传播。   ← A 的空 @ 事件随等待器结束收尾
[2026-07-24 20:11:10.249] respond.stage: Prepare to send - 1/49983336: [引用消息] 又是「1」喵!你们是在排队点铃吗~

日志中可见的三个关键事实:

  1. B 的消息进入事件总线时(20:11:05.896)不含任何 At 段,9ms 后(.905)即以
    [At:3878611411] 1 的形态重新出现——该 At 不可能来自平台往返,只能是
    AstrBot 内部插入;
  2. 20:11:05.905/.906 两行空发送分别属于两个不同用户:B 的原始消息被 A 创建的
    等待器消费并终止,A 的空 @ 事件同时收尾;
  3. 机器人最终回复中的“你们”表明 A 与 B 的输入已被混入同一对话上下文。

两组唯一的变量是补充消息的发送者,行为完全一致——证明等待器不区分发送者。

补充实验 —— 连带问题"消息吞没"的实测(纯图片被静默丢弃):

[2026-07-24 20:34:43.020] event_bus: 1/49983336: [At:3878611411]              ← B 发空 @,起等待器
[2026-07-24 20:34:48.365] respond.stage: Prepare to send - 1/49983336: [引用消息] 喵?铃在呢~ ...   ← 窗口开始
[2026-07-24 20:34:59.210] event_bus: hedssaz/3315540459: [图片]               ← A 发纯图片(无文字)
[2026-07-24 20:34:59.219] respond.stage: Prepare to send - hedssaz/3315540459:(空)
[2026-07-24 20:34:59.219] context_utils: astrbot - after_message_sent 终止了事件传播。   ← 图片被吞,机器人无任何回应
[2026-07-24 20:35:19.484] event_bus: hedssaz/3315540459: 111                  ← 20 秒后 A 发文字
[2026-07-24 20:35:19.491] respond.stage: Prepare to send - hedssaz/3315540459:(空)
[2026-07-24 20:35:19.491] context_utils: astrbot - after_message_sent 终止了事件传播。   ← 原始文字事件被吞
[2026-07-24 20:35:19.491] event_bus: hedssaz/3315540459: [At:3878611411] 111  ← 插入 At 后重投
[2026-07-24 20:35:19.492] respond.stage: Prepare to send - 1/49983336:(空)
[2026-07-24 20:35:19.492] context_utils: astrbot - after_message_sent 终止了事件传播。   ← B 的空 @ 事件随等待器结束收尾
[2026-07-24 20:35:23.060] respond.stage: Prepare to send - hedssaz/3315540459: [引用消息] 三个「1」!...

这一段同时证实了两点:

  1. 纯图片被静默吞没:A 的 [图片](20:34:59.210)在 9ms 后出现
    空发送 + 终止传播的组合(.219),机器人无任何回应;若图片是正常传播,
    不会产生这两行。且等待器未被图片终结——20 秒后 A 的文字仍被捕获,
    说明窗口期内每条非文本消息都会被反复丢弃;
  2. 吞没同样不区分发送者:本段中起等待器的是 B(1/49983336),被吞掉
    图片和被截获文字的却是 A(hedssaz)——与主 bug 同源。

(最初在真实群聊中发现本问题时的表现与上述一致:真实成员的普通消息
“应该吧”被插入 At 并触发回复,NapCat 侧原始事件确认 atType=0、无 At 消息段。)

Are you willing to submit a PR? / 你愿意提交 PR 吗?

  • Yes!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreThe bug / feature is about astrbot's core, backendbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions