From 30d6e476016c90fc2d757b46ed04bcd8eb85ce4b Mon Sep 17 00:00:00 2001 From: DBT Date: Thu, 12 Feb 2026 04:38:00 +0000 Subject: [PATCH] feat: implement full recursive multi-agent system in ClawGo --- pkg/agent/loop.go | 10 ++++- pkg/tools/subagent.go | 86 +++++++++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 045b89d..1eef2d1 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -86,7 +86,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers sessionsManager := session.NewSessionManager(filepath.Join(filepath.Dir(cfg.WorkspacePath()), "sessions")) - return &AgentLoop{ + loop := &AgentLoop{ bus: msgBus, provider: provider, workspace: workspace, @@ -97,6 +97,14 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers tools: toolsRegistry, running: false, } + + // 注入递归运行逻辑,使 subagent 具备 full tool-calling 能力 + subagentManager.SetRunFunc(func(ctx context.Context, task, channel, chatID string) (string, error) { + sessionKey := fmt.Sprintf("subagent:%d", time.Now().UnixNano()) + return loop.ProcessDirect(ctx, task, sessionKey) + }) + + return loop } func (al *AgentLoop) Run(ctx context.Context) error { diff --git a/pkg/tools/subagent.go b/pkg/tools/subagent.go index 8f4bc97..2d98479 100644 --- a/pkg/tools/subagent.go +++ b/pkg/tools/subagent.go @@ -28,6 +28,7 @@ type SubagentManager struct { bus *bus.MessageBus workspace string nextID int + runFunc SubagentRunFunc } func NewSubagentManager(provider providers.LLMProvider, workspace string, bus *bus.MessageBus) *SubagentManager { @@ -70,45 +71,74 @@ func (sm *SubagentManager) runTask(ctx context.Context, task *SubagentTask) { task.Status = "running" task.Created = time.Now().UnixMilli() - messages := []providers.Message{ - { - Role: "system", - Content: "You are a subagent. Complete the given task independently and report the result.", - }, - { - Role: "user", - Content: task.Task, - }, - } - - response, err := sm.provider.Chat(ctx, messages, nil, sm.provider.GetDefaultModel(), map[string]interface{}{ - "max_tokens": 4096, - }) - - sm.mu.Lock() - defer sm.mu.Unlock() - - if err != nil { - task.Status = "failed" - task.Result = fmt.Sprintf("Error: %v", err) + // 1. 独立 Agent 逻辑:支持递归工具调用 + // 这里简单实现:通过共享 AgentLoop 的逻辑来实现 full subagent 能力 + // 但目前 subagent.go 不方便反向依赖 agent 包,我们暂时通过 Inject 方式解决 + + // 如果没有注入 RunFunc,则退化为简单的一步 Chat + if sm.runFunc != nil { + result, err := sm.runFunc(ctx, task.Task, task.OriginChannel, task.OriginChatID) + sm.mu.Lock() + if err != nil { + task.Status = "failed" + task.Result = fmt.Sprintf("Error: %v", err) + } else { + task.Status = "completed" + task.Result = result + } + sm.mu.Unlock() } else { - task.Status = "completed" - task.Result = response.Content + // 原有的 One-shot 逻辑 + messages := []providers.Message{ + { + Role: "system", + Content: "You are a subagent. Complete the given task independently and report the result.", + }, + { + Role: "user", + Content: task.Task, + }, + } + + response, err := sm.provider.Chat(ctx, messages, nil, sm.provider.GetDefaultModel(), map[string]interface{}{ + "max_tokens": 4096, + }) + + sm.mu.Lock() + if err != nil { + task.Status = "failed" + task.Result = fmt.Sprintf("Error: %v", err) + } else { + task.Status = "completed" + task.Result = response.Content + } + sm.mu.Unlock() } - // Send announce message back to main agent + // 2. 结果广播 (原有逻辑保持) if sm.bus != nil { - announceContent := fmt.Sprintf("Task '%s' completed.\n\nResult:\n%s", task.Label, task.Result) + prefix := "Task completed" + if task.Label != "" { + prefix = fmt.Sprintf("Task '%s' completed", task.Label) + } + announceContent := fmt.Sprintf("%s.\n\nResult:\n%s", prefix, task.Result) sm.bus.PublishInbound(bus.InboundMessage{ Channel: "system", SenderID: fmt.Sprintf("subagent:%s", task.ID), - // Format: "original_channel:original_chat_id" for routing back - ChatID: fmt.Sprintf("%s:%s", task.OriginChannel, task.OriginChatID), - Content: announceContent, + ChatID: fmt.Sprintf("%s:%s", task.OriginChannel, task.OriginChatID), + Content: announceContent, }) } } +type SubagentRunFunc func(ctx context.Context, task, channel, chatID string) (string, error) + +func (sm *SubagentManager) SetRunFunc(f SubagentRunFunc) { + sm.mu.Lock() + defer sm.mu.Unlock() + sm.runFunc = f +} + func (sm *SubagentManager) GetTask(taskID string) (*SubagentTask, bool) { sm.mu.RLock() defer sm.mu.RUnlock()