112 lines
3.5 KiB
Go
112 lines
3.5 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"os/signal"
|
||
|
|
"syscall"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"laodingbot/internal/agent"
|
||
|
|
"laodingbot/internal/config"
|
||
|
|
"laodingbot/internal/knowledge"
|
||
|
|
"laodingbot/internal/llm"
|
||
|
|
"laodingbot/internal/logger"
|
||
|
|
"laodingbot/internal/memory"
|
||
|
|
"laodingbot/internal/tools"
|
||
|
|
"laodingbot/internal/tools/filetool"
|
||
|
|
"laodingbot/internal/tools/shelltool"
|
||
|
|
"laodingbot/internal/transport/feishu"
|
||
|
|
"laodingbot/internal/transport/telegram"
|
||
|
|
)
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||
|
|
defer stop()
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
panic(fmt.Sprintf("load config failed: %v", err))
|
||
|
|
}
|
||
|
|
|
||
|
|
appLogger, err := logger.New(cfg.LogLevel)
|
||
|
|
if err != nil {
|
||
|
|
panic(fmt.Sprintf("init logger failed: %v", err))
|
||
|
|
}
|
||
|
|
appLogger = appLogger.WithComponent("main")
|
||
|
|
appLogger.Infof("config loaded; channel=%s, log_level=%s", cfg.MessageChannel, cfg.LogLevel)
|
||
|
|
|
||
|
|
store, err := memory.NewSQLiteStore(cfg.SQLitePath, appLogger.WithComponent("memory"))
|
||
|
|
if err != nil {
|
||
|
|
appLogger.Errorf("init memory store failed: %v", err)
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
defer store.Close()
|
||
|
|
|
||
|
|
toolRegistry := tools.NewRegistry(appLogger.WithComponent("tools.registry"))
|
||
|
|
toolRegistry.Register(filetool.New(cfg.Security.AllowedDirs, appLogger.WithComponent("tools.file")))
|
||
|
|
toolRegistry.Register(shelltool.New(cfg.Security.AllowedCommands, cfg.Security.WorkDir, 15*time.Second, appLogger.WithComponent("tools.shell")))
|
||
|
|
|
||
|
|
soul, err := knowledge.LoadSoul(cfg.SoulPath)
|
||
|
|
if err != nil {
|
||
|
|
appLogger.Errorf("load soul failed path=%s err=%v", cfg.SoulPath, err)
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
skillsDoc, err := knowledge.LoadSkills(cfg.SkillsDir)
|
||
|
|
if err != nil {
|
||
|
|
appLogger.Errorf("load skills failed dir=%s err=%v", cfg.SkillsDir, err)
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
appLogger.Infof("knowledge loaded soul_path=%s skills_dir=%s", cfg.SoulPath, cfg.SkillsDir)
|
||
|
|
|
||
|
|
llmClient := llm.NewOpenAICompatibleClient(cfg.LLM, appLogger.WithComponent("llm"))
|
||
|
|
engine := agent.NewOrchestrator(
|
||
|
|
llmClient,
|
||
|
|
store,
|
||
|
|
toolRegistry,
|
||
|
|
soul,
|
||
|
|
skillsDoc,
|
||
|
|
cfg.ReactMaxSteps,
|
||
|
|
appLogger.WithComponent("agent"),
|
||
|
|
)
|
||
|
|
|
||
|
|
appLogger.Infof("LaodingBot started, channel=%s", cfg.MessageChannel)
|
||
|
|
if err := runMessageChannel(ctx, cfg, engine, appLogger); err != nil && ctx.Err() == nil {
|
||
|
|
appLogger.Errorf("message channel run failed: %v", err)
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
appLogger.Infof("LaodingBot stopped")
|
||
|
|
}
|
||
|
|
|
||
|
|
func runMessageChannel(ctx context.Context, cfg config.Config, engine *agent.Orchestrator, lg *logger.Logger) error {
|
||
|
|
switch cfg.MessageChannel {
|
||
|
|
case "telegram":
|
||
|
|
tg, err := telegram.NewBot(cfg.Telegram.Token, cfg.Telegram.PollTimeoutSeconds, lg.WithComponent("transport.telegram"))
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("init telegram bot failed: %w", err)
|
||
|
|
}
|
||
|
|
lg.Infof("starting telegram transport")
|
||
|
|
return tg.Run(ctx, func(ctx context.Context, msg telegram.IncomingMessage) (string, error) {
|
||
|
|
return engine.HandleMessage(ctx, msg.ChatID, msg.UserID, msg.Text)
|
||
|
|
})
|
||
|
|
case "feishu":
|
||
|
|
fs, err := feishu.NewBot(
|
||
|
|
cfg.Feishu.AppID,
|
||
|
|
cfg.Feishu.AppSecret,
|
||
|
|
cfg.Feishu.VerifyToken,
|
||
|
|
cfg.Feishu.ListenAddr,
|
||
|
|
cfg.Feishu.EventPath,
|
||
|
|
lg.WithComponent("transport.feishu"),
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("init feishu bot failed: %w", err)
|
||
|
|
}
|
||
|
|
lg.Infof("starting feishu transport")
|
||
|
|
return fs.Run(ctx, func(ctx context.Context, msg feishu.IncomingMessage) (string, error) {
|
||
|
|
return engine.HandleMessage(ctx, msg.ChatID, msg.UserID, msg.Text)
|
||
|
|
})
|
||
|
|
default:
|
||
|
|
return fmt.Errorf("unsupported message channel: %s", cfg.MessageChannel)
|
||
|
|
}
|
||
|
|
}
|