2、SpringAI接入ChatGPT与微服务整合

news2025/4/22 20:27:38

2、SpringAI接入ChatGPT与微服务整合

小薛博客AI 大模型资料

1、SpringAI简介

https://spring.io/projects/spring-ai

image-20250219110422639

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: 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

# mac 版
open ~/.zshrc

export 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">
            &lt;!&ndash;日志文件输出的文件名 &ndash;&gt;
            <FileNamePattern>${LOG_HOME}/xx-%d{yyyy-MM-dd}.%i.html</FileNamePattern>
            &lt;!&ndash;日志文件保留天数 &ndash;&gt;
            <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 {

    @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、不记录日志注解

<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、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

  <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
     */
    @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

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());

    @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));
    }
}
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();
    }
}
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 {

    @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是什么 请见后续讲解

<!--所有调用均基于 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 {

    @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

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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

榕壹云预约咨询系统:基于ThinkPHP+MySQL+UniApp打造的灵活预约小程序解决方案

数字化咨询场景的痛点与解决方案 在心理咨询、医疗问诊、法律咨询等需要预约服务的场景中&#xff0c;传统线下预约存在效率低、管理复杂、资源分配不均等问题。榕壹云预约咨询系统基于ThinkPHPMySQLUniApp技术栈开发&#xff0c;为咨询类行业提供了一套高效、安全、可扩展的数…

opencv 图像矫正的原理

图像矫正的原理是透视变换&#xff0c;下面来介绍一下透视变换的概念。 听名字有点熟&#xff0c;我们在图像旋转里接触过仿射变换&#xff0c;知道仿射变换是把一个二维坐标系转换到另一个二维坐标系的过程&#xff0c;转换过程坐标点的相对位置和属性不发生变换&#xff0c;…

计算机前沿技术课程论文 K-means算法在图像处理的应用

K-means算法在图像处理的应用 这是本人在计算机前沿技术课程中的课程论文文章&#xff0c;为了方便大家参考学习&#xff0c;我把完整的论文word文档发到了我的资源里&#xff0c;有需要的可以自取。 点击完整资源链接 目录 K-means算法在图像处理的应用摘要&#xff1a;引言1…

WSL2-Ubuntu22.04安装URSim5.21.3

WSL2-Ubuntu22.04安装URSim5.21.3 准备安装启动 准备 名称版本WSL2Ubuntu22.04URSim5.21.3VcXsrvNaN WSL2安装与可视化请见这篇:WSL2-Ubuntu22.04-配置。 安装 我们是wsl2-ubuntu22.04&#xff0c;所以安装Linux版本的URSim&#xff0c;下载之前需要注册一下&#xff0c;即…

blender 录课键位显示插件(图文傻瓜式安装)

1、下载 点击这个链接进行下载https://github.com/nutti/Screencast-Keys 下载好不用解压 2、安装 打开blender进行安装 点击编辑选择偏好设置 选择插件再点击这个下箭头 选择从磁盘安装 然后找到自己刚刚下载好的&#xff0c;点击从磁盘安装 安装完成后勾选上插件 …

天翼云手机断开连接2小时关机

2025-04-21 天翼云手机断开连接2小时自动 天翼云手机 4元1个月 天翼云手机永不关机 天翼云手机不休眠 天翼云手机断开连接时&#xff0c;界面显示&#xff1a;离线运行&#xff0c;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 解封装 封装的逆向操作&#xff1a;封装是把音频流、视频流…

我用deepseek做了一个提取压缩文件夹下pdf和word文件工具

由于最近需要把大量的压缩文件的pdf和word文件统一复制到一个文件夹中。 我们一般正常操作方式的是把一个压缩文件一个一个解压&#xff0c;然后在把一个的解压好的文件夹下文件复制到另外一个文件夹中。 这个也需太繁琐了&#xff0c;从以往统计的需要花费两个小时间&#x…

机器人进阶---视觉算法(五)仿射变换和投影变换有什么区别

仿射变换和投影变换有什么区别 1. 定义2. 几何特性3. 变换矩阵4. 应用场景5. Python代码示例仿射变换投影变换6. 总结仿射变换和投影变换都是图像处理中常用的几何变换方法,但它们在变换性质、应用场景和变换矩阵等方面存在一些关键区别。 1. 定义 仿射变换 (Affine Transform…

如何在 Amazon EC2 上部署 Java(Spring Boot 版)

让我们学习如何将 Java Spring Boot Web 服务器部署到 Amazon EC2。每月只需 3 美元。 使用 Azure&#xff0c;您可能不知道要花费多少钱。 Spring Boot 项目示例 在本教程中&#xff0c;我们将重点介绍如何将 Java Spring Boot 服务器部署到 Amazon EC2&#xff0c;因此我们不…

IDEA打不开、打开报错

目录 场景异常原因解决 场景 1、本机已经安装了IDEA 2、再次安装另外一个版本的IDEA后打不开、打开报错 异常 这里忘记截图了。。。 原因 情况1-打不开&#xff1a;在同一台电脑安装多个IDEA是需要对idea的配置文件进行调整的&#xff0c;否则打不开 情况2-打开报错&#…

【React】项目的搭建

create-react-app 搭建vite 搭建相关下载 在Vue中搭建项目的步骤&#xff1a;1.首先安装脚手架的环境&#xff0c;2.通过脚手架的指令创建项目 在React中有两种方式去搭建项目&#xff1a;1.和Vue一样&#xff0c;先安装脚手架然后通过脚手架指令搭建&#xff1b;2.npx create-…

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软考系统架构设计师:系统架构的定义与作用 - 练习题附答案及超详细解析

系统架构定义与作用综合知识单选题 题目覆盖核心概念、发展历程、设计原则、评估标准及易混淆点&#xff0c;附答案解析&#xff1a; 1. 系统架构的标准定义源自于以下哪个标准&#xff1f; A. ISO/IEC 9126 B. IEEE 1471-2000 C. TOGAF 9.2 D. ITIL v4 答案&#xff1a;B 简…

关于springmvc的404问题的一种猜测解决方案

本文是记录关于在学习动力结点老杜的springmvc时候遇到的404报错的一种解决方式&#xff1b; 由于本人之前学过老杜的springmvc&#xff0c;且运行成功&#xff0c;当时使用的是tomcat10.1.19版本。 idea使用2023.3.2版本。 而这次进行回顾的时候&#xff0c;使用tomcat10.0.1…

使用Postman调测“获取IAM用户Token”接口实际操作

概述 Postman是网页调试与辅助接口调用的工具&#xff0c;具有界面简洁清晰、操作方便快捷的特性&#xff0c;可以处理用户发送的HTTP请求&#xff0c;例如&#xff1a;GET&#xff0c;PUT、POST&#xff0c;DELETE等&#xff0c;支持用户修改HTTP请求中的参数并返回响应数据。…

如何测试雷达与相机是否时间同步?

在多传感器融合系统中&#xff0c;相机与雷达的协同感知已成为环境理解的关键。相机通过捕捉纹理信息识别物体类别&#xff0c;而雷达利用激光或毫米波实现全天候精确测距。两者的数据融合既能避免单一传感器缺陷&#xff08;如相机受光照影响、雷达缺乏语义信息&#xff09;&a…

爆肝整理!Stable Diffusion的完全使用手册(二)

继续介绍Stable Diffusion的文生图界面功能。 往期文章详见: 爆肝整理&#xff01;Stable Diffusion的完全使用手册&#xff08;一&#xff09; 下面接着对SD的文生图界面的进行详细的介绍。本期介绍文生图界面的截图2&#xff0c;主要包含生成模块下的采用方法、调度类型、迭…

OpenCV day5

函数内容接上文&#xff1a;OpenCV day4-CSDN博客 目录 9.cv2.adaptiveThreshold(): 10.cv2.split()&#xff1a; 11.cv2.merge()&#xff1a; 12.cv2.add()&#xff1a; 13.cv2.subtract()&#xff1a; 14.cv2.multiply()&#xff1a; 15.cv2.divide()&#xff1a; 1…

基于Spring Boot+微信小程序的智慧农蔬微团购平台-项目分享

基于Spring Boot微信小程序的智慧农蔬微团购平台-项目分享 项目介绍项目摘要目录系统功能图管理员E-R图用户E-R图项目预览登录页面商品管理统计分析用户地址添加 最后 项目介绍 使用者&#xff1a;管理员、用户 开发技术&#xff1a;MySQLSpringBoot微信小程序 项目摘要 随着…