87214b9441
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>
185 lines
4.5 KiB
Go
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
|
|
}
|
|
}
|