package main import ( "context" "flag" "log" "os" "ai-agent-scaffold-go/internal/config" "ai-agent-scaffold-go/internal/handler" "ai-agent-scaffold-go/internal/model" "ai-agent-scaffold-go/internal/service" "github.com/gin-gonic/gin" "github.com/joho/godotenv" "go.uber.org/zap" ) func main() { envPath := flag.String("env", ".env", "path to dotenv file (empty to skip)") configPath := flag.String("config", "configs/application.yaml", "path to application.yaml") flag.Parse() // 1. 加载 .env 文件 loadDotenv(*envPath) // 2. 加载应用配置 appCfg, err := config.LoadApplication(*configPath) if err != nil { log.Fatalf("load config: %v", err) } // 3. 初始化日志 logger, _ := zap.NewProduction() if appCfg.App.Env == "local" || appCfg.App.Env == "dev" { logger, _ = zap.NewDevelopment() } defer logger.Sync() // 4. 组装 Agent registry := model.NewInMemoryAgentRegistry() sessions := model.NewInMemorySessionStore() timeout, _ := appCfg.LLM.RequestTimeoutDuration() agents, err := service.LoadAndAssemble(context.Background(), appCfg.Agent.ConfigPaths, timeout) if err != nil { logger.Fatal("assemble agents failed", zap.Error(err)) } for _, agent := range agents { if err := registry.Register(agent); err != nil { logger.Fatal("register agent failed", zap.Error(err)) } } chatService := service.NewChatService(registry, sessions) // 5. 配置 HTTP 路由 router := gin.Default() router.Use(corsMiddleware()) router.GET("/healthz", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) handler.RegisterRoutes(router, chatService) // 6. 启动服务 addr := appCfg.Server.Addr logger.Info("server starting", zap.String("addr", addr), zap.Int("agents", len(agents))) if err := router.Run(addr); err != nil { logger.Fatal("server stopped", zap.Error(err)) } } // corsMiddleware 跨域中间件 func corsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { origin := c.GetHeader("Origin") if origin != "" { c.Header("Access-Control-Allow-Origin", origin) c.Header("Vary", "Origin") } else { c.Header("Access-Control-Allow-Origin", "*") } c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization") c.Header("Access-Control-Allow-Credentials", "true") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() } } // loadDotenv 加载 dotenv 文件,已有环境变量不会被覆盖 func loadDotenv(path string) { if path == "" { return } if _, err := os.Stat(path); os.IsNotExist(err) { return } pairs, err := godotenv.Read(path) if err != nil { log.Fatalf("read dotenv: %v", err) } for key, value := range pairs { if _, exists := os.LookupEnv(key); !exists { os.Setenv(key, value) } } }