orca.ai/cmd/orca/main.go
大森 81f6802f2e fix: skills loading from ~/.agents/skills
- Fix skill manager directory path to ~/.agents/skills
- Add InitPlugins() call in main.go to load skills on startup
- Skills now recognized and loaded correctly
2026-05-08 22:17:24 +08:00

230 lines
5.5 KiB
Go

// Orca is a Go-based Agent framework with a microkernel architecture.
//
// It supports multi-agent collaboration, persistent session memory,
// skill-based automation, sandboxed execution, custom tool registration,
// and local LLM integration via Ollama.
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/orca/orca/internal/config"
"github.com/orca/orca/pkg/kernel"
)
func main() {
// Load configuration from environment variables
cfg := config.LoadConfigFromEnv()
if v := os.Getenv("OLLAMA_BASE_URL"); v != "" {
cfg.Ollama.BaseURL = v
}
if v := os.Getenv("OLLAMA_MODEL"); v != "" {
cfg.Ollama.Model = v
}
if v := os.Getenv("OLLAMA_TIMEOUT"); v != "" {
if d, err := time.ParseDuration(v); err == nil {
cfg.Ollama.Timeout = d
}
}
if v := os.Getenv("DEEPSEEK_BASE_URL"); v != "" {
cfg.DeepSeek.BaseURL = v
}
if v := os.Getenv("DEEPSEEK_MODEL"); v != "" {
cfg.DeepSeek.Model = v
}
if v := os.Getenv("DEEPSEEK_API_KEY"); v != "" {
cfg.DeepSeek.APIKey = v
}
if v := os.Getenv("DEEPSEEK_TIMEOUT"); v != "" {
if d, err := time.ParseDuration(v); err == nil {
cfg.DeepSeek.Timeout = d
}
}
// Create and start kernel
k := kernel.NewWithConfig(cfg)
if err := k.Start(); err != nil {
log.Fatalf("Failed to start kernel: %v", err)
}
if err := k.InitPlugins(); err != nil {
log.Printf("Warning: failed to load skills: %v", err)
}
k.SetStreamWriter(os.Stdout)
fmt.Println("Orca Agent Framework")
fmt.Println("Kernel started successfully")
if cfg.Provider == config.ProviderDeepSeek {
fmt.Printf(" Provider: DeepSeek\n")
fmt.Printf(" LLM Model: %s\n", cfg.DeepSeek.Model)
} else {
fmt.Printf(" Provider: Ollama\n")
fmt.Printf(" LLM Model: %s\n", cfg.Ollama.Model)
fmt.Printf(" Ollama URL: %s\n", cfg.Ollama.BaseURL)
}
fmt.Println("Type your message or /help for commands.")
fmt.Println()
// Handle graceful shutdown
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
// REPL loop in a goroutine so we can catch signals
done := make(chan struct{})
go func() {
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("> ")
if !scanner.Scan() {
break
}
input := strings.TrimSpace(scanner.Text())
if input == "" {
continue
}
// Handle commands
if strings.HasPrefix(input, "/") {
handleCommand(input, k)
continue
}
// Send message to LLM agent via kernel
_, err := k.SendMessage("user", "llm", input)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
fmt.Println()
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
}
close(done)
}()
// Wait for either SIGINT or REPL exit
select {
case <-sig:
fmt.Println("\nShutting down Orca kernel...")
case <-done:
fmt.Println("\nInput closed. Shutting down Orca kernel...")
}
if err := k.Stop(); err != nil {
log.Fatalf("Failed to stop kernel: %v", err)
}
fmt.Println("Orca kernel shut down gracefully.")
}
// handleCommand processes REPL commands.
func handleCommand(cmd string, k *kernel.Kernel) {
switch cmd {
case "/help":
fmt.Println("Available commands:")
fmt.Println(" /help - Show this help message")
fmt.Println(" /exit - Exit the program")
fmt.Println(" /quit - Exit the program")
fmt.Println(" /plugins - List registered plugins")
fmt.Println(" /agents - List active agents")
fmt.Println(" /tools - List registered tools")
fmt.Println(" /skills - List loaded skills")
fmt.Println(" /status - Show kernel status")
fmt.Println()
fmt.Println("Any other input is sent to the LLM agent for processing.")
case "/exit", "/quit":
fmt.Println("Goodbye!")
os.Exit(0)
case "/plugins":
plugins := k.ListPlugins()
if len(plugins) == 0 {
fmt.Println("No plugins registered.")
} else {
fmt.Println("Registered plugins:")
for _, p := range plugins {
fmt.Printf(" - %s (%s)\n", p.Name(), p.Version())
}
}
case "/agents":
as := k.ActorSystem()
if as == nil {
fmt.Println("Actor system not initialized.")
return
}
infos := as.AgentInfos()
if len(infos) == 0 {
fmt.Println("No agents running.")
} else {
fmt.Println("Active agents:")
for _, info := range infos {
fmt.Printf(" - %s [%s] (status: %s)\n", info.ID, info.Role, info.Status)
}
}
case "/tools":
tm := k.ToolManager()
if tm == nil {
fmt.Println("Tool manager not initialized.")
return
}
tools := tm.List()
if len(tools) == 0 {
fmt.Println("No tools registered.")
} else {
fmt.Println("Registered tools:")
for _, t := range tools {
fmt.Printf(" - %s: %s\n", t.Name(), t.Description())
}
}
case "/skills":
sm := k.SkillManager()
if sm == nil {
fmt.Println("Skill manager not initialized.")
return
}
skills := sm.ListSkills()
if len(skills) == 0 {
fmt.Println("No skills loaded.")
} else {
fmt.Println("Loaded skills:")
for _, s := range skills {
fmt.Printf(" - %s: %s\n", s.Name, s.Description)
}
}
case "/status":
fmt.Printf("Kernel running: %v\n", k.IsRunning())
if tm := k.ToolManager(); tm != nil {
fmt.Printf("Tools registered: %d\n", tm.Count())
}
if as := k.ActorSystem(); as != nil {
fmt.Printf("Agents active: %d\n", as.AgentCount())
}
if sm := k.SkillManager(); sm != nil {
fmt.Printf("Skills loaded: %d\n", len(sm.ListSkills()))
}
default:
fmt.Printf("Unknown command: %s\n", cmd)
fmt.Println("Type /help for available commands.")
}
}