feat: 自主思考分层 — 轻量思考(简单)+深度思考(复杂)双模型
- 新增轻量思考循环 lightThinkLoop (默认60s间隔,快速模型)
- 轻量思考用极简 prompt 做快速状态检查
- 检测到「需要深思」时自动唤醒深度思考 performThink("light_wake")
- 环境变量: LIGHT_THINK_ENABLED / LIGHT_THINK_INTERVAL_SEC
- 轻量思考跳过活跃用户 (30s内有消息) 和重复触发 (10s内)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,10 @@ type Thinker struct {
|
|||||||
// 默认 300 秒(5 分钟),设为 0 则禁用定时触发
|
// 默认 300 秒(5 分钟),设为 0 则禁用定时触发
|
||||||
thinkInterval time.Duration
|
thinkInterval time.Duration
|
||||||
|
|
||||||
|
// 轻量思考(简单思考):高频、快速模型、简单 prompt
|
||||||
|
lightThinkEnabled bool
|
||||||
|
lightThinkInterval time.Duration
|
||||||
|
|
||||||
// 静默检测超时:用户多久不说话后昔涟可以主动搭话
|
// 静默检测超时:用户多久不说话后昔涟可以主动搭话
|
||||||
// 默认 120 秒(2 分钟),设为 0 则禁用静默检测
|
// 默认 120 秒(2 分钟),设为 0 则禁用静默检测
|
||||||
silenceTimeout time.Duration
|
silenceTimeout time.Duration
|
||||||
@@ -318,6 +322,9 @@ type ThinkerConfig struct {
|
|||||||
OfflineThinkGap time.Duration // 两次思考最小间隔 (离线,默认 10 分钟)
|
OfflineThinkGap time.Duration // 两次思考最小间隔 (离线,默认 10 分钟)
|
||||||
|
|
||||||
// 平台静默观察
|
// 平台静默观察
|
||||||
|
LightThinkEnabled bool
|
||||||
|
LightThinkInterval time.Duration // 轻量思考间隔 (默认 60s,0 = 禁用)
|
||||||
|
|
||||||
PlatformSilentThinkInterval time.Duration // 平台记忆观察间隔 (默认 600s,0 = 禁用)
|
PlatformSilentThinkInterval time.Duration // 平台记忆观察间隔 (默认 600s,0 = 禁用)
|
||||||
PlatformChannels []PlatformChannel
|
PlatformChannels []PlatformChannel
|
||||||
}
|
}
|
||||||
@@ -339,6 +346,8 @@ func DefaultThinkerConfig() ThinkerConfig {
|
|||||||
PostChatDelay: getEnvDuration("THINK_POST_CHAT_DELAY_SEC", 5),
|
PostChatDelay: getEnvDuration("THINK_POST_CHAT_DELAY_SEC", 5),
|
||||||
MinThinkGap: getEnvDuration("THINK_MIN_GAP_SEC", 30),
|
MinThinkGap: getEnvDuration("THINK_MIN_GAP_SEC", 30),
|
||||||
OfflineThinkGap: getEnvDuration("THINK_OFFLINE_GAP_SEC", 600),
|
OfflineThinkGap: getEnvDuration("THINK_OFFLINE_GAP_SEC", 600),
|
||||||
|
LightThinkEnabled: getEnvBool("LIGHT_THINK_ENABLED", true),
|
||||||
|
LightThinkInterval: getEnvDuration("LIGHT_THINK_INTERVAL_SEC", 60),
|
||||||
PlatformSilentThinkInterval: getEnvDuration("PLATFORM_THINK_INTERVAL_SEC", 600),
|
PlatformSilentThinkInterval: getEnvDuration("PLATFORM_THINK_INTERVAL_SEC", 600),
|
||||||
PlatformChannels: ParsePlatformChannels(os.Getenv("PLATFORM_CHANNELS")),
|
PlatformChannels: ParsePlatformChannels(os.Getenv("PLATFORM_CHANNELS")),
|
||||||
}
|
}
|
||||||
@@ -379,6 +388,8 @@ func NewThinker(
|
|||||||
toolAdapter: toolAdapter,
|
toolAdapter: toolAdapter,
|
||||||
iotClient: iotClient,
|
iotClient: iotClient,
|
||||||
thinkInterval: cfg.ThinkInterval,
|
thinkInterval: cfg.ThinkInterval,
|
||||||
|
lightThinkEnabled: cfg.LightThinkEnabled,
|
||||||
|
lightThinkInterval: cfg.LightThinkInterval,
|
||||||
silenceTimeout: cfg.SilenceTimeout,
|
silenceTimeout: cfg.SilenceTimeout,
|
||||||
proactiveMsgMinGap: getEnvDuration("PROACTIVE_MSG_MIN_GAP_SEC", 1800),
|
proactiveMsgMinGap: getEnvDuration("PROACTIVE_MSG_MIN_GAP_SEC", 1800),
|
||||||
postChatDelay: cfg.PostChatDelay,
|
postChatDelay: cfg.PostChatDelay,
|
||||||
@@ -426,6 +437,10 @@ func (t *Thinker) Start() {
|
|||||||
t.wg.Add(1)
|
t.wg.Add(1)
|
||||||
go t.periodicThinkLoop()
|
go t.periodicThinkLoop()
|
||||||
}
|
}
|
||||||
|
if t.lightThinkEnabled && t.lightThinkInterval > 0 {
|
||||||
|
t.wg.Add(1)
|
||||||
|
go t.lightThinkLoop()
|
||||||
|
}
|
||||||
|
|
||||||
// 启动平台静默观察循环
|
// 启动平台静默观察循环
|
||||||
if len(t.platformChannels) > 0 && t.platformThinkInterval > 0 {
|
if len(t.platformChannels) > 0 && t.platformThinkInterval > 0 {
|
||||||
@@ -689,6 +704,93 @@ func (t *Thinker) performPlatformObservation() {
|
|||||||
log.Printf("[后台思考] 平台观察摘要已生成 (长度=%d, 需要关注=%v)", len(result.Summary), result.NeedsAttention)
|
log.Printf("[后台思考] 平台观察摘要已生成 (长度=%d, 需要关注=%v)", len(result.Summary), result.NeedsAttention)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lightThinkLoop runs frequent lightweight thinking cycles using a fast model.
|
||||||
|
// It checks for urgent matters and can wake the deep thinker when needed.
|
||||||
|
func (t *Thinker) lightThinkLoop() {
|
||||||
|
defer t.wg.Done()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[后台思考] 轻量思考循环 panic 恢复: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("[后台思考] 轻量思考已启动 (间隔=%v)", t.lightThinkInterval)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.stopCh:
|
||||||
|
log.Println("[后台思考] 轻量思考已停止")
|
||||||
|
return
|
||||||
|
case <-time.After(t.lightThinkInterval):
|
||||||
|
t.mu.Lock()
|
||||||
|
sinceLastUser := time.Since(t.lastUserMessage)
|
||||||
|
sinceLastThink := time.Since(t.lastThinkTime)
|
||||||
|
t.mu.Unlock()
|
||||||
|
|
||||||
|
// Skip if user was active recently (last 30s).
|
||||||
|
if sinceLastUser < 30*time.Second {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Skip if deep think just ran.
|
||||||
|
if sinceLastThink < 10*time.Second {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.performLightThink()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// performLightThink runs a single lightweight thinking cycle using the fast model.
|
||||||
|
func (t *Thinker) performLightThink() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Build a minimal system prompt.
|
||||||
|
now := time.Now()
|
||||||
|
systemPrompt := fmt.Sprintf(`你是昔涟。这是一次快速的内心检查(%s)。
|
||||||
|
|
||||||
|
请用一句话在心里判断:
|
||||||
|
1. 开拓者有什么需要立即关注的事情吗?(提醒、设备状态、情绪变化)
|
||||||
|
2. 如果有紧急事项,输出【需要深思】标记;如果一切正常,输出一个字的感受即可。
|
||||||
|
|
||||||
|
注意:只需要极简判断,不要长篇反思。`, now.Format("15:04"))
|
||||||
|
|
||||||
|
userPrompt := "快速检查一下。"
|
||||||
|
messages := []model.LLMMessage{
|
||||||
|
{Role: model.RoleSystem, Content: systemPrompt},
|
||||||
|
{Role: model.RoleUser, Content: userPrompt},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use fast model (toolAdapter) for light think.
|
||||||
|
resp, err := t.toolAdapter.Chat(ctx, messages)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[轻量思考] LLM调用失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := resp.Content
|
||||||
|
if content == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[轻量思考] %s", strings.TrimSpace(content))
|
||||||
|
|
||||||
|
// Check if deep think should be woken.
|
||||||
|
if strings.Contains(content, "【需要深思】") {
|
||||||
|
log.Println("[轻量思考] 检测到【需要深思】,唤醒深度思考...")
|
||||||
|
t.mu.Lock()
|
||||||
|
canDeep := time.Since(t.lastThinkTime) >= t.minThinkGap
|
||||||
|
t.mu.Unlock()
|
||||||
|
if canDeep {
|
||||||
|
t.performThink("light_wake")
|
||||||
|
} else {
|
||||||
|
log.Println("[轻量思考] 距上次深度思考太近,跳过唤醒")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// periodicThinkLoop 周期性自主思考循环
|
||||||
// periodicThinkLoop 周期性自主思考循环
|
// periodicThinkLoop 周期性自主思考循环
|
||||||
//
|
//
|
||||||
// 使用动态间隔:若配置了 ScheduleLoader,每次循环根据当前时段计算间隔;
|
// 使用动态间隔:若配置了 ScheduleLoader,每次循环根据当前时段计算间隔;
|
||||||
|
|||||||
Reference in New Issue
Block a user