feat: implement multi-bot orchestration system with LLM routing
Implementa el sistema de orquestación para salas Matrix con múltiples bots. El orquestador es un "special agent" sin identidad Matrix que coordina qué bot responde y cuándo, usando LLM (Claude) para routing y evaluación de calidad. Cambios principales: - pkg/orchestration/task.go: tipos puros (TaskEvent, BotResponse, QualityScore, RoutingDecision) - shell/orchestration/: runtime del orquestador (orchestrator.go, router.go, evaluator.go) - agents/specials/orchestrator/: config + prompts (routing, quality, refinement) - internal/config/: SpecialConfig, OrchestrationCfg, LoadSpecial() - shell/bus/bus.go: protocolo request-reply (SendAndWait, Reply) para delegación - shell/matrix/listener.go: InterceptFunc para interceptar eventos en salas orquestadas - agents/runtime.go: SetBus, listenBus, handleTaskEvent para recibir tareas del orquestador - cmd/launcher/main.go: creación de bus compartido, arranque del orquestador antes de bots Incluye deduplicación para evitar que múltiples listeners en la misma sala disparen el orquestador más de una vez por mensaje. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,9 @@ import (
|
||||
asistente2agent "github.com/enmanuel/agents/agents/asistente-2"
|
||||
"github.com/enmanuel/agents/internal/config"
|
||||
"github.com/enmanuel/agents/pkg/decision"
|
||||
"github.com/enmanuel/agents/pkg/orchestration"
|
||||
"github.com/enmanuel/agents/shell/bus"
|
||||
orchshell "github.com/enmanuel/agents/shell/orchestration"
|
||||
)
|
||||
|
||||
// rulesRegistry maps agent IDs to their rule factories.
|
||||
@@ -58,6 +61,21 @@ func main() {
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
// ── Shared bus for inter-agent communication ──
|
||||
agentBus := bus.New()
|
||||
|
||||
// ── Start special agents (orchestrator, etc.) BEFORE normal bots ──
|
||||
orch, err := startOrchestrator(agentBus, logger)
|
||||
if err != nil {
|
||||
// Non-fatal: orchestration is optional
|
||||
logger.Warn("orchestrator not started", "err", err)
|
||||
} else {
|
||||
logger.Info("orchestrator ready",
|
||||
"managed_rooms", len(orch.cfg.Orchestration.Rooms),
|
||||
)
|
||||
}
|
||||
|
||||
// ── Start normal agents ──
|
||||
var wg sync.WaitGroup
|
||||
for _, path := range configPaths {
|
||||
path := path
|
||||
@@ -80,6 +98,25 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Connect agent to bus for orchestration
|
||||
a.SetBus(agentBus)
|
||||
|
||||
// If orchestrator is active, set interceptor so bots don't
|
||||
// handle events directly in orchestrated rooms.
|
||||
// The first bot's listener to receive the event will trigger orchestration.
|
||||
if orch != nil {
|
||||
a.SetInterceptor(orch.orchestrator.Intercept)
|
||||
}
|
||||
|
||||
// Register this agent as a participant in the orchestrator
|
||||
if orch != nil {
|
||||
orch.orchestrator.RegisterParticipant(orchestration.ParticipantInfo{
|
||||
ID: cfg.Agent.ID,
|
||||
Description: cfg.Agent.Description,
|
||||
Capabilities: cfg.Agent.Tags,
|
||||
})
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -106,6 +143,37 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// orchHandle wraps a running orchestrator with its config for the launcher.
|
||||
type orchHandle struct {
|
||||
orchestrator *orchshell.Orchestrator
|
||||
cfg *config.SpecialConfig
|
||||
}
|
||||
|
||||
// startOrchestrator scans agents/specials/orchestrator/config.yaml and
|
||||
// initializes the orchestrator if found and enabled.
|
||||
func startOrchestrator(agentBus *bus.Bus, logger *slog.Logger) (*orchHandle, error) {
|
||||
cfgPath := filepath.Join("agents", "specials", "orchestrator", "config.yaml")
|
||||
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := config.LoadSpecial(cfgPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !cfg.Special.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
orchLogger := logger.With("component", "orchestrator")
|
||||
orch, err := orchshell.New(cfg, agentBus, orchLogger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &orchHandle{orchestrator: orch, cfg: cfg}, nil
|
||||
}
|
||||
|
||||
func rulesFor(agentID string, logger *slog.Logger) []decision.Rule {
|
||||
factory, ok := rulesRegistry[agentID]
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user