From 814a963335868f0063314be0c53877bd36832fea Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 4 May 2026 20:02:33 +0800 Subject: [PATCH] feat: AI 通义千问支持多模态模型,并兼容从 metadata 解析 reasoningContent --- yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java | 40 ++++++++++++++++++++++++++++++++++------ yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java | 22 +++++++++++----------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java index dab4d5a..1518523 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.ai.util; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; @@ -33,6 +35,28 @@ public class AiUtils { public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER"; public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID"; + /** + * 通义千问支持多模态的模型 + * + * @see 模型广场 + * @see 必须开启 withMultiModel 参数 + */ + public static final Set TONG_YI_MULTI_MODELS = SetUtils.asSet( + // qwen3.5 / 3.6 系列(统一多模态主干) + "qwen3.6-plus", "qwen3.6-flash", + "qwen3.5-plus", "qwen3.5-flash", + // qwen-vl 视觉理解 + "qwen3-vl-plus", "qwen3-vl-flash", + "qwen-vl-max", "qwen-vl-plus", + "qwen2.5-vl-72b-instruct", "qwen2.5-vl-32b-instruct", + "qwen2.5-vl-7b-instruct", "qwen2.5-vl-3b-instruct", + // qvq 视觉推理 + "qvq-max", "qvq-plus", + // qwen-omni 全模态 + "qwen3.5-omni-plus", "qwen3.5-omni-flash", + "qwen3-omni-flash", "qwen-omni-turbo" + ); + public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { return buildChatOptions(platform, model, temperature, maxTokens, null, null); } @@ -44,9 +68,10 @@ public class AiUtils { // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) - .withEnableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置 - .withToolCallbacks(toolCallbacks).withToolContext(toolContext).build(); + return DashScopeChatOptions.builder().model(model).temperature(temperature).maxToken(maxTokens) + .enableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置 + .multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 是否多模态模型 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case DEEP_SEEK: @@ -125,10 +150,13 @@ public class AiUtils { || response.getResult().getOutput() == null) { return null; } - if (response.getResult().getOutput() instanceof DeepSeekAssistantMessage) { - return ((DeepSeekAssistantMessage) (response.getResult().getOutput())).getReasoningContent(); + AssistantMessage output = response.getResult().getOutput(); + // DeepSeek 通过专属 AssistantMessage 暴露 reasoningContent + if (output instanceof DeepSeekAssistantMessage) { + return ((DeepSeekAssistantMessage) output).getReasoningContent(); } - return null; + // 通义千问等通过 metadata 透传 reasoningContent + return MapUtil.getStr(output.getMetadata(), "reasoningContent"); } } \ No newline at end of file diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java index 23bd5d9..056709d 100644 --- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java +++ b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java @@ -34,15 +34,15 @@ public class TongYiChatModelTests { private final DashScopeChatModel chatModel = DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() - .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") + .apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key 获取密钥 .build()) .defaultOptions(DashScopeChatOptions.builder() -// .withModel("qwen1.5-72b-chat") // 模型 - .withModel("qwen3-235b-a22b-thinking-2507") // 模型 -// .withModel("deepseek-r1") // 模型(deepseek-r1) -// .withModel("deepseek-v3") // 模型(deepseek-v3) -// .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) -// .withEnableThinking(true) + .multiModel(true) // 注意:当使用 qwen3.6-plus 等多模态模型,需要设置为 true,可见 https://help.aliyun.com/zh/model-studio/error-code#error-url 链接 + .model("qwen3.6-plus") // 模型 +// .model("deepseek-r1") // 模型(deepseek-r1) +// .model("deepseek-v3") // 模型(deepseek-v3) +// .model("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) +// .enableThinking(true) .build()) .build(); @@ -85,9 +85,9 @@ public class TongYiChatModelTests { List messages = new ArrayList<>(); messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); DashScopeChatOptions options = DashScopeChatOptions.builder() - .withModel("qwen3-235b-a22b-thinking-2507") + .model("qwen3.6-plus").multiModel(true) // .withModel("qwen-max-2025-01-25") - .withEnableThinking(true) // 必须设置,否则会报错 + .enableThinking(true) // 必须设置,否则会报错 .build(); // 调用 @@ -112,8 +112,8 @@ public class TongYiChatModelTests { Document document01 = new Document("abc"); Document document02 = new Document("sapring"); RerankOptions options = DashScopeRerankOptions.builder() - .withTopN(1) - .withModel("gte-rerank-v2") + .topN(1) + .model("gte-rerank-v2") .build(); RerankRequest rerankRequest = new RerankRequest( query, -- libgit2 0.21.4