Commit 814a963335868f0063314be0c53877bd36832fea
1 parent
ef807be8
feat: AI 通义千问支持多模态模型,并兼容从 metadata 解析 reasoningContent
Showing
2 changed files
with
45 additions
and
17 deletions
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.util; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.map.MapUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjUtil; |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | |
| 5 | 7 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 6 | 8 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 7 | 9 | import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; |
| ... | ... | @@ -33,6 +35,28 @@ public class AiUtils { |
| 33 | 35 | public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER"; |
| 34 | 36 | public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID"; |
| 35 | 37 | |
| 38 | + /** | |
| 39 | + * 通义千问支持多模态的模型 | |
| 40 | + * | |
| 41 | + * @see <a href="https://bailian.console.aliyun.com/cn-beijing/?tab=model#/model-market/all?providers=qwen&capabilities=VU">模型广场</a> | |
| 42 | + * @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">必须开启 withMultiModel 参数</a> | |
| 43 | + */ | |
| 44 | + public static final Set<String> TONG_YI_MULTI_MODELS = SetUtils.asSet( | |
| 45 | + // qwen3.5 / 3.6 系列(统一多模态主干) | |
| 46 | + "qwen3.6-plus", "qwen3.6-flash", | |
| 47 | + "qwen3.5-plus", "qwen3.5-flash", | |
| 48 | + // qwen-vl 视觉理解 | |
| 49 | + "qwen3-vl-plus", "qwen3-vl-flash", | |
| 50 | + "qwen-vl-max", "qwen-vl-plus", | |
| 51 | + "qwen2.5-vl-72b-instruct", "qwen2.5-vl-32b-instruct", | |
| 52 | + "qwen2.5-vl-7b-instruct", "qwen2.5-vl-3b-instruct", | |
| 53 | + // qvq 视觉推理 | |
| 54 | + "qvq-max", "qvq-plus", | |
| 55 | + // qwen-omni 全模态 | |
| 56 | + "qwen3.5-omni-plus", "qwen3.5-omni-flash", | |
| 57 | + "qwen3-omni-flash", "qwen-omni-turbo" | |
| 58 | + ); | |
| 59 | + | |
| 36 | 60 | public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { |
| 37 | 61 | return buildChatOptions(platform, model, temperature, maxTokens, null, null); |
| 38 | 62 | } |
| ... | ... | @@ -44,9 +68,10 @@ public class AiUtils { |
| 44 | 68 | // noinspection EnhancedSwitchMigration |
| 45 | 69 | switch (platform) { |
| 46 | 70 | case TONG_YI: |
| 47 | - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) | |
| 48 | - .withEnableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置 | |
| 49 | - .withToolCallbacks(toolCallbacks).withToolContext(toolContext).build(); | |
| 71 | + return DashScopeChatOptions.builder().model(model).temperature(temperature).maxToken(maxTokens) | |
| 72 | + .enableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置 | |
| 73 | + .multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 是否多模态模型 | |
| 74 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 50 | 75 | case YI_YAN: |
| 51 | 76 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); |
| 52 | 77 | case DEEP_SEEK: |
| ... | ... | @@ -125,10 +150,13 @@ public class AiUtils { |
| 125 | 150 | || response.getResult().getOutput() == null) { |
| 126 | 151 | return null; |
| 127 | 152 | } |
| 128 | - if (response.getResult().getOutput() instanceof DeepSeekAssistantMessage) { | |
| 129 | - return ((DeepSeekAssistantMessage) (response.getResult().getOutput())).getReasoningContent(); | |
| 153 | + AssistantMessage output = response.getResult().getOutput(); | |
| 154 | + // DeepSeek 通过专属 AssistantMessage 暴露 reasoningContent | |
| 155 | + if (output instanceof DeepSeekAssistantMessage) { | |
| 156 | + return ((DeepSeekAssistantMessage) output).getReasoningContent(); | |
| 130 | 157 | } |
| 131 | - return null; | |
| 158 | + // 通义千问等通过 metadata 透传 reasoningContent | |
| 159 | + return MapUtil.getStr(output.getMetadata(), "reasoningContent"); | |
| 132 | 160 | } |
| 133 | 161 | |
| 134 | 162 | } |
| 135 | 163 | \ No newline at end of file | ... | ... |
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 { |
| 34 | 34 | |
| 35 | 35 | private final DashScopeChatModel chatModel = DashScopeChatModel.builder() |
| 36 | 36 | .dashScopeApi(DashScopeApi.builder() |
| 37 | - .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") | |
| 37 | + .apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key 获取密钥 | |
| 38 | 38 | .build()) |
| 39 | 39 | .defaultOptions(DashScopeChatOptions.builder() |
| 40 | -// .withModel("qwen1.5-72b-chat") // 模型 | |
| 41 | - .withModel("qwen3-235b-a22b-thinking-2507") // 模型 | |
| 42 | -// .withModel("deepseek-r1") // 模型(deepseek-r1) | |
| 43 | -// .withModel("deepseek-v3") // 模型(deepseek-v3) | |
| 44 | -// .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) | |
| 45 | -// .withEnableThinking(true) | |
| 40 | + .multiModel(true) // 注意:当使用 qwen3.6-plus 等多模态模型,需要设置为 true,可见 https://help.aliyun.com/zh/model-studio/error-code#error-url 链接 | |
| 41 | + .model("qwen3.6-plus") // 模型 | |
| 42 | +// .model("deepseek-r1") // 模型(deepseek-r1) | |
| 43 | +// .model("deepseek-v3") // 模型(deepseek-v3) | |
| 44 | +// .model("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) | |
| 45 | +// .enableThinking(true) | |
| 46 | 46 | .build()) |
| 47 | 47 | .build(); |
| 48 | 48 | |
| ... | ... | @@ -85,9 +85,9 @@ public class TongYiChatModelTests { |
| 85 | 85 | List<Message> messages = new ArrayList<>(); |
| 86 | 86 | messages.add(new UserMessage("详细分析下,如何设计一个电商系统?")); |
| 87 | 87 | DashScopeChatOptions options = DashScopeChatOptions.builder() |
| 88 | - .withModel("qwen3-235b-a22b-thinking-2507") | |
| 88 | + .model("qwen3.6-plus").multiModel(true) | |
| 89 | 89 | // .withModel("qwen-max-2025-01-25") |
| 90 | - .withEnableThinking(true) // 必须设置,否则会报错 | |
| 90 | + .enableThinking(true) // 必须设置,否则会报错 | |
| 91 | 91 | .build(); |
| 92 | 92 | |
| 93 | 93 | // 调用 |
| ... | ... | @@ -112,8 +112,8 @@ public class TongYiChatModelTests { |
| 112 | 112 | Document document01 = new Document("abc"); |
| 113 | 113 | Document document02 = new Document("sapring"); |
| 114 | 114 | RerankOptions options = DashScopeRerankOptions.builder() |
| 115 | - .withTopN(1) | |
| 116 | - .withModel("gte-rerank-v2") | |
| 115 | + .topN(1) | |
| 116 | + .model("gte-rerank-v2") | |
| 117 | 117 | .build(); |
| 118 | 118 | RerankRequest rerankRequest = new RerankRequest( |
| 119 | 119 | query, | ... | ... |