121 lines
5.0 KiB
Go
121 lines
5.0 KiB
Go
/*
|
|
* Copyright 2025 CloudWeGo Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Package supervisor implements the supervisor pattern for multi-agent systems,
|
|
// where a central agent coordinates a set of sub-agents.
|
|
//
|
|
// # Unified Tracing
|
|
//
|
|
// The supervisor pattern provides unified tracing support through an internal container.
|
|
// When using callbacks (e.g., for tracing or observability), the entire supervisor structure
|
|
// (supervisor agent + all sub-agents) shares a single trace root. This means:
|
|
// - OnStart is invoked once at the supervisor container level
|
|
// - The callback-enriched context (containing parent span info) is propagated to all agents
|
|
// - All agents within the supervisor appear as children of the same trace root
|
|
//
|
|
// This is achieved by wrapping the supervisor structure in an internal container that acts
|
|
// as the single entry point for tracing. The container delegates all execution to the
|
|
// underlying agents while providing a unified identity for callbacks.
|
|
package supervisor
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/cloudwego/eino/adk"
|
|
)
|
|
|
|
// Config is the configuration for creating a supervisor-based multi-agent system.
|
|
//
|
|
// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,
|
|
// which has not proven to be more effective empirically. Consider using
|
|
// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.
|
|
type Config struct {
|
|
// Supervisor specifies the agent that will act as the supervisor, coordinating and managing the sub-agents.
|
|
Supervisor adk.Agent
|
|
|
|
// SubAgents specifies the list of agents that will be supervised and coordinated by the supervisor agent.
|
|
SubAgents []adk.Agent
|
|
}
|
|
|
|
// supervisorContainer wraps the entire supervisor structure to provide unified tracing.
|
|
// When callbacks are registered (e.g., via Runner.Query with WithCallbacks), OnStart/OnEnd
|
|
// are invoked once for this container, creating a single trace root. The callback-enriched
|
|
// context is then propagated to the supervisor and all sub-agents, ensuring they share
|
|
// the same trace parent.
|
|
//
|
|
// This container implements Agent and ResumableAgent by delegating to the inner agent.
|
|
// It provides its own Name and GetType for callback identification.
|
|
type supervisorContainer struct {
|
|
name string
|
|
inner adk.ResumableAgent
|
|
}
|
|
|
|
func (s *supervisorContainer) Name(_ context.Context) string {
|
|
return s.name
|
|
}
|
|
|
|
func (s *supervisorContainer) Description(ctx context.Context) string {
|
|
return s.inner.Description(ctx)
|
|
}
|
|
|
|
func (s *supervisorContainer) GetType() string {
|
|
return "Supervisor"
|
|
}
|
|
|
|
func (s *supervisorContainer) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
|
|
return s.inner.Run(ctx, input, opts...)
|
|
}
|
|
|
|
func (s *supervisorContainer) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
|
|
return s.inner.Resume(ctx, info, opts...)
|
|
}
|
|
|
|
// New creates a supervisor-based multi-agent system with the given configuration.
|
|
//
|
|
// In the supervisor pattern, a designated supervisor agent coordinates multiple sub-agents.
|
|
// The supervisor can delegate tasks to sub-agents and receive their responses, while
|
|
// sub-agents can only communicate with the supervisor (not with each other directly).
|
|
// This hierarchical structure enables complex problem-solving through coordinated agent interactions.
|
|
//
|
|
// The returned agent is wrapped in an internal container that provides unified tracing.
|
|
// When used with Runner and callbacks, all agents within the supervisor structure will
|
|
// share the same trace root, making it easy to observe the entire multi-agent execution
|
|
// as a single logical unit.
|
|
//
|
|
// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,
|
|
// which has not proven to be more effective empirically. Consider using
|
|
// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.
|
|
func New(ctx context.Context, conf *Config) (adk.ResumableAgent, error) {
|
|
subAgents := make([]adk.Agent, 0, len(conf.SubAgents))
|
|
supervisorName := conf.Supervisor.Name(ctx)
|
|
for _, subAgent := range conf.SubAgents {
|
|
subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{
|
|
Agent: subAgent,
|
|
ToAgentNames: []string{supervisorName},
|
|
}))
|
|
}
|
|
|
|
inner, err := adk.SetSubAgents(ctx, conf.Supervisor, subAgents)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &supervisorContainer{
|
|
name: supervisorName,
|
|
inner: inner,
|
|
}, nil
|
|
}
|