package cn.iocoder.yudao.module.ai.framework.ai.config; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory; import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl; import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; 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.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.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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * 芋道 AI 自动配置 * * 使用 OpenAI 兼容接口对接通义千问 DashScope */ @Configuration @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() { return new AiModelFactoryImpl(); } @Bean @ConditionalOnMissingBean public ObservationRegistry observationRegistry() { return ObservationRegistry.NOOP; } @Bean @ConditionalOnMissingBean public BatchingStrategy batchingStrategy() { return new TokenCountBatchingStrategy(); } @Bean @ConditionalOnMissingBean public TokenCountEstimator tokenCountEstimator() { return new JTokkitTokenCountEstimator(); } // ========== 通义千问 Chat(通过 OpenAI 兼容接口)========== 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(); } // ========== 通义千问 Embedding(通过 OpenAI 兼容接口)========== 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(); } }