fix: 修复后台思考离线时被拉长到30分钟的问题
离线最小思考间隔从硬编码30分钟改为可配置环境变量 THINK_OFFLINE_GAP_SEC (默认10分钟)。 新增服务启动后首次思考触发,确保即使无客户端连接也能立即开始后台思考。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user