diff --git a/seer-admin/src/main/java/com/seer/teach/admin/SeerAdminApplication.java b/seer-admin/src/main/java/com/seer/teach/admin/SeerAdminApplication.java
index 121946b..367799f 100644
--- a/seer-admin/src/main/java/com/seer/teach/admin/SeerAdminApplication.java
+++ b/seer-admin/src/main/java/com/seer/teach/admin/SeerAdminApplication.java
@@ -15,7 +15,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Slf4j
@EnableFeignClients(basePackages = "com.seer.teach.*.api")
-@SpringBootApplication(scanBasePackages = "com.seer",exclude = QuartzAutoConfiguration.class)
+@SpringBootApplication(scanBasePackages = "com.seer",exclude = {QuartzAutoConfiguration.class})
@EnableTransactionManagement
@EnableAspectJAutoProxy
@EnableAsync
diff --git a/seer-admin/src/main/resources/application.yml b/seer-admin/src/main/resources/application.yml
index d4574a2..56d71aa 100644
--- a/seer-admin/src/main/resources/application.yml
+++ b/seer-admin/src/main/resources/application.yml
@@ -11,13 +11,8 @@ spring:
main:
allow-bean-definition-overriding: true
allow-circular-references: true
- mvc:
-
profiles:
active: dev
- mvc:
- pathmatch:
- matching-strategy: ant_path_matcher
flyway:
enabled: true
locations: classpath:db/mysql
@@ -39,6 +34,7 @@ spring:
server-addr: 192.168.0.39:8848 # 配置中心地址
file-extension: yaml # 配置文件后缀(yaml/properties)
namespace: ${spring.profiles.active}
+
#日志
logging:
config: classpath:logback-${spring.profiles.active}.xml
diff --git a/seer-dependencies/pom.xml b/seer-dependencies/pom.xml
index 9577480..38a7611 100644
--- a/seer-dependencies/pom.xml
+++ b/seer-dependencies/pom.xml
@@ -16,7 +16,7 @@
2025.0.0.0
1.1.2
- 1.1.0.0-M5
+ 1.1.0.0-RC2
@@ -660,6 +660,12 @@
${spring-ai-alibaba.version}
+
+ com.alibaba.cloud.ai
+ spring-ai-alibaba-dashscope
+ ${spring-ai-alibaba.version}
+
+
org.springframework.ai
spring-ai-starter-model-deepseek
@@ -668,8 +674,17 @@
org.springframework.ai
- spring-ai-starter-model-deepseek
+ spring-ai-deepseek
${spring-ai.version}
+ compile
+ true
+
+
+
+ org.springframework.ai
+ spring-ai-openai
+ ${spring-ai.version}
+ compile
diff --git a/seer-teacher/seer-teacher-ai/pom.xml b/seer-teacher/seer-teacher-ai/pom.xml
index 6af3ff8..ac91d20 100644
--- a/seer-teacher/seer-teacher-ai/pom.xml
+++ b/seer-teacher/seer-teacher-ai/pom.xml
@@ -26,17 +26,18 @@
com.alibaba.cloud.ai
- spring-ai-alibaba-starter-dashscope
+ spring-ai-alibaba-dashscope
org.springframework.ai
- spring-ai-starter-model-openai
+ spring-ai-openai
+ compile
org.springframework.ai
- spring-ai-starter-model-deepseek
+ spring-ai-deepseek
diff --git a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AbstractAiModelClient.java b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AbstractAiModelClient.java
index f53bd21..2506171 100644
--- a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AbstractAiModelClient.java
+++ b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AbstractAiModelClient.java
@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.model.tool.DefaultToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
@@ -24,9 +25,11 @@ public abstract class AbstractAiModelClient implements AiModelClient {
protected ResponseErrorHandler responseErrorHandler;
@Autowired(required = false)
+ @Qualifier("aiRestClient")
protected RestClient.Builder restClientBuilder;
@Autowired(required = false)
+ @Qualifier("aiWebClient")
protected WebClient.Builder webClientBuilder;
@Autowired
@@ -35,8 +38,7 @@ public abstract class AbstractAiModelClient implements AiModelClient {
@Autowired
protected ToolCallingManager toolCallingManager;
- @Autowired(required = false)
- protected ObservationRegistry observationRegistry;
+ protected ObservationRegistry observationRegistry = ObservationRegistry.create();
protected final DefaultToolExecutionEligibilityPredicate toolExecutionEligibilityPredicate = new DefaultToolExecutionEligibilityPredicate();
diff --git a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AiModelClient.java b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AiModelClient.java
index 0b1a2f0..59035ad 100644
--- a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AiModelClient.java
+++ b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/AiModelClient.java
@@ -1,6 +1,5 @@
package com.seer.teach.ai.client.model;
-import com.alibaba.cloud.ai.dashscope.audio.synthesis.SpeechSynthesisModel;
import com.seer.teach.ai.client.model.config.ModelConfig;
import org.springframework.ai.audio.tts.TextToSpeechModel;
import org.springframework.ai.chat.model.ChatModel;
@@ -43,17 +42,6 @@ public interface AiModelClient {
return Optional.empty();
}
- /**
- * 获取语音合成模型(Speech Synthesis Model)
- * 用于将文本转换为语音音频,仅dashscope平台支持
- *
- * @param modelConfig 模型配置
- * @return 语音合成模型实例的Optional包装
- */
- default Optional createSpeechSynthesisModel(ModelConfig modelConfig) {
- return Optional.empty();
- }
-
/**
* 获取文本转语音模型(Text To Speech Model)
* 用于将文本转换为语音输出,支持多种平台
diff --git a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/config/DashScopeClientConfig.java b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/config/DashScopeClientConfig.java
new file mode 100644
index 0000000..737fa4b
--- /dev/null
+++ b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/config/DashScopeClientConfig.java
@@ -0,0 +1,99 @@
+package com.seer.teach.ai.client.model.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.codec.json.Jackson2JsonDecoder;
+import org.springframework.http.codec.json.Jackson2JsonEncoder;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * DashScope API 配置类
+ * 用于配置restClient以正确处理聊天完成请求
+ */
+@Slf4j
+@Configuration
+public class DashScopeClientConfig {
+
+ /**
+ * 创建用于DashScope API的RestClient
+ */
+ @Bean
+ public RestClient aiRestClient() {
+ ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
+ .modules(new JavaTimeModule())
+ .build();
+
+ MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
+
+ return RestClient.builder()
+ .requestInterceptor(new LoggingInterceptor())
+ .messageConverters(converters -> converters.add(converter))
+ .build();
+ }
+
+ /**
+ * 创建用于DashScope API的WebClient并启用日志
+ */
+ @Bean
+ public WebClient aiWebClient() {
+ ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
+ .modules(new JavaTimeModule())
+ .build();
+
+ return WebClient.builder()
+ .codecs(configurer -> configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper)))
+ .codecs(configurer -> configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper)))
+ .filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
+ log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
+ clientRequest.headers().forEach((name, values) ->
+ values.forEach(value -> log.info("{}={}", name, value)));
+ return Mono.just(clientRequest);
+ }))
+ .filter(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
+ log.info("Response Status: {}", clientResponse.statusCode());
+ clientResponse.headers().asHttpHeaders().forEach((name, values) ->
+ values.forEach(value -> log.info("{}={}", name, value)));
+ return Mono.just(clientResponse);
+ }))
+ .build();
+ }
+
+
+ public static class LoggingInterceptor implements ClientHttpRequestInterceptor {
+
+ @Override
+ public ClientHttpResponse intercept(
+ HttpRequest request,
+ byte[] body,
+ ClientHttpRequestExecution execution) throws IOException {
+
+ // 记录请求信息
+ log.info("HTTP请求方法: {}", request.getMethod());
+ log.info("HTTP请求URL: {}", request.getURI());
+ log.info("HTTP请求头: {}", request.getHeaders());
+ if (body.length > 0) {
+ log.info("HTTP请求体: {}", new String(body, StandardCharsets.UTF_8));
+ }
+
+ // 执行请求
+ ClientHttpResponse response = execution.execute(request, body);
+
+ return response;
+ }
+ }
+}
\ No newline at end of file
diff --git a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/dashscope/DashScopeModelClient.java b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/dashscope/DashScopeModelClient.java
index 8ec0fce..6e93ce1 100644
--- a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/dashscope/DashScopeModelClient.java
+++ b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/dashscope/DashScopeModelClient.java
@@ -1,14 +1,10 @@
package com.seer.teach.ai.client.model.dashscope;
-import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatProperties;
-import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeConnectionProperties;
-import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingProperties;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.api.DashScopeAudioSpeechApi;
import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
import com.alibaba.cloud.ai.dashscope.audio.DashScopeAudioSpeechModel;
import com.alibaba.cloud.ai.dashscope.audio.DashScopeAudioSpeechOptions;
-import com.alibaba.cloud.ai.dashscope.audio.synthesis.SpeechSynthesisModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
@@ -17,12 +13,11 @@ import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
import com.seer.teach.ai.client.model.AbstractAiModelClient;
import com.seer.teach.ai.client.model.config.ModelConfig;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.audio.tts.TextToSpeechModel;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.image.ImageModel;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@@ -40,14 +35,9 @@ import java.util.Optional;
@Component
public class DashScopeModelClient extends AbstractAiModelClient {
- private DashScopeConnectionProperties connectionProperties;
-
- @Autowired(required = false)
- private DashScopeChatProperties chatProperties;
-
@Override
public String getPlatformName() {
- return "dashscope";
+ return "aliyun_bailian";
}
@Override
@@ -125,17 +115,15 @@ public class DashScopeModelClient extends AbstractAiModelClient {
}
@Override
- public Optional createSpeechSynthesisModel(ModelConfig modelConfig) {
+ public Optional createTextToSpeechModel(ModelConfig modelConfig) {
try {
DashScopeAudioSpeechApi audioSpeechApi = new DashScopeAudioSpeechApi(modelConfig.getApiKey(), modelConfig.getUrl());
DashScopeAudioSpeechOptions options = DashScopeAudioSpeechOptions.builder()
.model(modelConfig.getModel())
- .requestText(DashScopeAudioSpeechApi.RequestTextType.PLAIN_TEXT)
.voice("longyingxiao")
.build();
DashScopeAudioSpeechModel speechSynthesisModel = new DashScopeAudioSpeechModel(audioSpeechApi, options, retryTemplate);
-
return Optional.of(speechSynthesisModel);
} catch (Exception e) {
log.error("创建DashScope语音合成模型失败", e);
@@ -147,10 +135,9 @@ public class DashScopeModelClient extends AbstractAiModelClient {
@Override
public Optional createEmbeddingModel(ModelConfig modelConfig) {
try {
- DashScopeEmbeddingProperties embeddingProperties = new DashScopeEmbeddingProperties();
DashScopeApi dashScopeApi = createDashScopeApi(modelConfig.getApiKey(), modelConfig.getUrl());
- DashScopeEmbeddingModel embeddingModel = new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, embeddingProperties.getOptions(), retryTemplate, observationRegistry);
+ DashScopeEmbeddingModel embeddingModel = new DashScopeEmbeddingModel(dashScopeApi);
return Optional.of(embeddingModel);
} catch (Exception e) {
@@ -162,11 +149,10 @@ public class DashScopeModelClient extends AbstractAiModelClient {
/**
* 创建DashScope API实例
*/
- private DashScopeApi createDashScopeApi(String apiKey, String baseUrl) {
+ private DashScopeApi createDashScopeApi(String apiKey, String completionsPath) {
DashScopeApi.Builder builder = DashScopeApi.builder()
.apiKey(apiKey)
- .baseUrl(baseUrl);
-
+ .baseUrl("https://dashscope.aliyuncs.com");
// 如果有可用的客户端构建器,添加它们
if (webClientBuilder != null) {
builder.webClientBuilder(webClientBuilder);
diff --git a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/openai/OpenAIModelClient.java b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/openai/OpenAIModelClient.java
index 17c9aa7..229f54c 100644
--- a/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/openai/OpenAIModelClient.java
+++ b/seer-teacher/seer-teacher-ai/src/main/java/com/seer/teach/ai/client/model/openai/OpenAIModelClient.java
@@ -6,13 +6,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.audio.tts.TextToSpeechModel;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.image.ImageModel;
import org.springframework.ai.model.SimpleApiKey;
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechProperties;
-import org.springframework.ai.model.openai.autoconfigure.OpenAiChatProperties;
-import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingProperties;
import org.springframework.ai.openai.OpenAiAudioSpeechModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
@@ -131,8 +127,7 @@ public class OpenAIModelClient extends AbstractAiModelClient {
.responseErrorHandler(responseErrorHandler)
.build();
- OpenAiAudioSpeechProperties properties = new OpenAiAudioSpeechProperties();
- TextToSpeechModel textToSpeechModel = new OpenAiAudioSpeechModel(audioApi, properties.getOptions(), retryTemplate);
+ TextToSpeechModel textToSpeechModel = new OpenAiAudioSpeechModel(audioApi);
return Optional.of(textToSpeechModel);
} catch (Exception e) {
log.error("创建OpenAI文本转语音模型失败", e);
@@ -144,8 +139,7 @@ public class OpenAIModelClient extends AbstractAiModelClient {
public Optional createEmbeddingModel(ModelConfig modelConfig) {
try {
OpenAiApi openAiApi = createOpenAiApi(modelConfig.getApiKey(), modelConfig.getUrl());
- OpenAiEmbeddingProperties properties = new OpenAiEmbeddingProperties();
- EmbeddingModel embeddingModel = new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED,properties.getOptions(), retryTemplate);
+ EmbeddingModel embeddingModel = new OpenAiEmbeddingModel(openAiApi);
return Optional.of(embeddingModel);
} catch (Exception e) {
log.error("创建OpenAI嵌入模型失败", e);
@@ -157,8 +151,6 @@ public class OpenAIModelClient extends AbstractAiModelClient {
return OpenAiApi.builder()
.baseUrl(baseUrl)
.apiKey(new SimpleApiKey(apiKey))
- .completionsPath(OpenAiChatProperties.DEFAULT_COMPLETIONS_PATH)
- .embeddingsPath(OpenAiEmbeddingProperties.DEFAULT_EMBEDDINGS_PATH)
.restClientBuilder(restClientBuilder)
.webClientBuilder(webClientBuilder).build();
}
diff --git a/seer-teacher/seer-teacher-service-admin/pom.xml b/seer-teacher/seer-teacher-service-admin/pom.xml
index b8c6c34..7978baf 100644
--- a/seer-teacher/seer-teacher-service-admin/pom.xml
+++ b/seer-teacher/seer-teacher-service-admin/pom.xml
@@ -38,6 +38,12 @@
${project.version}
+
+ ${project.groupId}
+ seer-teacher-ai
+ ${project.version}
+
+
com.squareup.okhttp3
okhttp
diff --git a/seer-teacher/seer-teacher-service-admin/src/main/java/com/seer/teach/admin/service/impl/AbstractGenerationService.java b/seer-teacher/seer-teacher-service-admin/src/main/java/com/seer/teach/admin/service/impl/AbstractGenerationService.java
index a4a3b97..c528da6 100644
--- a/seer-teacher/seer-teacher-service-admin/src/main/java/com/seer/teach/admin/service/impl/AbstractGenerationService.java
+++ b/seer-teacher/seer-teacher-service-admin/src/main/java/com/seer/teach/admin/service/impl/AbstractGenerationService.java
@@ -2,11 +2,14 @@ package com.seer.teach.admin.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.seer.teach.ai.service.AiChatModelService;
import com.seer.teach.common.constants.CommonConstant;
import com.seer.teach.common.entity.BaseEntity;
import com.seer.teach.teacher.service.AiModelCallService;
import com.seer.teach.teacher.service.platform.LlmResponse;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.model.Generation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.support.TransactionTemplate;
@@ -16,6 +19,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.Function;
/**
@@ -41,6 +45,9 @@ public abstract class AbstractGenerationService {
@Autowired
protected TransactionTemplate transactionTemplate;
+ @Autowired
+ protected AiChatModelService chatModelService;
+
/**
* 检查当前线程是否被中断
*
@@ -78,6 +85,10 @@ public abstract class AbstractGenerationService {
*/
protected LlmResponse callLLM(String scenarioCode, String prompt, Map params, Integer userId) {
log.info("调用大模型,场景编码: {}, 提示词长度: {}", scenarioCode, prompt.length());
+ //ChatResponse chatResponse = chatModelService.chatMessage(scenarioCode, prompt);
+ LlmResponse response = new LlmResponse();
+ //Generation result = chatResponse.getResult();
+
return aiModelCallService.callModel(scenarioCode, prompt, params, userId);
}
@@ -160,8 +171,8 @@ public abstract class AbstractGenerationService {
* @param saver 保存器函数
*/
protected Integer executeBatchProcess(R request,
- Function> generator,
- java.util.function.Consumer> saver) {
+ Function> generator,
+ java.util.function.Consumer> saver) {
// 检查任务是否被取消
String taskId = getTaskIdFromRequest(request);
if (isThreadInterrupted() || isTaskCancelled(taskId)) {