fix: 回退muLock为简单版本,保留deadlock监控
This commit is contained in:
@@ -292,27 +292,31 @@ func timePeriod(now time.Time) (string, string) {
|
|||||||
// SetMessagePusher 设置主动消息推送回调
|
// SetMessagePusher 设置主动消息推送回调
|
||||||
// SetScheduleLoader sets the dynamic schedule loader for interval calculation.
|
// SetScheduleLoader sets the dynamic schedule loader for interval calculation.
|
||||||
func (t *Thinker) SetScheduleLoader(loader *ScheduleLoader) {
|
func (t *Thinker) SetScheduleLoader(loader *ScheduleLoader) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.scheduleLoader = loader
|
t.scheduleLoader = loader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thinker) SetMessagePusher(pusher func(string, string, string)) {
|
func (t *Thinker) SetMessagePusher(pusher func(string, string, string)) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.messagePusher = pusher
|
t.messagePusher = pusher
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPlatformMessagePusher sets the callback for pushing proactive messages to platform adapters (OBv11, etc.).
|
// SetPlatformMessagePusher sets the callback for pushing proactive messages to platform adapters (OBv11, etc.).
|
||||||
func (t *Thinker) SetPlatformMessagePusher(pusher func(ProactiveTarget, string)) {
|
func (t *Thinker) SetPlatformMessagePusher(pusher func(ProactiveTarget, string)) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.platformMessagePusher = pusher
|
t.platformMessagePusher = pusher
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBotUID sets the bot's own platform UID (e.g., OBv11 account).
|
// SetBotUID sets the bot's own platform UID (e.g., OBv11 account).
|
||||||
func (t *Thinker) SetBotUID(platform, uid string) {
|
func (t *Thinker) SetBotUID(platform, uid string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
if t.botUIDs == nil {
|
if t.botUIDs == nil {
|
||||||
t.botUIDs = make(map[string]string)
|
t.botUIDs = make(map[string]string)
|
||||||
@@ -324,7 +328,8 @@ func (t *Thinker) SetBotUID(platform, uid string) {
|
|||||||
|
|
||||||
// AddOrUpdatePlatformChannel adds or updates a platform channel with resolved display name.
|
// AddOrUpdatePlatformChannel adds or updates a platform channel with resolved display name.
|
||||||
func (t *Thinker) AddOrUpdatePlatformChannel(platform, channelType, channelID, channelName, adapterID, adapterName string) {
|
func (t *Thinker) AddOrUpdatePlatformChannel(platform, channelType, channelID, channelName, adapterID, adapterName string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
|
|
||||||
for i, ch := range t.platformChannels {
|
for i, ch := range t.platformChannels {
|
||||||
@@ -354,7 +359,8 @@ func (t *Thinker) AddOrUpdatePlatformChannel(platform, channelType, channelID, c
|
|||||||
|
|
||||||
// SetEmotionTracker sets the emotion tracker.
|
// SetEmotionTracker sets the emotion tracker.
|
||||||
func (t *Thinker) SetEmotionTracker(et *persona.EmotionTracker) {
|
func (t *Thinker) SetEmotionTracker(et *persona.EmotionTracker) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.emotionTracker = et
|
t.emotionTracker = et
|
||||||
}
|
}
|
||||||
@@ -362,7 +368,8 @@ func (t *Thinker) SetEmotionTracker(et *persona.EmotionTracker) {
|
|||||||
// UpdatePresence updates the user online status.
|
// UpdatePresence updates the user online status.
|
||||||
// Called by the ai-core presence endpoint when gateway detects connect/disconnect.
|
// Called by the ai-core presence endpoint when gateway detects connect/disconnect.
|
||||||
func (t *Thinker) UpdatePresence(online bool, sessionID string) {
|
func (t *Thinker) UpdatePresence(online bool, sessionID string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
wasOffline := !t.isUserOnline()
|
wasOffline := !t.isUserOnline()
|
||||||
t.setUserOnline(online)
|
t.setUserOnline(online)
|
||||||
@@ -390,7 +397,8 @@ func (t *Thinker) UpdatePresence(online bool, sessionID string) {
|
|||||||
// TriggerReminderMessage pushes a reminder message generated by LLM to the user.
|
// TriggerReminderMessage pushes a reminder message generated by LLM to the user.
|
||||||
// Called by the internal reminder-trigger endpoint when a reminder fires.
|
// Called by the internal reminder-trigger endpoint when a reminder fires.
|
||||||
func (t *Thinker) TriggerReminderMessage(userID, sessionID, message string) {
|
func (t *Thinker) TriggerReminderMessage(userID, sessionID, message string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
pusher := t.messagePusher
|
pusher := t.messagePusher
|
||||||
pushSessionID := sessionID
|
pushSessionID := sessionID
|
||||||
@@ -410,7 +418,8 @@ func (t *Thinker) TriggerReminderMessage(userID, sessionID, message string) {
|
|||||||
|
|
||||||
// PushPlatformMessage pushes a message to a platform channel via the platform pusher.
|
// PushPlatformMessage pushes a message to a platform channel via the platform pusher.
|
||||||
func (t *Thinker) PushPlatformMessage(target ProactiveTarget, message string) {
|
func (t *Thinker) PushPlatformMessage(target ProactiveTarget, message string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
pusher := t.platformMessagePusher
|
pusher := t.platformMessagePusher
|
||||||
|
|
||||||
@@ -424,7 +433,8 @@ func (t *Thinker) PushPlatformMessage(target ProactiveTarget, message string) {
|
|||||||
|
|
||||||
// IsUserRecentlyActive returns true if the user has been active within the given duration.
|
// IsUserRecentlyActive returns true if the user has been active within the given duration.
|
||||||
func (t *Thinker) IsUserRecentlyActive(d time.Duration) bool {
|
func (t *Thinker) IsUserRecentlyActive(d time.Duration) bool {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
return time.Since(t.lastUserTime()) < d
|
return time.Since(t.lastUserTime()) < d
|
||||||
}
|
}
|
||||||
@@ -557,7 +567,8 @@ func (t *Thinker) restoreContext() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !latest.IsZero() && time.Since(latest) < 24*time.Hour {
|
if !latest.IsZero() && time.Since(latest) < 24*time.Hour {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.lastUserMessage = latest
|
t.lastUserMessage = latest
|
||||||
t.setLastUser(latest)
|
t.setLastUser(latest)
|
||||||
@@ -672,7 +683,8 @@ func (t *Thinker) TriggerPostChatThink() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
canThink := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
canThink := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
||||||
|
|
||||||
@@ -742,7 +754,8 @@ func (t *Thinker) resetSilenceTimer() {
|
|||||||
return
|
return
|
||||||
case <-t.silenceTimer.C:
|
case <-t.silenceTimer.C:
|
||||||
// 再次检查:用户是否真的沉默了足够久
|
// 再次检查:用户是否真的沉默了足够久
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
silenceDuration := time.Since(t.lastUserTime())
|
silenceDuration := time.Since(t.lastUserTime())
|
||||||
canThink := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
canThink := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
||||||
@@ -860,7 +873,8 @@ func (t *Thinker) performPlatformObservation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
observationContent := fmt.Sprintf("[平台观察 %s]\n%s", time.Now().In(t.timeLocation).Format("15:04"), result.Summary)
|
observationContent := fmt.Sprintf("[平台观察 %s]\n%s", time.Now().In(t.timeLocation).Format("15:04"), result.Summary)
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.pendingThoughts = append(t.pendingThoughts, &PendingThought{
|
t.pendingThoughts = append(t.pendingThoughts, &PendingThought{
|
||||||
Content: observationContent,
|
Content: observationContent,
|
||||||
@@ -893,7 +907,8 @@ func (t *Thinker) lightThinkLoop() {
|
|||||||
log.Println("[后台思考] 轻量思考已停止")
|
log.Println("[后台思考] 轻量思考已停止")
|
||||||
return
|
return
|
||||||
case <-time.After(t.lightThinkInterval):
|
case <-time.After(t.lightThinkInterval):
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
sinceLastUser := time.Since(t.lastUserTime())
|
sinceLastUser := time.Since(t.lastUserTime())
|
||||||
sinceLastThink := time.Since(t.lastThinkTimeAtomic())
|
sinceLastThink := time.Since(t.lastThinkTimeAtomic())
|
||||||
@@ -955,7 +970,8 @@ func (t *Thinker) performLightThink() {
|
|||||||
// Check if deep think should be woken.
|
// Check if deep think should be woken.
|
||||||
if strings.Contains(content, "【需要深思】") {
|
if strings.Contains(content, "【需要深思】") {
|
||||||
log.Println("[轻量思考] 检测到【需要深思】,唤醒深度思考...")
|
log.Println("[轻量思考] 检测到【需要深思】,唤醒深度思考...")
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
canDeep := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
canDeep := time.Since(t.lastThinkTimeAtomic()) >= t.minThinkGap
|
||||||
|
|
||||||
@@ -971,7 +987,8 @@ func (t *Thinker) performLightThink() {
|
|||||||
topic := strings.TrimSpace(content[idx+len("【话题发起】"):])
|
topic := strings.TrimSpace(content[idx+len("【话题发起】"):])
|
||||||
if topic != "" {
|
if topic != "" {
|
||||||
log.Printf("[轻量思考] 话题发起: %s", topic)
|
log.Printf("[轻量思考] 话题发起: %s", topic)
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
pusher := t.messagePusher
|
pusher := t.messagePusher
|
||||||
sessionID := t.activeSessionID
|
sessionID := t.activeSessionID
|
||||||
@@ -1049,7 +1066,8 @@ func (t *Thinker) periodicThinkLoop() {
|
|||||||
|
|
||||||
// GetPendingThoughts 获取并消费所有待处理的后台思考
|
// GetPendingThoughts 获取并消费所有待处理的后台思考
|
||||||
func (t *Thinker) GetPendingThoughts() []*PendingThought {
|
func (t *Thinker) GetPendingThoughts() []*PendingThought {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
|
|
||||||
if len(t.pendingThoughts) == 0 {
|
if len(t.pendingThoughts) == 0 {
|
||||||
@@ -1067,7 +1085,8 @@ func (t *Thinker) GetPendingThoughts() []*PendingThought {
|
|||||||
|
|
||||||
// HasPendingThoughts 检查是否有待处理的思考
|
// HasPendingThoughts 检查是否有待处理的思考
|
||||||
func (t *Thinker) HasPendingThoughts() bool {
|
func (t *Thinker) HasPendingThoughts() bool {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
return len(t.pendingThoughts) > 0
|
return len(t.pendingThoughts) > 0
|
||||||
}
|
}
|
||||||
@@ -1079,7 +1098,8 @@ func (t *Thinker) HasPendingThoughts() bool {
|
|||||||
// 防御性速率限制:即使调用方未检查 minThinkGap,performThink 自身也会
|
// 防御性速率限制:即使调用方未检查 minThinkGap,performThink 自身也会
|
||||||
// 强制执行最小间隔,防止并发调用或 bug 导致 LLM 配额被快速消耗。
|
// 强制执行最小间隔,防止并发调用或 bug 导致 LLM 配额被快速消耗。
|
||||||
func (t *Thinker) performThink(triggerReason string) {
|
func (t *Thinker) performThink(triggerReason string) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
gapSinceLast := time.Since(t.lastThinkTimeAtomic())
|
gapSinceLast := time.Since(t.lastThinkTimeAtomic())
|
||||||
minGap := t.minThinkGap
|
minGap := t.minThinkGap
|
||||||
@@ -1100,14 +1120,16 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.thinkCancel = cancel
|
t.thinkCancel = cancel
|
||||||
|
|
||||||
|
|
||||||
// 0. 让步于前台——如果用户最近有活动(非post_chat),跳过本次思考。
|
// 0. 让步于前台——如果用户最近有活动(非post_chat),跳过本次思考。
|
||||||
if triggerReason != "post_chat" {
|
if triggerReason != "post_chat" {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
sinceLastUser := time.Since(t.lastUserTime())
|
sinceLastUser := time.Since(t.lastUserTime())
|
||||||
|
|
||||||
@@ -1121,7 +1143,8 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
|
|
||||||
// 0. 让步于前台——如果用户最近有活动(非post_chat),跳过本次思考。
|
// 0. 让步于前台——如果用户最近有活动(非post_chat),跳过本次思考。
|
||||||
if triggerReason != "post_chat" {
|
if triggerReason != "post_chat" {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
sinceLastUser := time.Since(t.lastUserTime())
|
sinceLastUser := time.Since(t.lastUserTime())
|
||||||
|
|
||||||
@@ -1141,7 +1164,8 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
// 2. 获取当前活跃会话的对话历史(优先活跃会话,回退到管理员主会话)
|
// 2. 获取当前活跃会话的对话历史(优先活跃会话,回退到管理员主会话)
|
||||||
var convHistory []model.LLMMessage
|
var convHistory []model.LLMMessage
|
||||||
if t.convStore != nil {
|
if t.convStore != nil {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
sessionID := t.activeSessionID
|
sessionID := t.activeSessionID
|
||||||
if sessionID == "" {
|
if sessionID == "" {
|
||||||
@@ -1259,7 +1283,8 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
// 4.5 获取最近平台观察(定期触发和对话后触发时注入)
|
// 4.5 获取最近平台观察(定期触发和对话后触发时注入)
|
||||||
var platformObservation string
|
var platformObservation string
|
||||||
if triggerReason == "periodic" || triggerReason == "post_chat" {
|
if triggerReason == "periodic" || triggerReason == "post_chat" {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
for i := len(t.pendingThoughts) - 1; i >= 0; i-- {
|
for i := len(t.pendingThoughts) - 1; i >= 0; i-- {
|
||||||
if strings.HasPrefix(t.pendingThoughts[i].Content, "[平台观察") {
|
if strings.HasPrefix(t.pendingThoughts[i].Content, "[平台观察") {
|
||||||
@@ -1410,7 +1435,8 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
adapterName, groupID := parts[0], parts[1]
|
adapterName, groupID := parts[0], parts[1]
|
||||||
// Find the channel to get the correct platform type.
|
// Find the channel to get the correct platform type.
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
var platform string
|
var platform string
|
||||||
for _, ch := range t.platformChannels {
|
for _, ch := range t.platformChannels {
|
||||||
@@ -1664,7 +1690,8 @@ func (t *Thinker) buildThinkingUserPrompt(
|
|||||||
case "post_chat":
|
case "post_chat":
|
||||||
sb.WriteString("刚有人和你聊完天。你想自然地在心里回味一下刚才的对话……\n")
|
sb.WriteString("刚有人和你聊完天。你想自然地在心里回味一下刚才的对话……\n")
|
||||||
case "silence":
|
case "silence":
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
silenceDuration := time.Since(t.lastUserTime())
|
silenceDuration := time.Since(t.lastUserTime())
|
||||||
|
|
||||||
@@ -1764,7 +1791,8 @@ func (t *Thinker) buildThinkingUserPrompt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OBv11 platform identity and available channels for proactive messaging.
|
// OBv11 platform identity and available channels for proactive messaging.
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
qqChannels := t.platformChannels
|
qqChannels := t.platformChannels
|
||||||
botUIDs := t.botUIDs
|
botUIDs := t.botUIDs
|
||||||
@@ -1913,7 +1941,8 @@ func (t *Thinker) buildOpenAITools() []llm.OpenAITool {
|
|||||||
|
|
||||||
// storeThought 存储思考结果到待推送队列,并异步持久化到 memory-service
|
// storeThought 存储思考结果到待推送队列,并异步持久化到 memory-service
|
||||||
func (t *Thinker) storeThought(content string, toolCallsJSON string, toolCallCount int) {
|
func (t *Thinker) storeThought(content string, toolCallsJSON string, toolCallCount int) {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
t.pendingThoughts = append(t.pendingThoughts, &PendingThought{
|
t.pendingThoughts = append(t.pendingThoughts, &PendingThought{
|
||||||
Content: content,
|
Content: content,
|
||||||
@@ -2089,7 +2118,8 @@ func (t *Thinker) extractProactiveMessage(content string) (string, *ProactiveTar
|
|||||||
channelID = "private_" + m[2]
|
channelID = "private_" + m[2]
|
||||||
}
|
}
|
||||||
adapterName := platform // fallback to format key
|
adapterName := platform // fallback to format key
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
for _, ch := range t.platformChannels {
|
for _, ch := range t.platformChannels {
|
||||||
if ch.Platform == platform && ch.ChannelType == chatType && ch.ChannelID == channelID {
|
if ch.Platform == platform && ch.ChannelType == chatType && ch.ChannelID == channelID {
|
||||||
@@ -2681,7 +2711,8 @@ func (t *Thinker) setLastProactive(ts time.Time) { t.lastProactiveNs.Store(ts.Un
|
|||||||
func (t *Thinker) DeadlockDetected(timeout time.Duration) bool {
|
func (t *Thinker) DeadlockDetected(timeout time.Duration) bool {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
|
|
||||||
close(done)
|
close(done)
|
||||||
@@ -2707,7 +2738,8 @@ func (t *Thinker) StartDeadlockMonitor() {
|
|||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
t.muLock()
|
if !t.muLock() {
|
||||||
|
return
|
||||||
defer t.muUnlock()
|
defer t.muUnlock()
|
||||||
|
|
||||||
close(done)
|
close(done)
|
||||||
|
|||||||
Reference in New Issue
Block a user