This commit is contained in:
OpenClaw-User 2026-03-14 17:13:35 +08:00
commit 3a319bf890
2 changed files with 25 additions and 2 deletions

View file

@ -278,15 +278,17 @@ func (al *AgentLoop) Run(ctx context.Context) error {
} }
if response != "" { if response != "" {
// Check if the message tool already sent a response during this round. // Check if the message tool already sent a response to the SAME chat during this round.
// If so, skip publishing to avoid duplicate messages to the user. // If so, skip publishing to avoid duplicate messages to the user.
// Only skip when the target chat_id matches — sending to a different chat
// (e.g. a newly created group) should not suppress the reply to the original chat.
// Use default agent's tools to check (message tool is shared). // Use default agent's tools to check (message tool is shared).
alreadySent := false alreadySent := false
defaultAgent := al.GetRegistry().GetDefaultAgent() defaultAgent := al.GetRegistry().GetDefaultAgent()
if defaultAgent != nil { if defaultAgent != nil {
if tool, ok := defaultAgent.Tools.Get("message"); ok { if tool, ok := defaultAgent.Tools.Get("message"); ok {
if mt, ok := tool.(*tools.MessageTool); ok { if mt, ok := tool.(*tools.MessageTool); ok {
alreadySent = mt.HasSentInRound() alreadySent = mt.HasSentInRound() && mt.HasSentToChatID(msg.ChatID)
} }
} }
} }

View file

@ -3,6 +3,7 @@ package tools
import ( import (
"context" "context"
"fmt" "fmt"
"sync"
"sync/atomic" "sync/atomic"
) )
@ -11,6 +12,9 @@ type SendCallback func(channel, chatID, content string) error
type MessageTool struct { type MessageTool struct {
sendCallback SendCallback sendCallback SendCallback
sentInRound atomic.Bool // Tracks whether a message was sent in the current processing round sentInRound atomic.Bool // Tracks whether a message was sent in the current processing round
mu sync.Mutex // protects sentChatIDs
sentChatIDs map[string]struct{} // All chat_ids the message tool sent to in the current round
} }
func NewMessageTool() *MessageTool { func NewMessageTool() *MessageTool {
@ -50,6 +54,9 @@ func (t *MessageTool) Parameters() map[string]any {
// Called by the agent loop at the start of each inbound message processing round. // Called by the agent loop at the start of each inbound message processing round.
func (t *MessageTool) ResetSentInRound() { func (t *MessageTool) ResetSentInRound() {
t.sentInRound.Store(false) t.sentInRound.Store(false)
t.mu.Lock()
t.sentChatIDs = nil
t.mu.Unlock()
} }
// HasSentInRound returns true if the message tool sent a message during the current round. // HasSentInRound returns true if the message tool sent a message during the current round.
@ -57,6 +64,14 @@ func (t *MessageTool) HasSentInRound() bool {
return t.sentInRound.Load() return t.sentInRound.Load()
} }
// HasSentToChatID returns true if the message tool sent to the given chat_id in this round.
func (t *MessageTool) HasSentToChatID(chatID string) bool {
t.mu.Lock()
defer t.mu.Unlock()
_, ok := t.sentChatIDs[chatID]
return ok
}
func (t *MessageTool) SetSendCallback(callback SendCallback) { func (t *MessageTool) SetSendCallback(callback SendCallback) {
t.sendCallback = callback t.sendCallback = callback
} }
@ -94,6 +109,12 @@ func (t *MessageTool) Execute(ctx context.Context, args map[string]any) *ToolRes
} }
t.sentInRound.Store(true) t.sentInRound.Store(true)
t.mu.Lock()
if t.sentChatIDs == nil {
t.sentChatIDs = make(map[string]struct{})
}
t.sentChatIDs[chatID] = struct{}{}
t.mu.Unlock()
// Silent: user already received the message directly // Silent: user already received the message directly
return &ToolResult{ return &ToolResult{
ForLLM: fmt.Sprintf("Message sent to %s:%s", channel, chatID), ForLLM: fmt.Sprintf("Message sent to %s:%s", channel, chatID),