From 83e1b4d0e661f11a407fd6ea86e906b9b87b7180 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 31 七月 2026 17:57:09 +0800
Subject: [PATCH] feat(aftersales): 售后工单新增问题类型和严重程度字段
---
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java | 303 ++++++-------------------------------------------
1 files changed, 40 insertions(+), 263 deletions(-)
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
index 559da13..27ceff0 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -1,64 +1,47 @@
package cn.iocoder.yudao.module.ai.framework.ai.config;
import cn.hutool.core.util.StrUtil;
-import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
-import com.openai.client.okhttp.OpenAIOkHttpClient;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
-import cn.iocoder.yudao.module.ai.tool.method.PersonService;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
+import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.support.ToolCallbacks;
+import org.springframework.ai.openai.OpenAiEmbeddingModel;
+import org.springframework.ai.openai.OpenAiEmbeddingOptions;
+import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
import org.springframework.ai.tokenizer.TokenCountEstimator;
-import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;
import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-
-import java.util.List;
-import java.util.Optional;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* 鑺嬮亾 AI 鑷姩閰嶇疆
*
- * @author fansili
+ * 浣跨敤 OpenAI 鍏煎鎺ュ彛瀵规帴閫氫箟鍗冮棶 DashScope
*/
@Configuration
-@EnableConfigurationProperties({ YudaoAiProperties.class,
- QdrantVectorStoreProperties.class, // 瑙f瀽 Qdrant 閰嶇疆
- RedisVectorStoreProperties.class, // 瑙f瀽 Redis 閰嶇疆
- MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 瑙f瀽 Milvus 閰嶇疆
+@EnableConfigurationProperties({
+ YudaoAiProperties.class,
+ QdrantVectorStoreProperties.class,
+ RedisVectorStoreProperties.class,
+ MilvusVectorStoreProperties.class,
+ MilvusServiceClientProperties.class
})
@Slf4j
public class AiAutoConfiguration {
+
+ private static final String DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
@Bean
public AiModelFactory aiModelFactory() {
@@ -68,252 +51,46 @@
@Bean
@ConditionalOnMissingBean
public ObservationRegistry observationRegistry() {
- // 鐗规畩锛氬厹搴曟湁 ObservationRegistry Bean锛岄伩鍏嶇浉鍏崇殑 ChatModel 鍒涘缓鎶ラ敊銆傜浉鍏� issue锛歨ttps://t.zsxq.com/CuPu4
return ObservationRegistry.NOOP;
}
- // ========== 鍚勭 AI Client 鍒涘缓 ==========
-
@Bean
- @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true")
- public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini();
- return buildGeminiChatClient(properties);
- }
-
- public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(GeminiChatModel.MODEL_DEFAULT);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .openAiClient(OpenAIOkHttpClient.builder()
- .baseUrl(GeminiChatModel.BASE_URL)
- .apiKey(properties.getApiKey())
- .build())
- .options(OpenAiChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new GeminiChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true")
- public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.DouBao properties = yudaoAiProperties.getDoubao();
- return buildDouBaoChatClient(properties);
- }
-
- public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBao properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .openAiClient(OpenAIOkHttpClient.builder()
- .baseUrl(DouBaoChatModel.BASE_URL)
- .apiKey(properties.getApiKey())
- .build())
- .options(OpenAiChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new DouBaoChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true")
- public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.SiliconFlow properties = yudaoAiProperties.getSiliconflow();
- return buildSiliconFlowChatClient(properties);
- }
-
- public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlow properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);
- }
- DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
- .apiKey(properties.getApiKey())
- .build())
- .defaultOptions(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new SiliconFlowChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true")
- public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.HunYuan properties = yudaoAiProperties.getHunyuan();
- return buildHunYuanChatClient(properties);
- }
-
- public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuan properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
- }
- // 鐗规畩锛氱敱浜庢贩鍏冨ぇ妯″瀷涓嶆彁渚� deepseek锛岃�屾槸閫氳繃鐭ヨ瘑寮曟搸锛屾墍浠ラ渶瑕佸尯鍒嗕笅 URL
- if (StrUtil.isEmpty(properties.getBaseUrl())) {
- properties.setBaseUrl(
- StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
- : HunYuanChatModel.BASE_URL);
- }
- // 鍒涘缓 DeepSeekChatModel銆丠unYuanChatModel 瀵硅薄
- DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(properties.getBaseUrl())
- .completionsPath(HunYuanChatModel.COMPLETE_PATH)
- .apiKey(properties.getApiKey())
- .build())
- .defaultOptions(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new HunYuanChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true")
- public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.XingHuo properties = yudaoAiProperties.getXinghuo();
- return buildXingHuoChatClient(properties);
- }
-
- public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuo properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
- }
- OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder()
- .baseUrl(XingHuoChatModel.BASE_URL_V1)
- .apiKey(properties.getAppKey() + ":" + properties.getSecretKey());
- if ("x1".equals(properties.getModel())) {
- builder.baseUrl(XingHuoChatModel.BASE_URL_V2);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .openAiClient(builder.build())
- .options(OpenAiChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- // TODO @鑺嬭壙锛氭槦鐏殑 function call 鏈� bug锛屼細鎶� ToolResponseMessage must have an id 閿欒锛侊紒锛�
- .toolCallingManager(getToolCallingManager())
- .build();
- return new XingHuoChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true")
- public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.BaiChuan properties = yudaoAiProperties.getBaichuan();
- return buildBaiChuanChatClient(properties);
- }
-
- public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuan properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .openAiClient(OpenAIOkHttpClient.builder()
- .baseUrl(BaiChuanChatModel.BASE_URL)
- .apiKey(properties.getApiKey())
- .build())
- .options(OpenAiChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new BaiChuanChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true")
- public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.Midjourney config = yudaoAiProperties.getMidjourney();
- return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true")
- public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) {
- return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
- }
-
- public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(GrokChatModel.MODEL_DEFAULT);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .openAiClient(OpenAIOkHttpClient.builder()
- .baseUrl(Optional.ofNullable(properties.getBaseUrl())
- .orElse(GrokChatModel.BASE_URL))
- .apiKey(properties.getApiKey())
- .build())
- .options(OpenAiChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .toolCallingManager(getToolCallingManager())
- .build();
- return new DouBaoChatModel(openAiChatModel);
- }
-
- // ========== RAG 鐩稿叧 ==========
-
- @Bean
- public TokenCountEstimator tokenCountEstimator() {
- return new JTokkitTokenCountEstimator();
- }
-
- @Bean
+ @ConditionalOnMissingBean
public BatchingStrategy batchingStrategy() {
return new TokenCountBatchingStrategy();
}
- private static ToolCallingManager getToolCallingManager() {
- return SpringUtil.getBean(ToolCallingManager.class);
+ @Bean
+ @ConditionalOnMissingBean
+ public TokenCountEstimator tokenCountEstimator() {
+ return new JTokkitTokenCountEstimator();
}
- // ========== Web Search 鐩稿叧 ==========
+ // ========== 閫氫箟鍗冮棶 Chat锛堥�氳繃 OpenAI 鍏煎鎺ュ彛锛�==========
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.web-search.enable", havingValue = "true")
- public AiWebSearchClient webSearchClient(YudaoAiProperties yudaoAiProperties) {
- return new AiBoChaWebSearchClient(yudaoAiProperties.getWebSearch().getApiKey());
+ public static OpenAiChatModel buildTongYiChatModel(String apiKey, String model) {
+ return OpenAiChatModel.builder()
+ .options(OpenAiChatOptions.builder()
+ .baseUrl(DASHSCOPE_BASE_URL)
+ .apiKey(apiKey)
+ .model(StrUtil.blankToDefault(model, "qwen-plus"))
+ .temperature(0.7)
+ .build())
+ .toolCallingManager(ToolCallingManager.builder().build())
+ .build();
}
- // ========== MCP 鐩稿叧 ==========
+ // ========== 閫氫箟鍗冮棶 Embedding锛堥�氳繃 OpenAI 鍏煎鎺ュ彛锛�==========
- /**
- * 鍙傝�冭嚜 <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html">MCP Server Boot Starter</>
- */
- @Bean
- public List<ToolCallback> toolCallbacks(PersonService personService) {
- return List.of(ToolCallbacks.from(personService));
+ public static OpenAiEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
+ return OpenAiEmbeddingModel.builder()
+ .options(OpenAiEmbeddingOptions.builder()
+ .baseUrl(DASHSCOPE_BASE_URL)
+ .apiKey(apiKey)
+ .model(StrUtil.blankToDefault(model, "text-embedding-v3"))
+ .build())
+ .metadataMode(MetadataMode.EMBED)
+ .build();
}
}
--
Gitblit v1.9.3