orca.ai/pkg/actor/orchestrator.go
大森 6b94476347 Initial commit: Orca Agent Framework
Core features:
- Microkernel architecture with Actor model
- Session management with JSONL persistence
- Tool system (5 built-in tools)
- Skill system with SKILL.md parsing
- Sandbox security execution
- Ollama integration with gemma4:e4b
- Prompt-based tool calling (compatible with native function calling)
- REPL interface

11 packages, all tests passing
2026-05-08 00:55:48 +08:00

124 lines
3.3 KiB
Go

package actor
import (
"context"
"fmt"
"sync"
"github.com/orca/orca/pkg/bus"
)
// Orchestrator is an agent that coordinates task execution across a pool of workers.
//
// It receives task requests, delegates them to available workers, and
// collects responses. The orchestrator maintains a registry of worker
// agents and can dynamically add or remove them.
type Orchestrator struct {
*BaseAgent
workers map[string]Agent
bus bus.MessageBus
mu sync.RWMutex
}
// NewOrchestrator creates a new Orchestrator agent with the given id and message bus.
// The agent is started automatically upon creation.
func NewOrchestrator(id string, mb bus.MessageBus) *Orchestrator {
o := &Orchestrator{
BaseAgent: NewBaseAgent(id, "orchestrator"),
workers: make(map[string]Agent),
bus: mb,
}
o.SetHandler(o.handleMessage)
// Start the agent's processing loop
if err := o.Start(); err != nil {
// This should not happen since handler is set above
panic(fmt.Sprintf("orchestrator: failed to start: %v", err))
}
return o
}
// handleMessage routes incoming messages to the appropriate handler.
func (o *Orchestrator) handleMessage(ctx context.Context, msg bus.Message) (bus.Message, error) {
switch msg.Type {
case bus.MsgTypeTaskRequest:
return o.handleTask(ctx, msg)
case bus.MsgTypeSystem:
return o.handleSystem(ctx, msg)
default:
return bus.Message{}, fmt.Errorf("orchestrator %s: unsupported message type %s", o.ID(), msg.Type)
}
}
// handleTask processes a task request by delegating to an available worker.
func (o *Orchestrator) handleTask(ctx context.Context, msg bus.Message) (bus.Message, error) {
o.mu.RLock()
defer o.mu.RUnlock()
if len(o.workers) == 0 {
return bus.Message{}, fmt.Errorf("orchestrator %s: no workers available", o.ID())
}
// Simple round-robin: pick the first available worker
for _, w := range o.workers {
return w.Process(ctx, msg)
}
return bus.Message{}, fmt.Errorf("orchestrator %s: no workers available", o.ID())
}
// handleSystem processes internal system messages.
func (o *Orchestrator) handleSystem(ctx context.Context, msg bus.Message) (bus.Message, error) {
return bus.Message{
ID: msg.ID + "-ack",
Type: bus.MsgTypeSystem,
From: o.ID(),
To: msg.From,
Content: "orchestrator acknowledged",
}, nil
}
// AddWorker registers a worker agent with this orchestrator.
func (o *Orchestrator) AddWorker(w Agent) {
o.mu.Lock()
defer o.mu.Unlock()
o.workers[w.ID()] = w
}
// RemoveWorker unregisters a worker agent from this orchestrator.
func (o *Orchestrator) RemoveWorker(id string) {
o.mu.Lock()
defer o.mu.Unlock()
delete(o.workers, id)
}
// WorkerCount returns the number of registered workers.
func (o *Orchestrator) WorkerCount() int {
o.mu.RLock()
defer o.mu.RUnlock()
return len(o.workers)
}
// GetWorker retrieves a registered worker by ID.
func (o *Orchestrator) GetWorker(id string) (Agent, bool) {
o.mu.RLock()
defer o.mu.RUnlock()
w, ok := o.workers[id]
return w, ok
}
// ListWorkers returns all registered workers.
func (o *Orchestrator) ListWorkers() []Agent {
o.mu.RLock()
defer o.mu.RUnlock()
workers := make([]Agent, 0, len(o.workers))
for _, w := range o.workers {
workers = append(workers, w)
}
return workers
}
// Bus returns the orchestrator's message bus reference.
func (o *Orchestrator) Bus() bus.MessageBus {
return o.bus
}