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:
+66
-1
@@ -2,8 +2,16 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Well-known message kinds used by the orchestrator.
|
||||
const (
|
||||
KindTask = "task" // orchestrator → bot: handle this question
|
||||
KindTaskResult = "task_result" // bot → orchestrator: here is my answer
|
||||
)
|
||||
|
||||
// AgentID identifies an agent.
|
||||
@@ -21,11 +29,17 @@ type AgentMessage struct {
|
||||
type Bus struct {
|
||||
mu sync.RWMutex
|
||||
channels map[AgentID]chan AgentMessage
|
||||
|
||||
replyMu sync.Mutex
|
||||
replyChs map[string]chan AgentMessage // taskID → one-shot reply channel
|
||||
}
|
||||
|
||||
// New creates a new Bus.
|
||||
func New() *Bus {
|
||||
return &Bus{channels: make(map[AgentID]chan AgentMessage)}
|
||||
return &Bus{
|
||||
channels: make(map[AgentID]chan AgentMessage),
|
||||
replyChs: make(map[string]chan AgentMessage),
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe registers an agent and returns its receive channel.
|
||||
@@ -53,6 +67,57 @@ func (b *Bus) Send(msg AgentMessage) error {
|
||||
}
|
||||
}
|
||||
|
||||
// SendAndWait sends a task message and blocks until a reply with the matching
|
||||
// taskID arrives or the context expires. The caller must ensure the reply is
|
||||
// routed via Reply().
|
||||
func (b *Bus) SendAndWait(ctx context.Context, msg AgentMessage, taskID string, timeout time.Duration) (AgentMessage, error) {
|
||||
ch := make(chan AgentMessage, 1)
|
||||
b.replyMu.Lock()
|
||||
b.replyChs[taskID] = ch
|
||||
b.replyMu.Unlock()
|
||||
|
||||
defer func() {
|
||||
b.replyMu.Lock()
|
||||
delete(b.replyChs, taskID)
|
||||
b.replyMu.Unlock()
|
||||
}()
|
||||
|
||||
if err := b.Send(msg); err != nil {
|
||||
return AgentMessage{}, err
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case reply := <-ch:
|
||||
return reply, nil
|
||||
case <-timer.C:
|
||||
return AgentMessage{}, fmt.Errorf("task %s: delegation timeout after %s", taskID, timeout)
|
||||
case <-ctx.Done():
|
||||
return AgentMessage{}, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Reply routes a task_result message to the waiting SendAndWait caller.
|
||||
// If no one is waiting for this taskID, it falls back to regular Send.
|
||||
func (b *Bus) Reply(taskID string, msg AgentMessage) error {
|
||||
b.replyMu.Lock()
|
||||
ch, ok := b.replyChs[taskID]
|
||||
b.replyMu.Unlock()
|
||||
|
||||
if ok {
|
||||
select {
|
||||
case ch <- msg:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("reply channel full for task %s", taskID)
|
||||
}
|
||||
}
|
||||
// Fallback: deliver via regular channel
|
||||
return b.Send(msg)
|
||||
}
|
||||
|
||||
// Unsubscribe removes an agent from the bus.
|
||||
func (b *Bus) Unsubscribe(id AgentID) {
|
||||
b.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user