起因
玩 AstrBot 的时候发现一个问题:bot 在每个群都是「失忆」的。
A 群里聊得热火朝天,群友说自己考试考砸了心情很差,结果到 B 群有人问「XX 怎么了」,bot 一脸懵——它完全不知道别的群发生了什么。
同样的,一个用户在私聊里跟 bot 聊了很多私人话题,换到群聊里 bot 就完全不认识你了。每个会话都是一座孤岛。
这不对。一个真正的「群友」应该是串得起各个群的话题的。
所以我写了这个插件:跨会话感知(Cross Session Awareness)。
它能干什么
两个核心能力:
1. 用户跨会话记忆
bot 会记住每个用户在不同群/私聊里说过的话(用 LLM 压缩成摘要),当这个用户出现在另一个会话时,bot 能自然地提起之前的话题。
比如你在 A 群说「刚买了个伊蕾娜手办,超好看」,然后去 B 群跟 bot 聊天,bot 可能会顺嘴问一句手办到了没——就像一个真正记得你的朋友。
2. 群聊上下文感知(v2.0 新增)
bot 不只记个人,还记整个群的对话流。它会把群聊对话定期总结成摘要,当别的群有人问起相关话题时,bot 能引用。
A 群发生了「小明被欺负了 → 原来是考试考砸了」这样的对话,B 群有人问「小明怎么了」,bot 能回答「他考试没考好,心情不太好」。
技术方案
消息捕获
插件挂了一个 priority=99990 的全局消息过滤器,捕获所有消息但不拦截(不影响其他插件)。太短的消息(<4 字)、命令消息(/ 或 ! 开头)会被跳过。图片消息也能处理,会提取图片 URL 交给视觉模型识别。
消息缓冲 + 防抖
不是每条消息都立刻调 LLM 做摘要——那太浪费了。用了一个 debounce 机制:
- 用户消息:同一用户在同一会话里连续发的消息,等静默 15 秒后合并成一批再摘要
- 群聊消息:群里静默 60 秒后,把积累的新消息打包生成群聊摘要
这样连续水群不会疯狂调 API。
摘要生成
用 LLM 把原始消息压缩成简短摘要:
- 用户个人摘要:第三人称,≤50 字,比如「深夜加班到凌晨3点,感到疲惫,有离职念头」
- 群聊摘要:场景描述,≤100 字,保留所有参与者和关键内容
如果消息没有实际意义(「嗯」「好的」),LLM 会返回「无需记忆」,直接丢弃。
没配置 LLM 模型也能用——退化为原文截断,只是没那么精炼。
记忆注入
关键一步:在 LLM 请求发出前(on_llm_request 钩子),把相关记忆注入到 system prompt 里。
注入两部分内容:
- 这个用户在别的会话说过什么(个人跨会话记忆)
- 别的群最近发生了什么(群聊上下文)
注入时会标注时间(「2小时前」)和来源(「在群123456」),让 LLM 能自然地引用。
方向控制
记忆流向可以精细控制:
- 私聊→群聊:用户私聊里说的话能不能在群里引用
- 群聊→私聊:群里的内容能不能在私聊里提起
- 群聊→群聊:A 群的内容能不能在 B 群引用
默认全开,但如果有隐私需求可以关掉「私聊→群聊」。
存储
JSON 文件持久化,简单粗暴。两个文件:
memories.json:用户个人跨会话记忆,按用户 ID 分组group_context.json:群聊原始消息日志 + 摘要
记忆有 TTL(默认 48 小时)和数量上限(每用户 20 条),自动清理过期数据。
一些设计决策
为什么用 debounce 而不是固定间隔?
人的聊天是突发的。可能 5 分钟内刷 20 条,然后沉默 2 小时。固定间隔要么浪费 API(空闲时也调),要么遗漏(高峰时来不及处理)。Debounce 只在「安静下来」后触发,自然又省钱。
为什么摘要队列用单 worker?
避免并发调 LLM 导致限流。摘要不是实时需求,晚几秒无所谓,但 429 就难受了。单 worker 串行处理,空闲 5 分钟自动退出,有新任务再拉起。
为什么注入 system prompt 而不是插入对话历史?
system prompt 是给 LLM 的「背景知识」,不会影响用户的对话流。如果插入对话历史,会改变上下文窗口的内容分布,可能导致模型行为异常。
管理命令
/跨群感知 状态 → 查看插件运行状态、记忆数量、缓冲区情况/跨群感知 群聊动态 → 查看各群的对话摘要/跨群感知 查看 → 查看自己的跨会话记忆/跨群感知 可用模型 → 列出可配置的 LLM 模型效果
配好模型之后,bot 就像一个真正参与了所有群的人。它不会生硬地说「你之前在 A 群说过…」,而是像真的记得一样自然地把话题串起来。
当然,前提是 prompt 写得好。我在注入模板里特别强调了「不要生硬引用」和「无关就别提」,避免 bot 变成复读机。
代码
开源在 GitHub:astrbot_plugin_cross_session_awareness
773 行 Python,零外部依赖,即装即用。
部分信息可能已经过时