What happened / 发生了什么
群聊中,成员 A 发送仅包含 @机器人 的空提及后,AstrBot 会启动 60 秒的
session_waiter 等待补充内容。但该等待器只以 unified_msg_origin(群会话)作为
会话标识,不包含发送者 ID。
因此在等待窗口内,同群任意其他成员 B 发送的第一条非空消息会被等待器截获:
- B 的原始消息被
event.stop_event() 终止传播(其他插件也收不到这条消息);
- AstrBot 在消息段开头人为插入指向机器人的
At 组件;
- 浅复制后作为新事件重新投递,走完整流水线并触发 LLM 回复。
结果:一条完全没有提及机器人的普通群消息被当作 A 的补充输入,机器人向错误的
用户、以错误的上下文作出回复。
根因定位(已对照 master fb02c72 确认):
astrbot/core/utils/session_waiter.py 中 DefaultSessionFilter.filter() 仅返回
event.unified_msg_origin,群聊中该值对所有成员相同;
astrbot/builtin_stars/astrbot/main.py 的 empty_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_agent 中 event.stop_event() 无条件执行,而等待器对
message_str 为空的消息(纯图片/语音/文件/表情)直接 return 且不结束会话。
因此等待窗口内任意成员发送的纯图片都会被静默吞掉,且等待器继续存活,
60 秒内可反复发生(实测日志见下方"补充实验"段);
SessionWaiter._cleanup() 无条件 USER_SESSIONS.pop(self.session_id),同 key 的
旧等待器超时清理时会把新注册的等待器一并移除。
Reproduce / 如何复现?
前提:platform_settings.unique_session 保持默认关闭(开启后 WakingCheckStage
会将发送者并入 session_id,本问题会被掩蔽——这也解释了为何部分部署观察不到)。
- 群聊中启用空提及等待(
platform_settings.empty_mention_waiting,默认开启);
- 成员 A 发送仅含
@机器人 的消息(无正文);
- 在 60 秒等待窗口内(注意:窗口从机器人回复完成后开始计时),
同群另一成员 B 发送任意带文字的普通消息(不 @ 机器人);
- 观察: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」喵!你们是在排队点铃吗~
日志中可见的三个关键事实:
- B 的消息进入事件总线时(20:11:05.896)不含任何 At 段,9ms 后(.905)即以
[At:3878611411] 1 的形态重新出现——该 At 不可能来自平台往返,只能是
AstrBot 内部插入;
- 20:11:05.905/.906 两行空发送分别属于两个不同用户:B 的原始消息被 A 创建的
等待器消费并终止,A 的空 @ 事件同时收尾;
- 机器人最终回复中的“你们”表明 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」!...
这一段同时证实了两点:
- 纯图片被静默吞没:A 的
[图片](20:34:59.210)在 9ms 后出现
空发送 + 终止传播的组合(.219),机器人无任何回应;若图片是正常传播,
不会产生这两行。且等待器未被图片终结——20 秒后 A 的文字仍被捕获,
说明窗口期内每条非文本消息都会被反复丢弃;
- 吞没同样不区分发送者:本段中起等待器的是 B(1/49983336),被吞掉
图片和被截获文字的却是 A(hedssaz)——与主 bug 同源。
(最初在真实群聊中发现本问题时的表现与上述一致:真实成员的普通消息
“应该吧”被插入 At 并触发回复,NapCat 侧原始事件确认 atType=0、无 At 消息段。)
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct
What happened / 发生了什么
群聊中,成员 A 发送仅包含
@机器人的空提及后,AstrBot 会启动 60 秒的session_waiter等待补充内容。但该等待器只以unified_msg_origin(群会话)作为会话标识,不包含发送者 ID。
因此在等待窗口内,同群任意其他成员 B 发送的第一条非空消息会被等待器截获:
event.stop_event()终止传播(其他插件也收不到这条消息);At组件;结果:一条完全没有提及机器人的普通群消息被当作 A 的补充输入,机器人向错误的
用户、以错误的上下文作出回复。
根因定位(已对照 master fb02c72 确认):
astrbot/core/utils/session_waiter.py中DefaultSessionFilter.filter()仅返回event.unified_msg_origin,群聊中该值对所有成员相同;astrbot/builtin_stars/astrbot/main.py的empty_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_agent中event.stop_event()无条件执行,而等待器对message_str为空的消息(纯图片/语音/文件/表情)直接return且不结束会话。因此等待窗口内任意成员发送的纯图片都会被静默吞掉,且等待器继续存活,
60 秒内可反复发生(实测日志见下方"补充实验"段);
SessionWaiter._cleanup()无条件USER_SESSIONS.pop(self.session_id),同 key 的旧等待器超时清理时会把新注册的等待器一并移除。
Reproduce / 如何复现?
前提:
platform_settings.unique_session保持默认关闭(开启后 WakingCheckStage会将发送者并入 session_id,本问题会被掩蔽——这也解释了为何部分部署观察不到)。
platform_settings.empty_mention_waiting,默认开启);@机器人的消息(无正文);同群另一成员 B 发送任意带文字的普通消息(不 @ 机器人);
不再正常传播。
补充:第 3 步若 B 发送的是纯图片,则该消息被静默吞掉且等待器不结束。
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
OS
Windows
Logs / 报错日志
以下为一次受控复现的完整日志窗口(两个测试账号:
hedssaz/3315540459为成员 A,1/49983336为成员 B)。由于根因已定位至源码且可按上述步骤稳定复现,此处为问题时间窗内的完整 INFO 级日志;如需 Debug 级日志可随时补充。
对照组 —— 同一用户补充内容(设计内的正常路径):
实验组 —— 另一用户的消息被截获(bug 本体):
日志中可见的三个关键事实:
[At:3878611411] 1的形态重新出现——该 At 不可能来自平台往返,只能是AstrBot 内部插入;
等待器消费并终止,A 的空 @ 事件同时收尾;
两组唯一的变量是补充消息的发送者,行为完全一致——证明等待器不区分发送者。
补充实验 —— 连带问题"消息吞没"的实测(纯图片被静默丢弃):
这一段同时证实了两点:
[图片](20:34:59.210)在 9ms 后出现空发送 + 终止传播的组合(.219),机器人无任何回应;若图片是正常传播,
不会产生这两行。且等待器未被图片终结——20 秒后 A 的文字仍被捕获,
说明窗口期内每条非文本消息都会被反复丢弃;
图片和被截获文字的却是 A(hedssaz)——与主 bug 同源。
(最初在真实群聊中发现本问题时的表现与上述一致:真实成员的普通消息
“应该吧”被插入 At 并触发回复,NapCat 侧原始事件确认 atType=0、无 At 消息段。)
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct