fix: 修复记忆力差和跨群聊上下文泄漏

记忆嵌入修复:
- 新增 memory.APIEmbedder 使用 text-embedding-3-small 替代 SimpleEmbedder
- Extractor 保存记忆时自动生成向量嵌入
- Embedder 接口增加 IsAvailable() 方法

跨群聊上下文隔离:
- Thinker 新增 thinkSessionID 字段,performThink 启动时绑定会话
- storeThought 优先使用绑定的 session 推送思考结果
- 防止思考过程中其他群消息改变 activeSessionID 导致串台

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-03 12:25:05 +08:00
parent 197c86fa72
commit 13a2c69d5e
5 changed files with 155 additions and 9 deletions
+13 -6
View File
@@ -229,6 +229,7 @@ type Thinker struct {
lastProactiveNs atomic.Int64 // UnixNano (replaces lastProactiveMsgTime)
lastOnlineChange time.Time
userSessionID string // 当前活跃的 session ID (用于重连)
thinkSessionID string // 当前思考周期绑定的 session(防止跨群串台)
// 时区设置 (默认 Asia/Shanghai,可通过 TZ 环境变量覆盖)
timeLocation *time.Location
@@ -1114,6 +1115,11 @@ func (t *Thinker) performThink(triggerReason string) {
log.Printf("[后台思考] 开始思考周期 (触发原因=%s, 计数=%d)...", triggerReason, currentCount)
// 捕获当前活跃 session,防止思考过程中其他群的消息改变 activeSessionID 导致串台
t.muLock()
t.thinkSessionID = t.activeSessionID
t.muUnlock()
// 0. 让步于前台——如果用户最近有活动(非post_chat),跳过本次思考。
if triggerReason != "post_chat" {
t.muLock()
@@ -1133,15 +1139,13 @@ func (t *Thinker) performThink(triggerReason string) {
return
}
// 2. 获取当前活跃会话的对话历史(优先活跃会话,回退到管理员主会话
// 2. 获取思考周期绑定会话的对话历史,回退到管理员主会话
var convHistory []model.LLMMessage
if t.convStore != nil {
t.muLock()
sessionID := t.activeSessionID
sessionID := t.thinkSessionID
if sessionID == "" {
sessionID = t.adminSessionID
}
t.muUnlock()
if sessionID != "" {
convHistory = t.convStore.GetHistory(sessionID, 30)
@@ -1922,8 +1926,11 @@ func (t *Thinker) storeThought(content string, toolCallsJSON string, toolCallCou
// Extract proactive message and optional platform target.
proactiveMsg, proactiveTarget := t.extractProactiveMessage(content)
// Prefer active session, fall back to admin main session.
pushSessionID := t.activeSessionID
// 优先使用思考周期绑定的 session(防止跨群串台),其次活跃 session,最后管理员主会话
pushSessionID := t.thinkSessionID
if pushSessionID == "" {
pushSessionID = t.activeSessionID
}
if pushSessionID == "" {
pushSessionID = t.adminSessionID
}