Appearance
2、SpringAI接入ChatGPT与微服务整合
1、SpringAI简介
https://spring.io/projects/spring-ai
Spring AI是一个人工智能工程的应用框架。其目标是将Spring生态系统的设计原则(如可移植性和模块化设计)应用于人工智能领域,并推动将POJO作为应用程序的构建块应用于AI领域。
SpringAI 是一个基于人工智能技术的工具或平台,通常用于构建、优化和部署AI模型。
简化AI开发:提供易用的接口和框架,加速AI模型的创建与训练。
可移植的API支持跨人工智能提供商的聊天,文本到图像,和嵌入模型。
2、环境要求
xml
<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
<?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
yaml
server:
port: 8001
spring:
application:
name: xx-ai
ai:
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 # 数据库连接 URL
username: 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
sh# mac 版 open ~/.zshrc export OPENAI_KEY="换成自己的" source ~/.zshrc # windows 自己配置环境变量即可
3、启动类
java
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
<?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
java
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 {
@Resource
private 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、不记录日志注解
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
java
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 {
// 我可以用在参数列表或者方法上,屏蔽不愿意记录的参数
}
java
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、ServletResponse
Class 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
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
java
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
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactor);
//设置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
8、MybatisPlusConfig
java
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设计
java
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);
}
}
java
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());
@Override
protected 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));
}
}
java
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 {
@Bean
public TraceFilter traceFilter() {
return new TraceFilter();
}
@Bean
public ResultTraceIdAspect fillRequestIdAspect() {
return new ResultTraceIdAspect();
}
}
java
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、文生文
java
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 {
@Resource
private 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
是什么 请见后续讲解
xml
<!--所有调用均基于 OpenAI 协议标准,实现一致的接口设计与规范
LangChain4j 提供与许多 LLM 提供商的集成。每个集成都有自己的 maven 依赖项。
最简单的开始方式是从 OpenAI 集成开始-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>
2、ChatLanguageModelConfig
java
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 {
@Bean
public ChatLanguageModel chatLanguageModel() {
return
OpenAiChatModel.builder()
.apiKey(System.getenv("LANGCHAIN4J_KEY"))
.modelName("qwen-turbo-0624")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
}
3、HelloALiAIController
java
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 {
@Resource
private 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);
}
}