diff --git a/decision/engine.go b/decision/engine.go index bab18fb9..3d96bc50 100644 --- a/decision/engine.go +++ b/decision/engine.go @@ -137,6 +137,7 @@ type FullDecision struct { UserPrompt string `json:"user_prompt"` // Input prompt sent to AI CoTTrace string `json:"cot_trace"` // Chain of thought analysis (AI output) Decisions []Decision `json:"decisions"` // Specific decision list + RawResponse string `json:"raw_response"` // Raw AI response (for debugging when parsing fails) Timestamp time.Time `json:"timestamp"` // AIRequestDurationMs records AI API call duration (milliseconds) for troubleshooting latency issues AIRequestDurationMs int64 `json:"ai_request_duration_ms,omitempty"` @@ -212,6 +213,7 @@ func GetFullDecisionWithStrategy(ctx *Context, mcpClient mcp.AIClient, engine *S decision.SystemPrompt = systemPrompt decision.UserPrompt = userPrompt decision.AIRequestDurationMs = aiCallDuration.Milliseconds() + decision.RawResponse = aiResponse // Save raw response for debugging } if err != nil { @@ -350,15 +352,13 @@ func GetFullDecisionWithCustomPrompt(ctx *Context, mcpClient mcp.AIClient, custo decision.SystemPrompt = systemPrompt // Save system prompt decision.UserPrompt = userPrompt // Save input prompt decision.AIRequestDurationMs = aiCallDuration.Milliseconds() + decision.RawResponse = aiResponse // Save raw response for debugging } if err != nil { return decision, fmt.Errorf("failed to parse AI response: %w", err) } - decision.Timestamp = time.Now() - decision.SystemPrompt = systemPrompt // Save system prompt - decision.UserPrompt = userPrompt // Save input prompt return decision, nil } @@ -581,7 +581,8 @@ func buildSystemPrompt(accountEquity float64, btcEthLeverage, altcoinLeverage in sb.WriteString("## Field Descriptions\n\n") sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n") sb.WriteString("- `confidence`: 0-100 (opening recommended ≥75)\n") - sb.WriteString("- Required for opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n\n") + sb.WriteString("- Required for opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n") + sb.WriteString("- **IMPORTANT**: All numeric values must be calculated numbers, NOT formulas/expressions (e.g., use `27.76` not `3000 * 0.01`)\n\n") return sb.String() } diff --git a/decision/strategy_engine.go b/decision/strategy_engine.go index be986eca..eb7f2cef 100644 --- a/decision/strategy_engine.go +++ b/decision/strategy_engine.go @@ -839,7 +839,8 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string sb.WriteString("## Field Description\n\n") sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n") sb.WriteString(fmt.Sprintf("- `confidence`: 0-100 (opening recommended ≥ %d)\n", riskControl.MinConfidence)) - sb.WriteString("- Required when opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n\n") + sb.WriteString("- Required when opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n") + sb.WriteString("- **IMPORTANT**: All numeric values must be calculated numbers, NOT formulas/expressions (e.g., use `27.76` not `3000 * 0.01`)\n\n") // 8. Custom Prompt if e.config.CustomPrompt != "" { diff --git a/store/decision.go b/store/decision.go index 6641f9dd..be62bc79 100644 --- a/store/decision.go +++ b/store/decision.go @@ -22,6 +22,7 @@ type DecisionRecord struct { InputPrompt string `json:"input_prompt"` CoTTrace string `json:"cot_trace"` DecisionJSON string `json:"decision_json"` + RawResponse string `json:"raw_response"` // Raw AI response for debugging CandidateCoins []string `json:"candidate_coins"` ExecutionLog []string `json:"execution_log"` Success bool `json:"success"` @@ -90,6 +91,7 @@ func (s *DecisionStore) initTables() error { input_prompt TEXT DEFAULT '', cot_trace TEXT DEFAULT '', decision_json TEXT DEFAULT '', + raw_response TEXT DEFAULT '', candidate_coins TEXT DEFAULT '', execution_log TEXT DEFAULT '', success BOOLEAN DEFAULT 0, @@ -108,6 +110,9 @@ func (s *DecisionStore) initTables() error { } } + // Migration: add raw_response column if not exists + s.db.Exec(`ALTER TABLE decision_records ADD COLUMN raw_response TEXT DEFAULT ''`) + return nil } @@ -127,13 +132,13 @@ func (s *DecisionStore) LogDecision(record *DecisionRecord) error { result, err := s.db.Exec(` INSERT INTO decision_records ( trader_id, cycle_number, timestamp, system_prompt, input_prompt, - cot_trace, decision_json, candidate_coins, execution_log, + cot_trace, decision_json, raw_response, candidate_coins, execution_log, success, error_message, ai_request_duration_ms - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, record.TraderID, record.CycleNumber, record.Timestamp.Format(time.RFC3339), record.SystemPrompt, record.InputPrompt, record.CoTTrace, record.DecisionJSON, - string(candidateCoinsJSON), string(executionLogJSON), + record.RawResponse, string(candidateCoinsJSON), string(executionLogJSON), record.Success, record.ErrorMessage, record.AIRequestDurationMs, ) if err != nil { diff --git a/trader/auto_trader.go b/trader/auto_trader.go index 087f2bf2..a0cddaf7 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -402,6 +402,7 @@ func (at *AutoTrader) runCycle() error { record.SystemPrompt = aiDecision.SystemPrompt // Save system prompt record.InputPrompt = aiDecision.UserPrompt record.CoTTrace = aiDecision.CoTTrace + record.RawResponse = aiDecision.RawResponse // Save raw AI response for debugging if len(aiDecision.Decisions) > 0 { decisionJSON, _ := json.MarshalIndent(aiDecision.Decisions, "", " ") record.DecisionJSON = string(decisionJSON)