diff --git a/decision/engine.go b/decision/engine.go index 9a75df38..9619cc61 100644 --- a/decision/engine.go +++ b/decision/engine.go @@ -444,12 +444,17 @@ func extractDecisions(response string) ([]Decision, error) { jsonContent := strings.TrimSpace(response[arrayStart : arrayEnd+1]) - // 🔧 修复常见的JSON格式错误:缺少引号的字段值 + // 🔧 先修复全角字符和引号问题(必须在验证之前!) + // 修复常见的JSON格式错误:全角字符、缺少引号的字段值等 // 匹配: "reasoning": 内容"} 或 "reasoning": 内容} (没有引号) // 修复为: "reasoning": "内容"} - // 使用简单的字符串扫描而不是正则表达式 jsonContent = fixMissingQuotes(jsonContent) + // 🔧 验证 JSON 格式(检测常见错误) + if err := validateJSONFormat(jsonContent); err != nil { + return nil, fmt.Errorf("JSON格式验证失败: %w\nJSON内容: %s\n完整响应:\n%s", err, jsonContent, response) + } + // 解析JSON var decisions []Decision if err := json.Unmarshal([]byte(jsonContent), &decisions); err != nil { @@ -478,6 +483,47 @@ func fixMissingQuotes(jsonStr string) string { return jsonStr } +// validateJSONFormat 验证 JSON 格式,检测常见错误 +func validateJSONFormat(jsonStr string) error { + trimmed := strings.TrimSpace(jsonStr) + + // 检查是否是决策对象数组(必须以 [{ 或 [ { 开头) + if !strings.HasPrefix(trimmed, "[{") && !strings.HasPrefix(trimmed, "[ {") { + // 检查是否是纯数字/范围数组(常见错误) + if strings.HasPrefix(trimmed, "[") && !strings.Contains(trimmed[:min(20, len(trimmed))], "{") { + return fmt.Errorf("不是有效的决策数组(必须包含对象 {}),实际内容: %s", trimmed[:min(50, len(trimmed))]) + } + return fmt.Errorf("JSON 必须以 [{ 开头(决策对象数组),实际: %s", trimmed[:min(20, len(trimmed))]) + } + + // 检查是否包含范围符号 ~(LLM 常见错误) + if strings.Contains(jsonStr, "~") { + return fmt.Errorf("JSON 中不可包含范围符号 ~,所有数字必须是精确的单一值") + } + + // 检查是否包含千位分隔符(如 98,000) + // 使用简单的模式匹配:数字+逗号+3位数字 + for i := 0; i < len(jsonStr)-4; i++ { + if jsonStr[i] >= '0' && jsonStr[i] <= '9' && + jsonStr[i+1] == ',' && + jsonStr[i+2] >= '0' && jsonStr[i+2] <= '9' && + jsonStr[i+3] >= '0' && jsonStr[i+3] <= '9' && + jsonStr[i+4] >= '0' && jsonStr[i+4] <= '9' { + return fmt.Errorf("JSON 数字不可包含千位分隔符逗号,发现: %s", jsonStr[i:min(i+10, len(jsonStr))]) + } + } + + return nil +} + +// min 返回两个整数中的较小值 +func min(a, b int) int { + if a < b { + return a + } + return b +} + // validateDecisions 验证所有决策(需要账户信息和杠杆配置) func validateDecisions(decisions []Decision, accountEquity float64, btcEthLeverage, altcoinLeverage int) error { for i, decision := range decisions {