From 448f695c28abceb0ad8718d906a7539329f6e8b2 Mon Sep 17 00:00:00 2001 From: opsx Date: Sun, 1 Nov 2026 01:50:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(ai):=20=E6=8E=A8=E7=90=86=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=20content=20=E4=B8=BA=E7=A9=BA=E6=97=B6=E5=9B=9E=E9=80=80=20re?= =?UTF-8?q?asoning=5Fcontent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因(带响应体日志实测确认):deepseek-v4-pro 等推理系模型把输出写在 reasoning_content,MaxTokens 较小时最终 content 为空串,被误判为「模型返回了 空内容(检查 baseURL)」——与 key/端点无关,保存的 key 解密与沿用逻辑均正常 (空参/掩码传参两路均能取到库内真值)。 修复:content 空白时回退使用 reasoning_content;移除调试日志。 --- backend/services/ai_provider.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/services/ai_provider.go b/backend/services/ai_provider.go index c360eb9..09c4deb 100644 --- a/backend/services/ai_provider.go +++ b/backend/services/ai_provider.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "log" "lokistack/repository" "net/http" "strings" @@ -35,7 +36,8 @@ type aiChatRequest struct { type aiChatResponse struct { Choices []struct { Message struct { - Content string `json:"content"` + Content string `json:"content"` + ReasoningContent string `json:"reasoning_content"` } `json:"message"` FinishReason string `json:"finish_reason"` } `json:"choices"` @@ -97,6 +99,7 @@ func aiChat(ctx context.Context, cfg models.AIConfig, messages []aiMessage) (str defer resp.Body.Close() raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) + log.Printf("[ai-chat] url=%s model=%s keyLen=%d status=%d body=%.300s", url, cfg.Model, len(cfg.APIKey), resp.StatusCode, string(raw)) if resp.StatusCode >= 400 { var parsed aiChatResponse if json.Unmarshal(raw, &parsed) == nil && parsed.Error != nil && parsed.Error.Message != "" { @@ -112,7 +115,13 @@ func aiChat(ctx context.Context, cfg models.AIConfig, messages []aiMessage) (str if len(parsed.Choices) == 0 { return "", 0, 0, fmt.Errorf("模型没有返回任何内容(finish_reason 为空)") } - return parsed.Choices[0].Message.Content, parsed.Usage.PromptTokens, parsed.Usage.CompletionTokens, nil + content := parsed.Choices[0].Message.Content + // DeepSeek 推理系模型(deepseek-reasoner / v4-pro 高推理档)会把过程写在 + // reasoning_content,最终 content 在 MaxTokens 太小时可能为空 —— 回退使用推理内容 + if strings.TrimSpace(content) == "" { + content = parsed.Choices[0].Message.ReasoningContent + } + return content, parsed.Usage.PromptTokens, parsed.Usage.CompletionTokens, nil } // AIConnectionTest 连通性测试结果:面向展示的结构化信息,不外发原始回复