2、SpringAI接入ChatGPT与微服务整合
2、SpringAI接入ChatGPT与微服务整合
小薛博客AI 大模型资料
1、SpringAI简介
https://spring.io/projects/spring-ai

Spring AI是一个人工智能工程的应用框架。其目标是将Spring生态系统的设计原则(如可移植性和模块化设计)应用于人工智能领域,并推动将POJO作为应用程序的构建块应用于AI领域。
SpringAI 是一个基于人工智能技术的工具或平台,通常用于构建、优化和部署AI模型。
-
简化AI开发:提供易用的接口和框架,加速AI模型的创建与训练。
-
可移植的API支持跨人工智能提供商的聊天,文本到图像,和嵌入模型。
2、环境要求
<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>17</java.version><spring-ai.version>1.0.0-M7</spring-ai.version>
</properties><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
Java 17 或更高版本
- Spring AI 和 Spring Boot 3.x 需要 Java 17 或更高版本。确保您的开发环境已安装并配置了正确的 Java 版本。
spring-ai-starter-model-openai是为 Spring Boot 3.x 设计的,因此需要确保您的项目基于 Spring Boot 3.x。
3、新建工程 xx-ai
一并整合
- spring-ai
- mybatis plus
- knife4j swagger3
1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.4</version><relativePath/></parent><artifactId>xx-ai</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>17</java.version><spring-ai.version>1.0.0-M7</spring-ai.version><mysql.version>8.0.27</mysql.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><dependency><groupId>com.xx</groupId><artifactId>xx-common-core</artifactId><version>1.7.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- mybatis-plus-spring-boot3-starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.7</version></dependency><!--SpringBoot集成druid连接池druid-spring-boot-starter --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.23</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>
2、application.yml
server:port: 8001spring:application:name: xx-aiai:openai:api-key: ${OPENAI_KEY} # OpenAI API 密钥base-url: https://gitaigc.com # OpenAI 基础 URL# 以下是可选的配置,已经被注释掉# spring.ai.openai.base-url: https://api.openai.com# spring.ai.openai.api-key:# 生成文本(text-to-text)模型配置# spring.ai.openai.chat.options.model: gpt-3.5-turbo# spring.ai.openai.chat.options.temperature: 0.4# 生成图像(text-to-image)模型配置#spring.ai.openai.image.options.model: gpt-4-dalle# ========================Redis 配置=====================
---
spring:data:redis:host: 127.0.0.1 # Redis 主机password: 123456 # Redis 密码port: 6379 # Redis 端口timeout: 1s # Redis 连接超时# ========================SQL 初始化配置===================
---
spring:sql:init:mode: always # 总是初始化数据库schema-locations: classpath:db/init.sql # 初始化SQL文件位置# ========================数据库配置(Druid + MySQL 8)=======================
---
spring:datasource:type: com.alibaba.druid.pool.DruidDataSource # 数据源类型driver-class-name: com.mysql.cj.jdbc.Driver # MySQL JDBC 驱动类名url: jdbc:mysql://127.0.0.1:3306/aicloud?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true # 数据库连接 URLusername: root # 数据库用户名password: mac_root # 数据库密码druid:test-while-idle: false # 配置 Druid 数据源的空闲连接检测# ========================MyBatis-Plus 配置===================
mybatis-plus:configuration:map-underscore-to-camel-case: true # 将数据库表字段的下划线转为驼峰命名法mapper-locations: classpath:mapper/*.xml # MyBatis 映射器文件的位置type-aliases-package: com.xx.entities # MyBatis 类型别名包路径# ========================日志配置=======================
logging:level:com.xx: info # 设置特定包的日志级别# ========================JSON 日期格式配置===================
---
spring:jackson:date-format: yyyy-MM-dd HH:mm:ss # JSON 日期格式time-zone: GMT+8 # 设置时区为 GMT+8
API Key
# mac 版 open ~/.zshrcexport OPENAI_KEY="换成自己的"source ~/.zshrc# windows 自己配置环境变量即可
3、启动类
package com.xx;import com.xx.utils.LocalIpUtil;
import com.xx.utils.ValidationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;/*** @Author: xueqimiao* @Date: 2025/4/15 10:34*/
@SpringBootApplication
@Slf4j
public class AiApplication {public static void main(String[] args) {ConfigurableApplicationContext application = SpringApplication.run(AiApplication.class, args);Environment env = application.getEnvironment();String ip = LocalIpUtil.getLocalIp();String port = env.getProperty("server.port");String path = ValidationUtil.isEmpty(env.getProperty("server.servlet.context-path")) ? "" : env.getProperty("server.servlet.context-path");log.info("\n----------------------------------------------------------\n" +"Welcome to the 小薛博客 AI大模型项目 Interface documentation:\n\t" +"Local: \t\thttp://127.0.0.1:" + port + path + "/doc.html\n" +"Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" +"进入首页: \thttp://" + ip + ":" + port + path + "\n" +"----------------------------------------------------------");}}
4、日志文件
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!--定义日志文件的存储地址 --><property name="LOG_HOME" value="../logs/xx-ai" /><!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />--><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [traceId:%X{traceId}] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!--日志文件输出的文件名 --><FileNamePattern>${LOG_HOME}/xx-%d{yyyy-MM-dd}.%i.log</FileNamePattern><!--日志文件保留天数 --><MaxHistory>10</MaxHistory><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [traceId:%X{traceId}] %-5level %logger{50}:%L - %msg%n</pattern></encoder></appender><!-- 生成 error html格式日志开始 --><appender name="HTML" class="ch.qos.logback.core.FileAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!--设置日志级别,过滤掉info日志,只输入error日志--><level>ERROR</level></filter><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.html.HTMLLayout"><pattern>%p%d%msg%M%F{32}%L</pattern></layout></encoder><file>${LOG_HOME}/error-log.html</file></appender><!-- 生成 error html格式日志结束 --><!-- 每天生成一个html格式的日志开始 --><!--<appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!–日志文件输出的文件名 –><FileNamePattern>${LOG_HOME}/xx-%d{yyyy-MM-dd}.%i.html</FileNamePattern><!–日志文件保留天数 –><MaxHistory>10</MaxHistory><MaxFileSize>10MB</MaxFileSize></rollingPolicy><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.html.HTMLLayout"><pattern>%p%d%msg%M%F{32}%L</pattern></layout></encoder></appender>--><!-- 每天生成一个html格式的日志结束 --><!--myibatis log configure --><logger name="com.apache.ibatis" level="TRACE" /><logger name="java.sql.Connection" level="DEBUG" /><logger name="java.sql.Statement" level="DEBUG" /><logger name="java.sql.PreparedStatement" level="DEBUG" /><!-- 日志输出级别 --><root level="INFO"><appender-ref ref="STDOUT" /><appender-ref ref="FILE" /><appender-ref ref="HTML" /><!--<appender-ref ref="FILE_HTML" />--></root></configuration>
5、HelloOpenAIController
package com.xx.controller;import com.xx.common.Result;
import jakarta.annotation.Resource;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @Author: xueqimiao* @Date: 2025/4/15 10:35*/
@RestController
public class HelloOpenAIController {@Resourceprivate OpenAiChatModel chatClient;@GetMapping("/txtToTxt")public Result txtToTxt(@RequestParam String keyword) {StopWatch stopWatch = new StopWatch();stopWatch.start();String retValue = chatClient.call(keyword);System.out.println(retValue);stopWatch.stop();System.out.println("耗时: " + stopWatch.getTotalTimeSeconds() + " 毫秒");return Result.ok(retValue);}}
6、不记录日志注解
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
package com.xx.annotations;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @auther xx* @create 2024-04-20 21:57*/
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogParam {// 我可以用在参数列表或者方法上,屏蔽不愿意记录的参数
}
package com.xx.aspect;import cn.hutool.json.JSONUtil;
import com.xx.annotations.NoLogParam;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;/*** @auther xx* @create 2024-04-20 21:59*/
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Aspect
@Component
public class NoLogAspect {private Logger log = LoggerFactory.getLogger(this.getClass());@Around("execution(* com.xx..*Controller.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object result = null;MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();try {//打印处理当前请求的完整类名和方法名称log.info("接口方法:{}.{}", methodSignature.getDeclaringTypeName(), methodSignature.getName());//获取所有要打印的参数,丢到map中,key为参数名称,value为参数的值,然后会将这个map以json格式输出Map<String, Object> logParamsMap = new LinkedHashMap<>();String[] parameterNames = methodSignature.getParameterNames();Object[] args = joinPoint.getArgs();for (int i = 0; i < args.length; i++) {if (parameterIsLog(methodSignature, i)) {//参数名称String parameterName = parameterNames[i];//参数值Object parameterValue = args[i];//将其放入到map中,稍后会以json格式输出logParamsMap.put(parameterName, parameterValue);}}log.info("方法参数列表:{}", JSONUtil.toJsonStr(logParamsMap));result = joinPoint.proceed();//程序放行......return result;} finally {//判断方法的返回值是否需要打印?方法上有 @NoLogAnnotation 注解的,表示结果不打印方法返回值if (this.resultIsLog(methodSignature)) {log.info("方法返回值:{}", JSONUtil.toJsonStr(result));}}}/*** 指定位置的参数是否需要打印出来?** @param methodSignature* @param paramIndex* @return*/private boolean parameterIsLog(MethodSignature methodSignature, int paramIndex) {if (methodSignature.getMethod().getParameterCount() == 0) {return false;}// 参数上有 @NoLogAnnotation注解的不会打印Annotation[] parameterAnnotation = methodSignature.getMethod().getParameterAnnotations()[paramIndex];if (parameterAnnotation != null && parameterAnnotation.length > 0) {for (Annotation annotation : parameterAnnotation) {if (annotation.annotationType() == NoLogParam.class) {return false;}}}// 参数类型是下面这些类型的,也不会打印,比如:ServletRequest、ServletResponseClass parameterType = methodSignature.getParameterTypes()[paramIndex];for (Class<?> type : noLogTypes) {if (type.isAssignableFrom(parameterType)) {return false;}}return true;}// 参数类型是下面这些类型的,也不会打印,比如:ServletRequest、ServletResponse,大家可以扩展private static List<Class<?>> noLogTypes = Arrays.asList(ServletRequest.class, ServletResponse.class);/*** 判断方法的返回值是否需要打印?方法上有 @NoLogAnnotation 注解的,表示结果不打印方法返回值** @param methodSignature* @return*/private boolean resultIsLog(MethodSignature methodSignature) {return methodSignature.getMethod().getAnnotation(NoLogParam.class) == null;}
}
7、RedisConfig
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
package com.xx.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** @auther xx* @create 2024-03-13 11:51*/
@Configuration
@Slf4j
public class RedisConfig {/*** redis序列化的工具配置类,下面这个请一定开启配置* 127.0.0.1:6379> keys ** 1) "ord:102" 序列化过* 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过* this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法* this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法* this.redisTemplate.opsForSet(); //提供了操作set的所有方法* this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法* this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法** @param redisConnectionFactor* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactor);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}
}
8、MybatisPlusConfig
package com.xx.config;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;@Configuration
@MapperScan(value = {"com.xx.mapper"})
public class MybatisPlusConfig {}
9、Trace日志跟踪
详情见架构设计之请求链路日志追踪快速定位程序Bug设计
package com.xx.trace;import org.slf4j.MDC;/*** @auther xx* @create 2024-04-18 19:32* <p>* org.slf4j.MDC;* 是 SLF4J(Simple Logging Facade for Java)库中的一个组件,它代表 Mapped Diagnostic Context。MDC* 提供了一种机制,允许你在日志消息中插入上下文信息,这些信息可以跨多个方法调用和线程边界传播。* 这对于跟踪和调试分布式系统或多线程应用程序中的请求非常有用。* <p>* MDC 允许你将键值对与当前线程关联起来。然后,你可以在你的日志语句中引用这些值,* 从而能够更容易地识别和理解日志消息产生的上下文。* <p>* 例如,你可能会在 Web 应用程序的每个请求开始时,将用户的 ID 或会话 ID 放入 MDC,* 然后在你的日志语句中引用这个值。这样,当你查看日志时,你可以很容易地看到哪个用户的哪个请求产生了哪些日志消息。* <p>* 使用 MDC 的基本步骤如下:* 设置值: MDC.put("userId", "12345");* 在日志语句中使用值: logger.info("Processing request for user: {}", MDC.get("userId"));* 清除值: MDC.remove("userId");*/
public class TraceUtils {public static final String TRACE_ID = "traceId";public static ThreadLocal<String> traceIdThreadLocal = new ThreadLocal<>();public static void setTraceId(String traceId) {traceIdThreadLocal.set(traceId);MDC.put(TRACE_ID, traceId);}public static String getTraceId() {return traceIdThreadLocal.get();}public static void removeTraceId() {traceIdThreadLocal.remove();MDC.remove(TRACE_ID);}
}
package com.xx.trace;import cn.hutool.core.util.IdUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;/*** @auther xx* @create 2024-04-18 19:36*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter(urlPatterns = "/**", filterName = "TraceFilter")
public class TraceFilter extends OncePerRequestFilter {private Logger log = LoggerFactory.getLogger(this.getClass());@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String traceID = IdUtil.fastSimpleUUID();TraceUtils.setTraceId(traceID); //ThreadLocal.set(traceID);long startTime = System.currentTimeMillis();try {filterChain.doFilter(request, response);} finally {TraceUtils.removeTraceId();}long endTime = System.currentTimeMillis();log.info("请求地址:{},耗时(毫秒):{}", request.getRequestURL().toString(), (endTime - startTime));}
}
package com.xx.trace;import com.xx.aspect.ResultTraceIdAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @auther xx* @create 2024-04-18 20:03* @Configuration注解的proxyBeanMethods属性的作用 注解的意思是proxyBeanMethods配置类是用来指定@Bean注解标注的方法是否使用代理,* 2.1 默认是true使用代理,直接从IOC容器之中取得对象;* <p>* 2.2 如果设置为false,也就是不使用注解,每次调用@Bean标注的方法获取到的对象和IOC容器中的都不一样,* 是一个新的对象,所以我们可以将此属性设置为false来提高性能。*/
@Configuration(proxyBeanMethods = false)
public class TraceConfiguration {@Beanpublic TraceFilter traceFilter() {return new TraceFilter();}@Beanpublic ResultTraceIdAspect fillRequestIdAspect() {return new ResultTraceIdAspect();}
}
package com.xx.aspect;import com.xx.common.Result;
import com.xx.trace.TraceUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @auther xx* @create 2024-04-18 19:43*/
@Order
@Aspect
@Component
public class ResultTraceIdAspect {@Pointcut("execution(* com.xx..*Controller.*(..)) || execution(* com.xx.exception.GlobalExceptionHandler.*(..))")public void pointCut() {}@Around("pointCut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {Object result = null;result = proceedingJoinPoint.proceed(); //放行if (result instanceof Result<?>) {((Result<?>) result).setTraceId(TraceUtils.getTraceId());}return result;}
}
10、文生文
package com.xx.controller;import com.xx.common.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @Author: xueqimiao* @Date: 2025/4/15 10:35*/
@RestController
@RequestMapping("/hello")
@Tag(name = "文生文")
public class HelloOpenAIController {@Resourceprivate OpenAiChatModel chatClient;@GetMapping("/txtToTxt")@Operation(summary = "文生文")@Parameter(name = "keyword", description = "关键字", required = true)public Result txtToTxt(@RequestParam /*@NoLogParam*/ String keyword) {StopWatch stopWatch = new StopWatch();stopWatch.start();String retValue = chatClient.call(keyword);System.out.println(retValue);stopWatch.stop();System.out.println("耗时: " + stopWatch.getTotalTimeSeconds() + " 毫秒");return Result.ok(retValue);}
}

4、整合阿里通义千问
有些同学没有chatgpt账号,就用以下的方式
1、langchain4j
langchain4j是什么 请见后续讲解
<!--所有调用均基于 OpenAI 协议标准,实现一致的接口设计与规范
LangChain4j 提供与许多 LLM 提供商的集成。每个集成都有自己的 maven 依赖项。
最简单的开始方式是从 OpenAI 集成开始-->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>
2、ChatLanguageModelConfig
package com.xx.config;import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: xueqimiao* @Date: 2025/3/4 10:14* 默认情况下,Spring 会为配置类创建代理,这意味着每次调用 @Bean 方法时都会经过代理处理,这可能会增加一些性能开销。如果你确定不需要这种代理(比如配置类中的方法没有相互调用),可以通过 proxyBeanMethods = false 来禁用代理,减少不必要的开销。*/
@Configuration(proxyBeanMethods = false)
public class ChatLanguageModelConfig {@Beanpublic ChatLanguageModel chatLanguageModel() {returnOpenAiChatModel.builder().apiKey(System.getenv("LANGCHAIN4J_KEY")).modelName("qwen-turbo-0624").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}
}
3、HelloALiAIController
package com.xx.controller;import com.xx.common.Result;
import dev.langchain4j.model.chat.ChatLanguageModel;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @Author: xueqimiao* @Date: 2025/4/15 14:12*/
@RequestMapping("/helloALiAI")
@Tag(name = "ALi AI")
@RestController
public class HelloALiAIController {@Resourceprivate ChatLanguageModel chatLanguageModel;// http://127.0.0.1:8001/chatlanguagemodel/hello@GetMapping(value = "/hello")public Result hello(@RequestParam(value = "prompt", defaultValue = "你是谁") String prompt) {String result = chatLanguageModel.generate(prompt);System.out.println("通过langchain4j调用模型返回结果:" + result);return Result.ok(result);}
}
相关文章:
2、SpringAI接入ChatGPT与微服务整合
2、SpringAI接入ChatGPT与微服务整合 小薛博客AI 大模型资料 1、SpringAI简介 https://spring.io/projects/spring-ai Spring AI是一个人工智能工程的应用框架。其目标是将Spring生态系统的设计原则(如可移植性和模块化设计)应用于人工智能领域&#…...
【Linux】多进程任务模块
创建多个进程,同时完成任务 task.c #include <sys/types.h> #include <unistd.h> #include<stdio.h> #include <sys/wait.h> int create_process_tasks(Task_fun_t tasks[],int tsak_cnt) {pid_t pid;int i 0;for(i 0;i < 4;i){pid …...
榕壹云预约咨询系统:基于ThinkPHP+MySQL+UniApp打造的灵活预约小程序解决方案
数字化咨询场景的痛点与解决方案 在心理咨询、医疗问诊、法律咨询等需要预约服务的场景中,传统线下预约存在效率低、管理复杂、资源分配不均等问题。榕壹云预约咨询系统基于ThinkPHPMySQLUniApp技术栈开发,为咨询类行业提供了一套高效、安全、可扩展的数…...
鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)
import { util } from kit.ArkTS;/*** LRUCache缓存工具类(单例模式)* author 鸿蒙布道师* since 2025/04/21*/ export class LRUCacheUtil {private static instance: LRUCacheUtil;private lruCache: util.LRUCache<string, any>;/*** 私有构造函…...
opencv 图像矫正的原理
图像矫正的原理是透视变换,下面来介绍一下透视变换的概念。 听名字有点熟,我们在图像旋转里接触过仿射变换,知道仿射变换是把一个二维坐标系转换到另一个二维坐标系的过程,转换过程坐标点的相对位置和属性不发生变换,…...
计算机前沿技术课程论文 K-means算法在图像处理的应用
K-means算法在图像处理的应用 这是本人在计算机前沿技术课程中的课程论文文章,为了方便大家参考学习,我把完整的论文word文档发到了我的资源里,有需要的可以自取。 点击完整资源链接 目录 K-means算法在图像处理的应用摘要:引言1…...
【股票数据API接口37】如何获取股票指数实时数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据
如今,量化分析在股市领域风靡一时,其核心要素在于数据,获取股票数据,是踏上量化分析之路的第一步。你可以选择亲手编写爬虫来抓取,但更便捷的方式,莫过于利用专业的股票数据API接口。自编爬虫虽零成本&a…...
【仓颉 + 鸿蒙 + AI Agent】CangjieMagic框架(17):PlanReactExecutor
CangjieMagic框架:使用华为仓颉编程语言编写,专门用于开发AI Agent,支持鸿蒙、Windows、macOS、Linux等系统。 这篇文章剖析一下 CangjieMagic 框架中的 PlanReactExecutor。 1 PlanReactExecutor的工作原理 #mermaid-svg-OqJUCSoxZkzylbDY…...
docker harbor私有仓库登录报错
docker harbor私有仓库登录报错如下: [rootsrv-1 ~]# docker login -u user1 -p pwd1 harbor.chinacloudapi.cn WARNING! Using --password via the CLI is insecure. Use --password-stdin. Error response from daemon: Get "https://harbor.chinacloudapi.…...
IQ信号和实信号的关系与转换的matlab实现
IQ信号 IQ信号通常是指两路正交的信号(I路和Q路),在实际信号采样中,通常会进行IQ采样,将实信号转换为复基带信号进行存储。 IQ信号转实信号 IQ信号转为实信号,其实就是将IQ两路正交信号通过上变频合并为一个实数的带通信号,这通常在通信系统中用于将基带信号调制到载…...
WSL2-Ubuntu22.04安装URSim5.21.3
WSL2-Ubuntu22.04安装URSim5.21.3 准备安装启动 准备 名称版本WSL2Ubuntu22.04URSim5.21.3VcXsrvNaN WSL2安装与可视化请见这篇:WSL2-Ubuntu22.04-配置。 安装 我们是wsl2-ubuntu22.04,所以安装Linux版本的URSim,下载之前需要注册一下,即…...
blender 录课键位显示插件(图文傻瓜式安装)
1、下载 点击这个链接进行下载https://github.com/nutti/Screencast-Keys 下载好不用解压 2、安装 打开blender进行安装 点击编辑选择偏好设置 选择插件再点击这个下箭头 选择从磁盘安装 然后找到自己刚刚下载好的,点击从磁盘安装 安装完成后勾选上插件 …...
天翼云手机断开连接2小时关机
2025-04-21 天翼云手机断开连接2小时自动 天翼云手机 4元1个月 天翼云手机永不关机 天翼云手机不休眠 天翼云手机断开连接时,界面显示:离线运行,2小时后自动关机 电脑每小时自动连接一次 手机每小时自动连接一次...
基于 FFmpeg 的音视频处理基础原理与实验探究
目录 1 基本知识1.1 解封装1.2 AAC和ADTS说明 1.3 H2641.3.1 H264编码结构解析1.3.2 NALU1.3.2 分类 2 实验1 探究音视频信息2.1 重要结构体介绍2.2 相关的API 3 实验二 提取AAC数据4 实验三 提取h264 1 基本知识 1.1 解封装 封装的逆向操作:封装是把音频流、视频流…...
【Rust 精进之路之第5篇-数据基石·下】复合类型:元组 (Tuple) 与数组 (Array) 的定长世界
系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025-04-20 引言:从原子到分子——组合的力量 在上一篇【数据基石上】中,我们仔细研究了 Rust 的四种基本标量类型࿱…...
【前端样式】用 aspect-ratio 实现等比容器:视频封面与图片占位的终极解决方案
在网页开发中,处理视频封面、图片卡片等需要固定比例的容器一直是前端工程师的必修课。本文将以 aspect-ratio 属性为核心,深入探讨如何优雅实现等比容器,并通过完整代码示例和常见问题解析,助你彻底掌握这一现代布局利器。 目录…...
【网络安全】OWASP 十大漏洞
1. OWASP 十大漏洞 为了应对未来的风险,安全专业人员需要随时掌握最新信息。之前,您了解了CVE 列表,这是一个公开的已知漏洞和暴露列表。CVE 列表是全球安全社区相互共享信息的重要信息来源。 在本文中,您将了解安全专业人士参考…...
我用deepseek做了一个提取压缩文件夹下pdf和word文件工具
由于最近需要把大量的压缩文件的pdf和word文件统一复制到一个文件夹中。 我们一般正常操作方式的是把一个压缩文件一个一个解压,然后在把一个的解压好的文件夹下文件复制到另外一个文件夹中。 这个也需太繁琐了,从以往统计的需要花费两个小时间&#x…...
【Docker】在容器中使用 NVIDIA GPU
解决容器 GPU 设备映射问题,实现 AI 应用加速 🔗 官方文档:NVIDIA Container Toolkit GitHub 常见错误排查 若在运行测试容器时遇到以下错误: docker: Error response from daemon: could not select device driver ""…...
机器人进阶---视觉算法(五)仿射变换和投影变换有什么区别
仿射变换和投影变换有什么区别 1. 定义2. 几何特性3. 变换矩阵4. 应用场景5. Python代码示例仿射变换投影变换6. 总结仿射变换和投影变换都是图像处理中常用的几何变换方法,但它们在变换性质、应用场景和变换矩阵等方面存在一些关键区别。 1. 定义 仿射变换 (Affine Transform…...
如何在 Amazon EC2 上部署 Java(Spring Boot 版)
让我们学习如何将 Java Spring Boot Web 服务器部署到 Amazon EC2。每月只需 3 美元。 使用 Azure,您可能不知道要花费多少钱。 Spring Boot 项目示例 在本教程中,我们将重点介绍如何将 Java Spring Boot 服务器部署到 Amazon EC2,因此我们不…...
IDEA打不开、打开报错
目录 场景异常原因解决 场景 1、本机已经安装了IDEA 2、再次安装另外一个版本的IDEA后打不开、打开报错 异常 这里忘记截图了。。。 原因 情况1-打不开:在同一台电脑安装多个IDEA是需要对idea的配置文件进行调整的,否则打不开 情况2-打开报错&#…...
【React】项目的搭建
create-react-app 搭建vite 搭建相关下载 在Vue中搭建项目的步骤:1.首先安装脚手架的环境,2.通过脚手架的指令创建项目 在React中有两种方式去搭建项目:1.和Vue一样,先安装脚手架然后通过脚手架指令搭建;2.npx create-…...
如何提高单元测试的覆盖率
一、定位未覆盖的代码 利用 IDEA 的覆盖率工具: 右键测试类 → Run with Coverage,或使用 AltShiftF10(Windows)打开运行菜单选择覆盖率。查看高亮标记: 绿色:已覆盖代码行。红色&#x…...
【SAP ME 43】RESRCE表操作导致HANA中表锁定解决方案
症状 SAP ME 通过执行以下 SQL 查询导致 RESRCE 表上的 HANA 数据库锁: 从 RESRCE WHERE HANDLE =? 选择站点待更新 或者 SELECT HANDLE FROM RESRCE WHERE HANDLE =... 用于更新 其他条款 HANA、锁、RESRCE 原因和前提条件 该问题是由运行 SQL FOR UPDATE 查询时的 …...
使用C#和FFmpeg开发RTSP视频播放器的完整指南
RTSP(Real Time Streaming Protocol)是流媒体技术中广泛使用的协议,广泛应用于视频监控、视频会议和在线直播等领域。本文将详细介绍如何使用C#和FFmpeg开发一个功能完整的RTSP视频播放器,涵盖从环境搭建到核心功能实现的全部过程。 一、开发环境准备 …...
CSS例子 > 图片瀑布流布局(vue2)
<template><div class"container"><!-- 临时容器用于计算高度 --><div v-if"!isLayoutReady" class"temp-container"><divv-for"(item, index) in list":key"temp- index":ref"(el) > …...
1.2软考系统架构设计师:系统架构的定义与作用 - 练习题附答案及超详细解析
系统架构定义与作用综合知识单选题 题目覆盖核心概念、发展历程、设计原则、评估标准及易混淆点,附答案解析: 1. 系统架构的标准定义源自于以下哪个标准? A. ISO/IEC 9126 B. IEEE 1471-2000 C. TOGAF 9.2 D. ITIL v4 答案:B 简…...
关于springmvc的404问题的一种猜测解决方案
本文是记录关于在学习动力结点老杜的springmvc时候遇到的404报错的一种解决方式; 由于本人之前学过老杜的springmvc,且运行成功,当时使用的是tomcat10.1.19版本。 idea使用2023.3.2版本。 而这次进行回顾的时候,使用tomcat10.0.1…...
PGSql常用操作命令
1 连接数据库: psql -U postgres (psql -U username -d databse_name -h host -W) -U 指定用户 -d 指定数据库 -h 要链接的主机 -W 提示输入密码 psql -h 主机名/服务器IP -p 端口号 -U 用户名 -d 数据库名 注意:(…...
