mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-04-30 05:38:31 +00:00
DeepSeek大模型切换为新发布deepseek-v4-flash ,流程中调用出现异常 #9585
This commit is contained in:
parent
f9865b0441
commit
b4ced02d3e
@ -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,流程中调用出现异常------------
|
||||
|
||||
/**
|
||||
* 知识库类型:知识库
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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-->
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user