Files
Cyrene/backend/ai-core/internal/scheduler/message_scheduler.go
T
AskaEth 87214b9441 feat: Phase 1+2 架构进化 — 连续思考链/主动消息决策/情感状态机/离线自主思考 (86文件)
Phase 1 (基础设施):
- ThinkChain 思考链连续性 + 差异化思考提示词 (persistent)
- AutonomousToolPolicy 工具安全策略 (safe/unsafe/conditional)
- MessageScheduler 自适应消息节奏 (Idle/Available/Busy)
- SessionEnrichmentStore 渐进式上下文丰富 (5层)
- ConversationBus 事件总线 + ResponseCache (dedup)
- pkg/logger 统一日志 + 所有 handler 替换 fmt.Printf
- NPE 守卫/链路优化/数据库表修复/Go workspace

Phase 2 (人格交互):
- EmotionState/EmotionTracker 情感状态机 (5种心情, 情绪衰减)
- ProactiveGuard 主动消息多维决策 (静默时段/紧急度/频率/校验)
- Gateway↔ai-core 在线状态感知链路 (presence notification)
- 离线思考频率控制 + 重连问候 + 离线消息排队

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 15:25:12 +08:00

185 lines
4.5 KiB
Go

// Package scheduler 消息发送调度器
// Phase 1 Step 3: 自适应消息节奏控制
package scheduler
import (
"math"
"math/rand"
"time"
"unicode/utf8"
)
// MessageDisplayType 消息展示类型
type MessageDisplayType string
const (
DisplayChat MessageDisplayType = "chat"
DisplayAction MessageDisplayType = "action"
DisplayThinking MessageDisplayType = "thinking"
DisplayToolProgress MessageDisplayType = "tool_progress"
DisplaySystemInfo MessageDisplayType = "system_info"
)
// ScheduledMessage 待发送消息
type ScheduledMessage struct {
Type MessageDisplayType
Content string
Priority int // 0=立即, 1=正常, 2=可延迟
Delay time.Duration // 相对上一条消息的延迟
}
// Complexity 消息复杂度
type Complexity int
const (
ComplexitySimple Complexity = iota // 问候、确认
ComplexityNormal // 日常对话
ComplexityComplex // 详细解答
)
// SchedulingRules 调度规则
type SchedulingRules struct {
MinInterval time.Duration // 最小消息间隔 200ms
MaxInterval time.Duration // 最大消息间隔 800ms
MaxMessagesPerRound int // 每轮最多消息数 5
MaxActionsPerRound int // 每轮最多动作消息数 2
ChatBeforeAction bool // 聊天消息先于动作
AdaptiveRhythm bool // 自适应节奏
}
// DefaultRules 默认调度规则
func DefaultRules() SchedulingRules {
return SchedulingRules{
MinInterval: 200 * time.Millisecond,
MaxInterval: 800 * time.Millisecond,
MaxMessagesPerRound: 5,
MaxActionsPerRound: 2,
ChatBeforeAction: true,
AdaptiveRhythm: true,
}
}
// MessageScheduler 消息发送调度器
type MessageScheduler struct {
rules SchedulingRules
rng *rand.Rand
}
// NewMessageScheduler 创建调度器
func NewMessageScheduler(rules SchedulingRules) *MessageScheduler {
return &MessageScheduler{
rules: rules,
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}
// Schedule 调度消息发送:计算每条消息的发送延迟
func (s *MessageScheduler) Schedule(messages []ScheduledMessage) []ScheduledMessage {
if len(messages) == 0 {
return nil
}
// 1. 限制总量
messages = s.enforceLimits(messages)
// 2. 评估复杂度
complexity := s.assessComplexity(messages)
// 3. 计算基础延迟
baseDelay := s.baseDelayForComplexity(complexity)
// 4. 为每条消息分配延迟
for i := range messages {
msg := &messages[i]
// action 消息紧跟前面的 chat
if msg.Type == DisplayAction {
msg.Delay = 0
continue
}
// 第一条消息立即发送
if i == 0 {
msg.Delay = 0
continue
}
// chat 消息使用带 jitter 的延迟
jitter := baseDelay * time.Duration(0.7+0.6*s.rng.Float64())
msg.Delay = jitter
// 短消息适当加快
runeCount := utf8.RuneCountInString(msg.Content)
if runeCount < 20 {
msg.Delay = time.Duration(math.Max(float64(msg.Delay)*0.6, float64(s.rules.MinInterval)))
}
// 限制在 [MinInterval, MaxInterval] 范围内
if msg.Delay < s.rules.MinInterval {
msg.Delay = s.rules.MinInterval
}
if msg.Delay > s.rules.MaxInterval {
msg.Delay = s.rules.MaxInterval
}
}
return messages
}
// enforceLimits 限制消息数量
func (s *MessageScheduler) enforceLimits(messages []ScheduledMessage) []ScheduledMessage {
if len(messages) <= s.rules.MaxMessagesPerRound {
return messages
}
var result []ScheduledMessage
actionCount := 0
for _, msg := range messages {
if msg.Type == DisplayAction {
if actionCount >= s.rules.MaxActionsPerRound {
continue
}
actionCount++
}
result = append(result, msg)
if len(result) >= s.rules.MaxMessagesPerRound {
break
}
}
return result
}
// assessComplexity 根据消息数量和总长度评估复杂度
func (s *MessageScheduler) assessComplexity(messages []ScheduledMessage) Complexity {
if len(messages) <= 1 {
return ComplexitySimple
}
var totalChars int
for _, msg := range messages {
totalChars += utf8.RuneCountInString(msg.Content)
}
if len(messages) <= 2 && totalChars < 60 {
return ComplexitySimple
}
if len(messages) <= 3 && totalChars < 200 {
return ComplexityNormal
}
return ComplexityComplex
}
// baseDelayForComplexity 根据复杂度返回基础延迟
func (s *MessageScheduler) baseDelayForComplexity(c Complexity) time.Duration {
switch c {
case ComplexitySimple:
return 200 * time.Millisecond
case ComplexityNormal:
return 400 * time.Millisecond
case ComplexityComplex:
return 600 * time.Millisecond
default:
return 400 * time.Millisecond
}
}