Files
LaodingBot/cmd/bot/main.go
2026-02-21 23:01:39 +08:00

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)
}
}