fix: 修复后台思考离线时被拉长到30分钟的问题

离线最小思考间隔从硬编码30分钟改为可配置环境变量 THINK_OFFLINE_GAP_SEC (默认10分钟)。
新增服务启动后首次思考触发,确保即使无客户端连接也能立即开始后台思考。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 12:52:00 +08:00
parent 70f8b30d04
commit 3c2bf9206f
+47 -33
View File
@@ -82,6 +82,10 @@ type Thinker struct {
// 默认 30 秒
minThinkGap time.Duration
// 离线时最小思考间隔:用户不在线时的周期思考间隔
// 默认 10 分钟
offlineThinkGap time.Duration
// 主动消息最小间隔:避免频繁推送打扰用户
// 默认 30 分钟,设为 0 则每次思考都可推送
proactiveMsgMinGap time.Duration
@@ -186,11 +190,12 @@ func (t *Thinker) UpdatePresence(online bool, sessionID string) {
// ThinkerConfig 后台思考配置
type ThinkerConfig struct {
Enabled bool
ThinkInterval time.Duration // 周期性思考间隔 (默认 5 分钟,0 = 禁用)
SilenceTimeout time.Duration // 用户沉默多久后昔涟可以主动搭话 (0 = 禁用)
PostChatDelay time.Duration // 对话后多久触发思考
MinThinkGap time.Duration // 两次思考最小间隔
Enabled bool
ThinkInterval time.Duration // 周期性思考间隔 (默认 5 分钟,0 = 禁用)
SilenceTimeout time.Duration // 用户沉默多久后昔涟可以主动搭话 (0 = 禁用)
PostChatDelay time.Duration // 对话后多久触发思考
MinThinkGap time.Duration // 两次思考最小间隔 (在线)
OfflineThinkGap time.Duration // 两次思考最小间隔 (离线,默认 10 分钟)
}
// DefaultThinkerConfig 默认配置
@@ -204,11 +209,12 @@ type ThinkerConfig struct {
// PROACTIVE_MSG_MIN_GAP_SEC — 主动消息最小间隔 (默认 1800 = 30分钟,0 = 禁用)
func DefaultThinkerConfig() ThinkerConfig {
return ThinkerConfig{
Enabled: getEnvBool("ENABLE_BACKGROUND_THINKING", true),
ThinkInterval: getEnvDuration("THINK_INTERVAL_SEC", 300),
SilenceTimeout: getEnvDuration("THINK_SILENCE_TIMEOUT_SEC", 120),
PostChatDelay: getEnvDuration("THINK_POST_CHAT_DELAY_SEC", 5),
MinThinkGap: getEnvDuration("THINK_MIN_GAP_SEC", 30),
Enabled: getEnvBool("ENABLE_BACKGROUND_THINKING", true),
ThinkInterval: getEnvDuration("THINK_INTERVAL_SEC", 300),
SilenceTimeout: getEnvDuration("THINK_SILENCE_TIMEOUT_SEC", 120),
PostChatDelay: getEnvDuration("THINK_POST_CHAT_DELAY_SEC", 5),
MinThinkGap: getEnvDuration("THINK_MIN_GAP_SEC", 30),
OfflineThinkGap: getEnvDuration("THINK_OFFLINE_GAP_SEC", 600),
}
}
@@ -235,9 +241,10 @@ func NewThinker(
thinkInterval: cfg.ThinkInterval,
silenceTimeout: cfg.SilenceTimeout,
proactiveMsgMinGap: getEnvDuration("PROACTIVE_MSG_MIN_GAP_SEC", 1800),
postChatDelay: cfg.PostChatDelay,
minThinkGap: cfg.MinThinkGap,
memoryStore: memoryStore,
postChatDelay: cfg.PostChatDelay,
minThinkGap: cfg.MinThinkGap,
offlineThinkGap: cfg.OfflineThinkGap,
memoryStore: memoryStore,
toolRegistry: toolRegistry,
convStore: convStore,
@@ -276,8 +283,15 @@ func (t *Thinker) Start() {
go t.periodicThinkLoop()
}
log.Printf("[后台思考] 已就绪 — 周期=%v + 事件驱动模式 (静默超时=%v, 对话后延迟=%v, 最小思考间隔=%v, 管理员=%s)",
t.thinkInterval, t.silenceTimeout, t.postChatDelay, t.minThinkGap, t.adminUserID)
log.Printf("[后台思考] 已就绪 — 周期=%v + 事件驱动模式 (静默超时=%v, 对话后延迟=%v, 在线最小间隔=%v, 离线最小间隔=%v, 管理员=%s)",
t.thinkInterval, t.silenceTimeout, t.postChatDelay, t.minThinkGap, t.offlineThinkGap, t.adminUserID)
// 启动后首次思考:延迟 5s,让服务完全初始化后再触发
go func() {
time.Sleep(5 * time.Second)
log.Println("[后台思考] 首次启动思考 (startup)")
t.performThink("startup")
}()
}
// Stop 停止后台思考器
@@ -441,27 +455,27 @@ func (t *Thinker) periodicThinkLoop() {
sinceLastUser := time.Since(t.lastUserMessage)
t.mu.Unlock()
// Phase 2: 离线时降低思考频率 (每30分钟一次,而非5分钟)
t.mu.Lock()
isOffline := !t.userOnline
t.mu.Unlock()
offlineMinGap := 30 * time.Minute
// 离线时降低思考频率(可配置,默认 10 分钟)
t.mu.Lock()
isOffline := !t.userOnline
t.mu.Unlock()
offlineMinGap := t.offlineThinkGap
// 跳过条件:用户最近在活动(30s 内有消息),说明正在对话中
if sinceLastUser < 30*time.Second {
log.Printf("[后台思考] 用户在 %v 前发过消息,跳过周期性触发 (留给事件驱动处理)", sinceLastUser.Round(time.Second))
continue
}
// 跳过条件:用户最近在活动(30s 内有消息),说明正在对话中
if sinceLastUser < 30*time.Second {
log.Printf("[后台思考] 用户在 %v 前发过消息,跳过周期性触发 (留给事件驱动处理)", sinceLastUser.Round(time.Second))
continue
}
if isOffline && sinceLastThink < offlineMinGap {
log.Printf("[后台思考] 用户离线,距上次思考仅 %v,跳过 (离线模式最小间隔=%v)", sinceLastThink.Round(time.Second), offlineMinGap)
continue
}
if isOffline && sinceLastThink < offlineMinGap {
log.Printf("[后台思考] 用户离线,距上次思考仅 %v,跳过 (离线模式最小间隔=%v)", sinceLastThink.Round(time.Second), offlineMinGap)
continue
}
if !isOffline && sinceLastThink < t.minThinkGap {
log.Printf("[后台思考] 距上次思考仅 %v,跳过周期性触发", sinceLastThink.Round(time.Second))
continue
}
if !isOffline && sinceLastThink < t.minThinkGap {
log.Printf("[后台思考] 距上次思考仅 %v,跳过周期性触发", sinceLastThink.Round(time.Second))
continue
}
log.Printf("[后台思考] 周期性触发 (上次思考=%v前, 上次用户消息=%v前)", sinceLastThink.Round(time.Second), sinceLastUser.Round(time.Second))
t.performThink("periodic")