From a13f39afdd79e9b366b9c401ab33e20746184bbf Mon Sep 17 00:00:00 2001 From: btcman <109521734+manwallet@users.noreply.github.com> Date: Wed, 29 Oct 2025 22:48:28 +0800 Subject: [PATCH] Feature: Add support for custom OpenAI-compatible API This update enables users to configure any OpenAI-compatible API endpoint, allowing the use of: - OpenAI official API (GPT-4, GPT-4o, etc.) - OpenRouter (access to multiple models) - Local deployed models (Ollama, LM Studio, etc.) - Other OpenAI-format compatible API services Changes: - config: Add custom_api_url, custom_api_key, custom_model_name fields - mcp: Add SetCustomAPI function and ProviderCustom constant - trader: Update AI initialization logic to support custom API - manager: Pass custom API config to trader instances - Add CUSTOM_API.md documentation with usage examples - Update config.json.example with custom API sample Co-Authored-By: tinkle-community --- CUSTOM_API.md | 185 ++++++++++++++++++++++++++++++++++++++ config.json.example | 13 +++ config/config.go | 20 ++++- manager/trader_manager.go | 3 + mcp/client.go | 10 +++ trader/auto_trader.go | 13 ++- 6 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 CUSTOM_API.md diff --git a/CUSTOM_API.md b/CUSTOM_API.md new file mode 100644 index 00000000..979a89c6 --- /dev/null +++ b/CUSTOM_API.md @@ -0,0 +1,185 @@ +# 自定义 AI API 使用指南 + +## 功能说明 + +现在 NOFX 支持使用任何 OpenAI 格式兼容的 API,包括: +- OpenAI 官方 API (gpt-4o, gpt-4-turbo 等) +- OpenRouter (可访问多种模型) +- 本地部署的模型 (Ollama, LM Studio 等) +- 其他兼容 OpenAI 格式的 API 服务 + +## 配置方式 + +在 `config.json` 中添加使用自定义 API 的 trader: + +```json +{ + "traders": [ + { + "id": "trader_custom", + "name": "My Custom AI Trader", + "ai_model": "custom", + "exchange": "binance", + + "binance_api_key": "your_binance_api_key", + "binance_secret_key": "your_binance_secret_key", + + "custom_api_url": "https://api.openai.com/v1", + "custom_api_key": "sk-your-openai-api-key", + "custom_model_name": "gpt-4o", + + "initial_balance": 1000, + "scan_interval_minutes": 3 + } + ] +} +``` + +## 配置字段说明 + +| 字段 | 类型 | 必需 | 说明 | +|-----|------|------|------| +| `ai_model` | string | ✅ | 设置为 `"custom"` 启用自定义 API | +| `custom_api_url` | string | ✅ | API 的 Base URL (不含 `/chat/completions`) | +| `custom_api_key` | string | ✅ | API 密钥 | +| `custom_model_name` | string | ✅ | 模型名称 (如 `gpt-4o`, `claude-3-5-sonnet` 等) | + +## 使用示例 + +### 1. OpenAI 官方 API + +```json +{ + "ai_model": "custom", + "custom_api_url": "https://api.openai.com/v1", + "custom_api_key": "sk-proj-xxxxx", + "custom_model_name": "gpt-4o" +} +``` + +### 2. OpenRouter + +```json +{ + "ai_model": "custom", + "custom_api_url": "https://openrouter.ai/api/v1", + "custom_api_key": "sk-or-xxxxx", + "custom_model_name": "anthropic/claude-3.5-sonnet" +} +``` + +### 3. 本地 Ollama + +```json +{ + "ai_model": "custom", + "custom_api_url": "http://localhost:11434/v1", + "custom_api_key": "ollama", + "custom_model_name": "llama3.1:70b" +} +``` + +### 4. Azure OpenAI + +```json +{ + "ai_model": "custom", + "custom_api_url": "https://your-resource.openai.azure.com/openai/deployments/your-deployment", + "custom_api_key": "your-azure-api-key", + "custom_model_name": "gpt-4" +} +``` + +## 兼容性要求 + +自定义 API 必须: +1. 支持 OpenAI Chat Completions 格式 +2. 接受 `POST /chat/completions` 端点 +3. 支持 `Authorization: Bearer {api_key}` 认证 +4. 返回标准的 OpenAI 响应格式 + +## 注意事项 + +1. **URL 格式**:`custom_api_url` 应该是 Base URL,系统会自动添加 `/chat/completions` + - ✅ 正确:`https://api.openai.com/v1` + - ❌ 错误:`https://api.openai.com/v1/chat/completions` + +2. **模型名称**:确保 `custom_model_name` 与 API 提供商支持的模型名称完全一致 + +3. **API 密钥**:某些本地部署的模型可能不需要真实的 API 密钥,可以填写任意字符串 + +4. **超时设置**:默认超时时间为 120 秒,如果模型响应较慢可能需要调整 + +## 多 AI 对比交易 + +你可以同时配置多个不同 AI 的 trader 进行对比: + +```json +{ + "traders": [ + { + "id": "deepseek_trader", + "ai_model": "deepseek", + "deepseek_key": "sk-xxxxx", + ... + }, + { + "id": "gpt4_trader", + "ai_model": "custom", + "custom_api_url": "https://api.openai.com/v1", + "custom_api_key": "sk-xxxxx", + "custom_model_name": "gpt-4o", + ... + }, + { + "id": "claude_trader", + "ai_model": "custom", + "custom_api_url": "https://openrouter.ai/api/v1", + "custom_api_key": "sk-or-xxxxx", + "custom_model_name": "anthropic/claude-3.5-sonnet", + ... + } + ] +} +``` + +## 故障排除 + +### 问题:配置验证失败 + +**错误信息**:`使用自定义API时必须配置custom_api_url` + +**解决方案**:确保设置了 `ai_model: "custom"` 后,同时配置了: +- `custom_api_url` +- `custom_api_key` +- `custom_model_name` + +### 问题:API 调用失败 + +**可能原因**: +1. URL 格式错误(检查是否包含了 `/chat/completions`) +2. API 密钥无效 +3. 模型名称错误 +4. 网络连接问题 + +**调试方法**:查看日志中的错误信息,通常会包含 HTTP 状态码和错误详情 + +## 向后兼容性 + +现有的 `deepseek` 和 `qwen` 配置完全不受影响,可以继续使用: + +```json +{ + "ai_model": "deepseek", + "deepseek_key": "sk-xxxxx" +} +``` + +或 + +```json +{ + "ai_model": "qwen", + "qwen_key": "sk-xxxxx" +} +``` diff --git a/config.json.example b/config.json.example index 60a494ea..d6865ec5 100644 --- a/config.json.example +++ b/config.json.example @@ -21,6 +21,19 @@ "qwen_key": "your_qwen_api_key", "initial_balance": 1000, "scan_interval_minutes": 3 + }, + { + "id": "binance_custom", + "name": "Binance Custom API Trader", + "ai_model": "custom", + "exchange": "binance", + "binance_api_key": "your_binance_api_key", + "binance_secret_key": "your_binance_secret_key", + "custom_api_url": "https://api.openai.com/v1", + "custom_api_key": "sk-your-api-key", + "custom_model_name": "gpt-4o", + "initial_balance": 1000, + "scan_interval_minutes": 3 } ], "leverage": { diff --git a/config/config.go b/config/config.go index 2e26c925..bed19a3b 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,11 @@ type TraderConfig struct { QwenKey string `json:"qwen_key,omitempty"` DeepSeekKey string `json:"deepseek_key,omitempty"` + // 自定义AI API配置(支持任何OpenAI格式的API) + CustomAPIURL string `json:"custom_api_url,omitempty"` + CustomAPIKey string `json:"custom_api_key,omitempty"` + CustomModelName string `json:"custom_model_name,omitempty"` + InitialBalance float64 `json:"initial_balance"` ScanIntervalMinutes int `json:"scan_interval_minutes"` } @@ -95,8 +100,8 @@ func (c *Config) Validate() error { if trader.Name == "" { return fmt.Errorf("trader[%d]: Name不能为空", i) } - if trader.AIModel != "qwen" && trader.AIModel != "deepseek" { - return fmt.Errorf("trader[%d]: ai_model必须是 'qwen' 或 'deepseek'", i) + if trader.AIModel != "qwen" && trader.AIModel != "deepseek" && trader.AIModel != "custom" { + return fmt.Errorf("trader[%d]: ai_model必须是 'qwen', 'deepseek' 或 'custom'", i) } // 验证交易平台配置 @@ -124,6 +129,17 @@ func (c *Config) Validate() error { if trader.AIModel == "deepseek" && trader.DeepSeekKey == "" { return fmt.Errorf("trader[%d]: 使用DeepSeek时必须配置deepseek_key", i) } + if trader.AIModel == "custom" { + if trader.CustomAPIURL == "" { + return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_api_url", i) + } + if trader.CustomAPIKey == "" { + return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_api_key", i) + } + if trader.CustomModelName == "" { + return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_model_name", i) + } + } if trader.InitialBalance <= 0 { return fmt.Errorf("trader[%d]: initial_balance必须大于0", i) } diff --git a/manager/trader_manager.go b/manager/trader_manager.go index 18373a13..cf41570f 100644 --- a/manager/trader_manager.go +++ b/manager/trader_manager.go @@ -45,6 +45,9 @@ func (tm *TraderManager) AddTrader(cfg config.TraderConfig, coinPoolURL string, UseQwen: cfg.AIModel == "qwen", DeepSeekKey: cfg.DeepSeekKey, QwenKey: cfg.QwenKey, + CustomAPIURL: cfg.CustomAPIURL, + CustomAPIKey: cfg.CustomAPIKey, + CustomModelName: cfg.CustomModelName, ScanInterval: cfg.GetScanInterval(), InitialBalance: cfg.InitialBalance, BTCETHLeverage: leverage.BTCETHLeverage, // 使用配置的杠杆倍数 diff --git a/mcp/client.go b/mcp/client.go index 55f1fb81..ead27384 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -16,6 +16,7 @@ type Provider string const ( ProviderDeepSeek Provider = "deepseek" ProviderQwen Provider = "qwen" + ProviderCustom Provider = "custom" ) // Config AI API配置 @@ -53,6 +54,15 @@ func SetQwenAPIKey(apiKey, secretKey string) { defaultConfig.Model = "qwen-plus" // 可选: qwen-turbo, qwen-plus, qwen-max } +// SetCustomAPI 设置自定义OpenAI兼容API +func SetCustomAPI(apiURL, apiKey, modelName string) { + defaultConfig.Provider = ProviderCustom + defaultConfig.APIKey = apiKey + defaultConfig.BaseURL = apiURL + defaultConfig.Model = modelName + defaultConfig.Timeout = 120 * time.Second +} + // SetConfig 设置完整的AI配置(高级用户) func SetConfig(config Config) { if config.Timeout == 0 { diff --git a/trader/auto_trader.go b/trader/auto_trader.go index c90ceef1..1e6b84a3 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -38,6 +38,11 @@ type AutoTraderConfig struct { DeepSeekKey string QwenKey string + // 自定义AI API配置 + CustomAPIURL string + CustomAPIKey string + CustomModelName string + // 扫描配置 ScanInterval time.Duration // 扫描间隔(建议3分钟) @@ -91,10 +96,16 @@ func NewAutoTrader(config AutoTraderConfig) (*AutoTrader, error) { } // 初始化AI - if config.UseQwen { + if config.AIModel == "custom" { + // 使用自定义API + mcp.SetCustomAPI(config.CustomAPIURL, config.CustomAPIKey, config.CustomModelName) + log.Printf("🤖 [%s] 使用自定义AI API: %s (模型: %s)", config.Name, config.CustomAPIURL, config.CustomModelName) + } else if config.UseQwen || config.AIModel == "qwen" { + // 使用Qwen mcp.SetQwenAPIKey(config.QwenKey, "") log.Printf("🤖 [%s] 使用阿里云Qwen AI", config.Name) } else { + // 默认使用DeepSeek mcp.SetDeepSeekAPIKey(config.DeepSeekKey) log.Printf("🤖 [%s] 使用DeepSeek AI", config.Name) }