DeepSeek大模型切换为新发布deepseek-v4-flash ,流程中调用出现异常 #9585

This commit is contained in:
JEECG 2026-04-29 23:24:19 +08:00
parent f9865b0441
commit b4ced02d3e
4 changed files with 123 additions and 5 deletions

View File

@ -98,6 +98,42 @@ public class LLMConsts {
*/
public static final String DEEPSEEK_REASONER = "deepseek-reasoner";
//update-begin---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
/**
* DEEPSEEK 推理模型(返回 reasoning_content 字段在多轮工具调用中要求把 reasoning_content 回传)集合
* 后续 DeepSeek 新增推理模型时在此追加非推理模型( deepseek-chat)不要加入
* 触发场景仅当对话存在工具调用导致的多轮请求时才会出现 "reasoning_content must be passed back" 错误
* 单轮 Q&A( AI 应用聊天无工具)不会触发但开启 sendThinking 也无副作用
*/
public static final Set<String> DEEPSEEK_THINKING_MODELS = new HashSet<>(Arrays.asList(
"deepseek-reasoner",
"deepseek-v4-flash",
"deepseek-v4-pro"
));
/**
* 判断指定模型名是否为 DeepSeek 推理模型(返回 reasoning_content 字段)
* 匹配规则先做大小写不敏感的精确匹配再做关键字包含匹配(reasoner/v4-flash/v4-pro)
* 以兼容带版本后缀的变体( deepseek-v4-flash-0428)
*
* @param modelName 模型名(大小写不敏感首尾空白容错)
* @return true=推理模型false=非推理模型或空
*/
public static boolean isDeepSeekThinkingModel(String modelName) {
if (modelName == null || modelName.trim().isEmpty()) {
return false;
}
String name = modelName.trim().toLowerCase();
if (DEEPSEEK_THINKING_MODELS.contains(name)) {
return true;
}
// 兼容带版本后缀或厂商前缀的变体
return name.contains("reasoner")
|| name.contains("v4-flash")
|| name.contains("v4-pro");
}
//update-end---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
/**
* 知识库类型知识库
*/

View File

@ -16,6 +16,7 @@ import org.jeecg.common.util.AssertUtils;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.AiChatConfig;
import org.jeecg.config.AiRagConfigBean;
import org.jeecg.modules.airag.common.consts.AiragConsts;
import org.jeecg.modules.airag.common.handler.AIChatParams;
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
@ -25,7 +26,6 @@ import org.jeecg.modules.airag.llm.entity.AiragMcp;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.jeecg.modules.airag.llm.mapper.AiragMcpMapper;
import org.jeecg.modules.airag.llm.mapper.AiragModelMapper;
import org.jeecg.config.AiRagConfigBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -119,6 +119,9 @@ public class AIChatHandler implements IAIChatHandler {
*/
public String completions(AiragModel airagModel, List<ChatMessage> messages, AIChatParams params) {
params = mergeParams(airagModel, params);
//update-begin---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
messages = injectThinkingPlaceholderIfNeeded(messages, airagModel.getModelName());
//update-end---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
String resp = null;
try {
resp = llmHandler.completions(messages, params);
@ -205,9 +208,64 @@ public class AIChatHandler implements IAIChatHandler {
*/
private TokenStream chat(AiragModel airagModel, List<ChatMessage> messages, AIChatParams params) {
params = mergeParams(airagModel, params);
//update-begin---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
messages = injectThinkingPlaceholderIfNeeded(messages, airagModel.getModelName());
//update-end---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
return llmHandler.chat(messages, params);
}
//update-begin---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
/**
* 当目标模型是 DeepSeek 推理模型(deepseek-v4-flash )
* 为历史 AI 消息( MessageHistory 重建出来的thinking 字段为空的 AiMessage)注入占位 thinking
*
* 原因DeepSeek 推理模型校验请求时要求每条 assistant 历史消息携带 reasoning_content 字段
* langchain4j sendThinking 仅在 AiMessage.thinking() 非空时才会注入 reasoning_content
* 而历史持久化层(MessageHistory)目前没有保存 reasoning_content重建出的 AiMessage thinking 始终为 null
* 导致 DeepSeek 返回 "The reasoning_content in the thinking mode must be passed back to the API."
*
* 临时方案注入占位字符串让 langchain4j 通过 isNullOrEmpty 校验 reasoning_content 字段带上
* 后续若 MessageHistory 升级支持持久化 reasoning_content 可去掉该兜底
*
* @param messages 原始消息列表(可能为 null/)
* @param modelName 目标模型名(用于判定是否需要注入)
* @return 处理后的新消息列表(若无需处理则原样返回)
* @author scott
* @date 2026-04-29
*/
private static List<ChatMessage> injectThinkingPlaceholderIfNeeded(List<ChatMessage> messages, String modelName) {
if (messages == null || messages.isEmpty()
|| !LLMConsts.isDeepSeekThinkingModel(modelName)) {
return messages;
}
List<ChatMessage> result = new ArrayList<>(messages.size());
int injected = 0;
for (ChatMessage msg : messages) {
if (msg instanceof AiMessage) {
AiMessage aiMsg = (AiMessage) msg;
if (oConvertUtils.isEmpty(aiMsg.thinking())) {
AiMessage rebuilt = AiMessage.builder()
.text(aiMsg.text())
// 占位 langchain4j reasoning_content 字段带上以满足 DeepSeek 校验
.thinking("...")
.toolExecutionRequests(aiMsg.toolExecutionRequests())
.attributes(aiMsg.attributes())
.build();
result.add(rebuilt);
injected++;
continue;
}
}
result.add(msg);
}
if (injected > 0) {
log.info("[AI-CHAT][issues/9585] 为 DeepSeek 推理模型[{}]的 {} 条历史 AI 消息注入了占位 thinking",
modelName, injected);
}
return result;
}
//update-end---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
/**
* 使用默认模型聊天
*
@ -306,6 +364,28 @@ public class AIChatHandler implements IAIChatHandler {
buildPlugins(params);
}
//update-begin---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
// 仅对 DeepSeek 推理模型(deepseek-reasoner/deepseek-v4-flash )开启思考过程的捕获与回传
// 否则 deepseek-v4-flash 在工具调用多轮对话中会返回:
// "The reasoning_content in the thinking mode must be passed back to the API."
// 注意不要对 deepseek-chat 等非推理模型开启避免无意义的请求字段污染
boolean isDsThinking = LLMConsts.isDeepSeekThinkingModel(modelName);
log.info("[AI-CHAT][issues/9585] mergeParams provider={}, modelName={}, isDeepSeekThinkingModel={}, params.returnThinking={}, params.sendThinking={}",
airagModel.getProvider(), modelName, isDsThinking, params.getReturnThinking(), params.getSendThinking());
if (isDsThinking) {
// returnThinking: 把响应中的 reasoning_content 解析到 AiMessage.thinking
if (null == params.getReturnThinking()) {
params.setReturnThinking(true);
}
// sendThinking: AiMessage.thinking reasoning_content 字段回传到下次请求
if (null == params.getSendThinking()) {
params.setSendThinking(true);
}
log.info("[AI-CHAT][issues/9585] mergeParams after-fix returnThinking={}, sendThinking={}",
params.getReturnThinking(), params.getSendThinking());
}
//update-end---author:scott ---date:20260429 for[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash流程中调用出现异常------------
return params;
}

View File

@ -546,7 +546,7 @@
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>jeecg-boot-starter-ai</artifactId>
<version>3.9.2</version>
<version>3.9.2.1</version>
</dependency>
<!--flyway 支持 mysql5.7+、MariaDB10.3.16-->
<!--mysql5.6需要把版本号改成5.2.1-->

View File

@ -21,12 +21,14 @@
"title": "DeepSeek",
"value": "DEEPSEEK",
"LLM": [
{"label": "deepseek-reasoner", "value": "deepseek-reasoner","descr": "【官方模型】深度求索 新推出的推理模型R1满血版\n火便全球。\n支持64k上下文其中支持8k最大回复。","type": "text"},
{"label":"deepseek-chat", "value": "deepseek-chat","descr": "最强开源 MoE 模型 DeepSeek-V3全球首个在代码、数学能力上与GPT-4-Turbo争锋的模型在代码、数学的多个榜单上位居全球第二","type": "text"}
{"label": "deepseek-v4-pro", "value": "deepseek-v4-pro","descr": "【官方模型】深度求索 新推出的推理模型R1满血版\n火便全球。\n支持64k上下文其中支持8k最大回复。","type": "text"},
{"label": "deepseek-v4-flash", "value": "deepseek-v4-flash","descr": "【官方模型】深度求索 新推出的推理模型R1满血版\n火便全球。\n支持64k上下文其中支持8k最大回复。","type": "text"},
{"label": "deepseek-reasoner", "value": "deepseek-reasoner","descr": " 2026/07/24下线【官方模型】深度求索 新推出的推理模型R1满血版\n火便全球。\n支持64k上下文其中支持8k最大回复。","type": "text"},
{"label":"deepseek-chat", "value": "deepseek-chat","descr": "2026/07/24下线最强开源 MoE 模型 DeepSeek-V3全球首个在代码、数学能力上与GPT-4-Turbo争锋的模型在代码、数学的多个榜单上位居全球第二","type": "text"}
],
"type": ["LLM"],
"baseUrl": "https://api.deepseek.com/v1",
"LLMDefaultValue": "deepseek-chat"
"LLMDefaultValue": "deepseek-v4-pro"
},
{
"title": "Ollama",