package config import ( "fmt" "os" "path/filepath" "strings" "gopkg.in/yaml.v3" ) type Config struct { Server ServerConfig `yaml:"server"` Log LogConfig `yaml:"log"` MCP MCPConfig `yaml:"mcp"` OpenAI OpenAIConfig `yaml:"openai"` Security SecurityConfig `yaml:"security"` Database DatabaseConfig `yaml:"database"` } type ServerConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` } type LogConfig struct { Level string `yaml:"level"` Output string `yaml:"output"` } type MCPConfig struct { Enabled bool `yaml:"enabled"` Host string `yaml:"host"` Port int `yaml:"port"` } type OpenAIConfig struct { APIKey string `yaml:"api_key"` BaseURL string `yaml:"base_url"` Model string `yaml:"model"` } type SecurityConfig struct { Tools []ToolConfig `yaml:"tools,omitempty"` // 向后兼容:支持在主配置文件中定义工具 ToolsDir string `yaml:"tools_dir,omitempty"` // 工具配置文件目录(新方式) } type DatabaseConfig struct { Path string `yaml:"path"` } type ToolConfig struct { Name string `yaml:"name"` Command string `yaml:"command"` Args []string `yaml:"args,omitempty"` // 固定参数(可选) ShortDescription string `yaml:"short_description,omitempty"` // 简短描述(用于工具列表,减少token消耗) Description string `yaml:"description"` // 详细描述(用于工具文档) Enabled bool `yaml:"enabled"` Parameters []ParameterConfig `yaml:"parameters,omitempty"` // 参数定义(可选) ArgMapping string `yaml:"arg_mapping,omitempty"` // 参数映射方式: "auto", "manual", "template"(可选) } // ParameterConfig 参数配置 type ParameterConfig struct { Name string `yaml:"name"` // 参数名称 Type string `yaml:"type"` // 参数类型: string, int, bool, array Description string `yaml:"description"` // 参数描述 Required bool `yaml:"required,omitempty"` // 是否必需 Default interface{} `yaml:"default,omitempty"` // 默认值 Flag string `yaml:"flag,omitempty"` // 命令行标志,如 "-u", "--url", "-p" Position *int `yaml:"position,omitempty"` // 位置参数的位置(从0开始) Format string `yaml:"format,omitempty"` // 参数格式: "flag", "positional", "combined" (flag=value), "template" Template string `yaml:"template,omitempty"` // 模板字符串,如 "{flag} {value}" 或 "{value}" Options []string `yaml:"options,omitempty"` // 可选值列表(用于枚举) } func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("读取配置文件失败: %w", err) } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("解析配置文件失败: %w", err) } // 如果配置了工具目录,从目录加载工具配置 if cfg.Security.ToolsDir != "" { configDir := filepath.Dir(path) toolsDir := cfg.Security.ToolsDir // 如果是相对路径,相对于配置文件所在目录 if !filepath.IsAbs(toolsDir) { toolsDir = filepath.Join(configDir, toolsDir) } tools, err := LoadToolsFromDir(toolsDir) if err != nil { return nil, fmt.Errorf("从工具目录加载工具配置失败: %w", err) } // 合并工具配置:目录中的工具优先,主配置中的工具作为补充 existingTools := make(map[string]bool) for _, tool := range tools { existingTools[tool.Name] = true } // 添加主配置中不存在于目录中的工具(向后兼容) for _, tool := range cfg.Security.Tools { if !existingTools[tool.Name] { tools = append(tools, tool) } } cfg.Security.Tools = tools } return &cfg, nil } // LoadToolsFromDir 从目录加载所有工具配置文件 func LoadToolsFromDir(dir string) ([]ToolConfig, error) { var tools []ToolConfig // 检查目录是否存在 if _, err := os.Stat(dir); os.IsNotExist(err) { return tools, nil // 目录不存在时返回空列表,不报错 } // 读取目录中的所有 .yaml 和 .yml 文件 entries, err := os.ReadDir(dir) if err != nil { return nil, fmt.Errorf("读取工具目录失败: %w", err) } for _, entry := range entries { if entry.IsDir() { continue } name := entry.Name() if !strings.HasSuffix(name, ".yaml") && !strings.HasSuffix(name, ".yml") { continue } filePath := filepath.Join(dir, name) tool, err := LoadToolFromFile(filePath) if err != nil { // 记录错误但继续加载其他文件 fmt.Printf("警告: 加载工具配置文件 %s 失败: %v\n", filePath, err) continue } tools = append(tools, *tool) } return tools, nil } // LoadToolFromFile 从单个文件加载工具配置 func LoadToolFromFile(path string) (*ToolConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("读取文件失败: %w", err) } var tool ToolConfig if err := yaml.Unmarshal(data, &tool); err != nil { return nil, fmt.Errorf("解析工具配置失败: %w", err) } // 验证必需字段 if tool.Name == "" { return nil, fmt.Errorf("工具名称不能为空") } if tool.Command == "" { return nil, fmt.Errorf("工具命令不能为空") } return &tool, nil } func Default() *Config { return &Config{ Server: ServerConfig{ Host: "0.0.0.0", Port: 8080, }, Log: LogConfig{ Level: "info", Output: "stdout", }, MCP: MCPConfig{ Enabled: true, Host: "0.0.0.0", Port: 8081, }, OpenAI: OpenAIConfig{ BaseURL: "https://api.openai.com/v1", Model: "gpt-4", }, Security: SecurityConfig{ Tools: []ToolConfig{}, // 工具配置应该从 config.yaml 或 tools/ 目录加载 ToolsDir: "tools", // 默认工具目录 }, Database: DatabaseConfig{ Path: "data/conversations.db", }, } }