Fix duplicate replies after message tool sends from named agents

This commit is contained in:
FyZhu97 2026-03-23 10:34:36 +08:00
parent cf9e0496f7
commit 88d4a04e89
2 changed files with 78 additions and 6 deletions

View file

@ -458,7 +458,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
if target == nil { if target == nil {
cancelDrain() cancelDrain()
if finalResponse != "" { if finalResponse != "" {
al.publishResponseIfNeeded(ctx, msg.Channel, msg.ChatID, finalResponse) al.publishResponseIfNeeded(ctx, msg.SessionKey, msg.Channel, msg.ChatID, finalResponse)
} }
return return
} }
@ -518,7 +518,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
} }
if finalResponse != "" { if finalResponse != "" {
al.publishResponseIfNeeded(ctx, target.Channel, target.ChatID, finalResponse) al.publishResponseIfNeeded(ctx, target.SessionKey, target.Channel, target.ChatID, finalResponse)
} }
}() }()
default: default:
@ -604,15 +604,17 @@ func (al *AgentLoop) Stop() {
al.running.Store(false) al.running.Store(false)
} }
func (al *AgentLoop) publishResponseIfNeeded(ctx context.Context, channel, chatID, response string) { func (al *AgentLoop) publishResponseIfNeeded(
ctx context.Context,
sessionKey, channel, chatID, response string,
) {
if response == "" { if response == "" {
return return
} }
alreadySent := false alreadySent := false
defaultAgent := al.GetRegistry().GetDefaultAgent() if agent := al.agentForSession(sessionKey); agent != nil {
if defaultAgent != nil { if tool, ok := agent.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()
} }

View file

@ -132,6 +132,76 @@ func TestProcessMessage_IncludesCurrentSenderInDynamicContext(t *testing.T) {
} }
} }
func TestPublishResponseIfNeeded_UsesSessionAgentMessageState(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "agent-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
cfg := &config.Config{
Agents: config.AgentsConfig{
Defaults: config.AgentDefaults{
Workspace: tmpDir,
Model: "test-model",
MaxTokens: 4096,
MaxToolIterations: 10,
},
List: []config.AgentConfig{
{ID: "worker"},
},
},
Tools: config.ToolsConfig{
Message: config.ToolConfig{Enabled: true},
},
}
msgBus := bus.NewMessageBus()
al := NewAgentLoop(cfg, msgBus, &mockProvider{})
worker, ok := al.registry.GetAgent("worker")
if !ok {
t.Fatal("expected named agent to be registered")
}
tool, ok := worker.Tools.Get("message")
if !ok {
t.Fatal("expected message tool to be registered on named agent")
}
messageTool, ok := tool.(*tools.MessageTool)
if !ok {
t.Fatalf("expected *tools.MessageTool, got %T", tool)
}
messageTool.ResetSentInRound()
result := messageTool.Execute(
tools.WithToolContext(context.Background(), "discord", "chat-1"),
map[string]any{"content": "tool message"},
)
if result == nil || result.Err != nil {
t.Fatalf("message tool execute failed: %+v", result)
}
sessionKey := routing.BuildAgentMainSessionKey(worker.ID)
al.publishResponseIfNeeded(context.Background(), sessionKey, "discord", "chat-1", "final response")
select {
case msg := <-msgBus.OutboundChan():
if msg.Content != "tool message" {
t.Fatalf("first outbound content = %q, want %q", msg.Content, "tool message")
}
default:
t.Fatal("expected tool message to be published")
}
select {
case msg := <-msgBus.OutboundChan():
t.Fatalf("unexpected duplicate outbound message: %+v", msg)
default:
}
}
func TestProcessMessage_UseCommandLoadsRequestedSkill(t *testing.T) { func TestProcessMessage_UseCommandLoadsRequestedSkill(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
skillDir := filepath.Join(tmpDir, "skills", "shell") skillDir := filepath.Join(tmpDir, "skills", "shell")