当前位置: 首页 > news >正文

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

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: 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">&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 {@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生态系统的设计原则&#xff08;如可移植性和模块化设计&#xff09;应用于人工智能领域&#…...

6.7.图的深度优先遍历(英文缩写DFS)

树是特殊的图&#xff0c;没有回路的图就是树 BFS与DFS的区别在于&#xff0c;BFS使用了队列&#xff0c;DFS使用了栈 一.深度优先遍历&#xff1a; 1.树的深度优先遍历&#xff1a; 树的深度优先遍历分为先根遍历和后根遍历。 以树的先根遍历为例&#xff1a; 上述图片里…...

若依同步企业微信架构及ACTIVITI

企业微信配置 1.进入企业微信后台管理应用管理&#xff0c;新建应用 2.配置网页授权及JS-SDK 将验证文件下载到项目的pubilc下 启动项目 npm run dev 然后验证域名归属 3.配置企业可信IP 公网IP地址即可&#xff0c;仅此IP的服务可调用企业微信接口信息 4.构造域名回调域&a…...

PyTorch分布式训练调试方法(跟踪调用过程)

PyTorch分布式训练调试方法(跟踪调用过程) 背景 在分布式深度学习训练场景中,通信操作(如AllReduce、Send/Recv)和CUDA操作的时序问题往往难以调试。本工具通过以下方式提供调试支持: 拦截所有PyTorch张量操作并记录调用栈监控分布式通信操作的完整生命周期自动生成带时间…...

跟我学C++中级篇——内存异常的分析

一、内存问题引发的现象 在实际使用电脑过程中&#xff0c;经常会遇到电脑卡住了&#xff0c;鼠标和键盘也没反应。这时候儿会怎么办&#xff1f;其实此时打开任务管理器&#xff0c;就可以发现&#xff0c;有几种情况&#xff1a;一是CPU占用过高&#xff1b;二是IO占用过高&…...

HTML新标签与核心 API 实战

HTML5 新标签与核心 API 实战 引言 今天咱们来看看HTML5 新标签以及一些核心 API。 HTML5 的引入彻底改变了 Web 前端开发格局&#xff0c;尤其是其新增的多媒体和交互能力标签。对于前端开发者而言&#xff0c;理解并掌握 <video>、<audio>、<canvas> 和…...

vscode 红色波浪线问题

VSCode 红色波浪线问题终极解决方案 问题描述 在编写 C 项目时&#xff0c;CMake 编译通过但代码出现红色波浪线&#xff0c;常见问题&#xff1a; #include 提示找不到头文件枚举或类型名未定义成员函数或变量无法识别 这些是 VSCode 的 IntelliSense 配置问题&#xff0c…...

类型补充,scan 和数据库管理命令

Redis 数据类型 stream stream 就是一个队列&#xff08;阻塞队列&#xff09; Redis 作为一个消息队列的重要支撑&#xff0c;属于是 list 中 blpop 和 brpop 的升级版本 geospatial 主要用于存储地理位置信息&#xff0c;并对存储的信息进行操作 存储一些点后&#xff0c…...

AI Agent系列(十) -Data Agent(数据分析智能体)开源资源汇总

AI Agent系列【十】 前言一、 Open Interpreter1.1 Open Interpreter的特点1.2 Open Interpreter的优势1.3 Open Interpreter的应用场景 二、DB-GPT2.1 核心能力2.2关键特性:2.3 系统架构 三、DeepBI3.1 特点 前言 DataAgent的能力本质上比较依赖大模型的自然语言转API/SQL/代…...

Vue3 + TypeScript中provide和inject的用法示例

基础写法&#xff08;类型安全&#xff09; typescript // parent.component.vue import { provide, ref } from vue import type { InjectionKey } from vue// 1. 定义类型化的 InjectionKey const COUNTER_KEY Symbol() as InjectionKey<number> const USER_KEY Sy…...

【国家能源集团生态协作平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

不确定与非单调推理的基本概念

前文我们讨论了建立在经典逻辑基础上的确定性推理,这是一种运用确定性知识进行的精确推理。同时,它又是一种单调性推理,即随着新知识的加人,推出的结论或证明了的命题将单调地增加。但是,人们通常是在信息不完善、不精确的情况下运用不确定性知识进行思维、求解问题的,推…...

系统架构设计师:计算机组成与体系结构(如CPU、存储系统、I/O系统)高效记忆要点、知识体系、考点详解、、练习题并提供答案与解析

计算机组成与体系结构高效记忆要点 从CPU、存储系统、I/O系统三大模块展开&#xff0c;结合高频考点与记忆技巧&#xff0c;有助于系统化掌握核心知识点。 一、CPU结构与工作原理 1. CPU的组成 核心组件&#xff1a; 运算器&#xff08;ALU&#xff09; &#xff1a;负责算术…...

FreeSWITCH中SIP网关(Gateway)操作

freeswitch是一款简单好用的VOIP开源软交换平台。 以下是一篇关于FreeSWITCH中SIP网关&#xff08;Gateway&#xff09;操作的技术指南&#xff0c;基于提供的官方文档内容整理&#xff1a; 一、网关生命周期管理 1. 创建新SIP Profile并启动 FreeSWITCH支持多SIP Profile&…...

构建自动翻译工作流:技术与实践

一、引言 制药行业客户迫切需要一种翻译解决方案&#xff0c;以解决公司内部多样化的翻译难题。他们需要的不仅是提升翻译效率和准确性的工具&#xff0c;更希望能够保持文档的原始格式。我们观察到客户的需求广泛&#xff0c;包括多语言办公文件、研究文档和药品报批文件等&a…...

在pycharm中搭建yolo11分类检测系统--PyQt5学习(二)

第二部分 测试本地pycharm通过程序连接远程服务器autodl 模型的推理需要借助远程服务器autodl&#xff0c;但是界面的运行是在pycharm中&#xff0c;我的设想是按钮调用一个py文件就好了。 1. 本地运行PyQt5界面。 2. 当需要载入权重时&#xff0c;通过SSH连接到AutodL服务…...

汽车行驶工况特征参数:从“速度曲线”到“驾驶DNA”的硬核解码

作为新能源汽车行业的从业者&#xff0c;你是否曾困惑于这些问题&#xff1a; 为什么同一款电动车&#xff0c;不同用户的实际续航差异高达30%&#xff1f;如何精准量化驾驶行为对电池寿命的影响&#xff1f;车企标定的“NEDC续航”与真实路况差距的根源是什么&#xff1f; 这…...

dify本地部署,docker-plugin_daemon-1启动不了,一直报错

解决办法&#xff0c;修改这两个配置&#xff0c;然后重启 进入源代码目录&#xff0c;docker文件夹下 docker compose down docker compose up -d https://github.com/langgenius/dify/issues/17677...

2025 年第十五届 MathorCup 数学应用挑战赛 A 题:汽车风阻预测

A 题&#xff1a;汽车风阻预测 在空气动力学领域&#xff0c;空气阻力对汽车以及航空航天工业中载具的 性能和效率有着至关重要的影响。以往的研究表明&#xff0c;预测这种阻力需 要借助先进的工业仿真软件进行大规模流体力学模拟仿真&#xff0c;而该模拟 过程不仅算法复杂…...

AI Agents系列之AI代理架构体系

1. 引言 智能体架构是定义智能体组件如何组织和交互的蓝图,使智能体能够感知其环境、推理并采取行动。本质上,它就像是智能体的数字大脑——集成了“眼睛”(传感器)、“大脑”(决策逻辑)和“手”(执行器),用于处理信息并采取行动。 选择正确的架构对于构建有效的智能…...

使用Java动态数据生成PDF报告:简化您的报告导出流程

在当今的数据驱动世界中&#xff0c;能够快速且有效地将数据转化为可视化的报告是一项宝贵的技能。无论是商业分析、项目管理还是学术研究&#xff0c;PDF报告都是分享和存档信息的理想格式。在这篇博客中&#xff0c;我们将探讨如何使用Java编程语言结合iText库来动态生成包含…...

Sqlite3交叉编译全过程

Sqlite3交叉编译全过程 一、概述二、下载三、解压四、配置五、编译六、安装七、验证文件类型八、移植8.1、头文件sqlite3.h8.2、动态链接库移植8.3、静态态链接库移植 九、验证使用9.1. 关键函数说明 十、触发器使用十一、sqlite表清空且恢复id值十二、全文总结 一、概述 SQLi…...

Qt QThread 两种线程管理方法

在 Qt 中,QThread 有两种常见的用法,分别对应不同的线程管理模型。这两种方法各有优缺点,适用于不同的场景: 1. 子类化 QThread(继承 QThread,重写 run() 方法) 这是传统的线程用法,类似于 Java 或 C++ 标准库中的线程模型。通过继承 QThread 并重写 run() 方法,将需…...

React 中如何获取 DOM:用 useRef 操作非受控组件

&#x1f4cc; 场景说明 在写 React 的时候&#xff0c;通常我们是通过“受控组件”来管理表单元素&#xff0c;比如用 useState 控制 <input> 的值。 但有些时候&#xff0c;控制的需求只是临时性的&#xff0c;或者完全不需要重新渲染组件&#xff0c;这时候直接访问…...

GoogleCodeUtil.java

Google动态验证码实现 GoogleCodeUtil.java package zwf;import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.SecureRandom;/** https://mvnrepository.com/artifact/commons-codec/…...

Hutool之DateUtil:让Java日期处理变得更加简单

前言 在Java开发中&#xff0c;日期和时间的处理是一个常见问题。为了简化这个过程&#xff0c;许多开发者会使用第三方工具包&#xff0c;如Hutool。Hutool是一个Java工具包&#xff0c;提供了许多实用的功能&#xff0c;其中之一就是日期处理。日期时间工具类是Hutool的核心包…...

解锁古籍中的气候密码,探索GPT/BERT在历史灾害研究中的前沿应用;气候史 文本挖掘 防灾减灾;台风案例、干旱案例、暴雨案例

历史灾害文献分析方法论的研究&#xff0c;是连接过去与未来的关键桥梁。通过对古籍、方志、档案等非结构化文本的系统性挖掘与量化分析&#xff0c;不仅能够重建千年尺度的灾害事件序列&#xff08;如台风、洪旱等&#xff09;&#xff0c;弥补仪器观测数据的时空局限性&#…...

DeepSeek-R3、GPT-4o 与 Claude-3.5-Sonnet 全面对比:性能、应用场景与技术解析

随着大模型技术的迅猛发展&#xff0c;国产模型正逐渐崭露头角&#xff0c;尤其是DeepSeek-R3的发布&#xff0c;更是在AI技术社区中引起广泛关注。而与此同时&#xff0c;国际领先的GPT-4o和Claude-3.5-Sonnet也在不断迭代升级&#xff0c;持续刷新业界对AI能力的认知。下文将…...

智能翻译播放器,让无字幕视频不再难懂

打工人们你们好&#xff01;这里是摸鱼 特供版~ 今天给大家带来一款超牛的黑科技——Splayer&#xff0c;完全免费的智能翻译播放器&#xff0c;让你轻松看懂无字幕视频&#xff01; 推荐指数&#xff1a;★★★★★ 软件简介 Splayer 是一款完全免费的智能翻译播放器&#…...

快手本地生活2024年GMV同增200%,“新线城市+AI”将成增长引擎

4月17日&#xff0c;“新线大市场&#xff0c;AI新机遇”快手本地生活2025聚力大会在沈阳召开。 会上&#xff0c;快手高级副总裁、本地生活事业部负责人笑古在聚力大会上表示&#xff0c;“新线城市AI”将成为快手本地生活的增长引擎。 据「TMT星球」了解&#xff0c;2024年…...

第七篇:系统分析师第三遍——1、2章

目录 一、目标二、计划三、完成情况四、意外之喜(最少2点)1.计划内的明确认知和思想的提升标志2.计划外的具体事情提升内容和标志 五、总结 一、目标 通过参加考试&#xff0c;训练学习能力&#xff0c;而非单纯以拿证为目的。 1.在复习过程中&#xff0c;训练快速阅读能力、掌…...

2025妈妈杯数学建模D题完整分析论文(共42页)(含模型建立、代码)

2025 年第十五届 MathorCup 数学建模D题完整分析论文 目录 摘要 一、问题分析 二、问题重述 三、模型假设 四、 模型建立与求解 4.1问题1 4.1.1问题1思路分析 4.1.2问题1模型建立 4.1.3问题1样例代码&#xff08;MATLAB和Python&#xff09; 4.1.4问题1结果…...

软考高级-系统架构设计师 论文范文参考(一)

文章目录 论SOA技术的应用论SOA在企业信息化中的应用论UP&#xff08;统一过程方法&#xff09;的应用论分布式数据库的设计与实现论改进Web服务器性能的有关技术论基于UML的需求分析论基于构件的软件开发论基于构件的软件开发(二) 论SOA技术的应用 摘要&#xff1a;  本人于…...

江湖路远,唯PUT可稳:Express 路由更新招式全解

前言 江湖传闻,后端开发如同修炼绝世武功:有人精通 POST 掌,横扫千军;有人修习 GET 指法,探查万象。而真正踏入高阶境界的高手,常常默默修炼一门冷门却威力极强的秘技,PUT 神功。 今日时机正好,你我相逢于码海江湖,不如来一场技术切磋,也许能悟出更新之道,功力再上…...

【k8s系列1】一主两从结构的环境准备

环境准备 虚拟机软件准备及安装&#xff0c;这里就不详细展开了&#xff0c;可以看文章:【一、虚拟机vmware安装】 linux环境准备及下载&#xff0c;下载镜像centOS7.9&#xff0c;以前也有写过这个步骤的文章&#xff0c;可以看&#xff1a;【二、安装centOS】 开始进入正题…...

通过特定协议拉起 electron 应用

在 Android 通过 sheme 协议可以拉起其他应用。 electron 应用也可以通过类似特定协议被拉起。 在同时有 web、客户端的应用里&#xff0c;可以通过这种方式在 web 拉起客户端。 支持拉起客户端 const PROTOCOL xxxif (process.defaultApp) {// 这里是开发环境&#xff0c;有…...

IDEA MyBatisCodeHelper Pro插件高版本解密

声明&#xff0c;该方法仅仅作为个人研究学习使用&#xff0c;请勿传播 版本说明 系统&#xff1a;macOS&#xff08;Windows同理&#xff09;IDE&#xff1a;IntelliJ IDEA 2024.4MyBatisCodeHelper Pro 3.4 环境准备 从插件市场下载MyBatisCodeHelper Pro 找到MyBatisCo…...

网站制作公司哪家好?如何选择靠谱的网站设计公司

在当今数字化时代&#xff0c;企业想要在互联网上立足&#xff0c;一个专业、美观且功能强大的网站是必不可少的。然而&#xff0c;面对众多的网站建设公司&#xff0c;如何选择一家真正适合自己需求的合作伙伴&#xff0c;成为了许多企业主和创业者面临的难题。毕竟&#xff0…...

Linux之基础命令

Linux作为开源操作系统的代表&#xff0c;以其高效、灵活和强大的命令行工具闻名。无论是系统管理、开发调试还是日常使用&#xff0c;掌握基础命令都是与Linux系统交互的必备技能。本文整理了20个最常用的Linux基础命令&#xff0c;帮助新手快速入门。 目录 目录与文件导航文…...

使用手机归属地查询API,使效率事半功倍

手机归属地查询API通过查询手机号码的归属地信息&#xff0c;为企业提供了一个高效、便捷的解决方案&#xff0c;帮助企业在竞争激烈的市场中脱颖而出。 代码示例 请求参数说明: 名称必填类型说明key是string个人中心查看phone是int手机号 返回参数说明: 名称类型说明phon…...

基于FPGA的AES加解密系统verilog实现,包含testbench和开发板硬件测试

目录 1.课题概述 2.系统测试效果 3.核心程序与模型 4.系统原理简介 4.1 字节替换&#xff08;SubBytes&#xff09; 4.2 行移位&#xff08;ShiftRows&#xff09; 4.3 列混合&#xff08;MixColumns&#xff09; 4.4 轮密钥加&#xff08;AddRoundKey&#xff09; 4.…...

【AI部署】腾讯云GPU -—SadTalker的AI数字人访问web服务—未来之窗超算中心

访问部署在Cloud Studio上的web服务 当你把该项目部署在本地时&#xff0c;访问该服务的请求地址为http://localhost:8080/hello&#xff1b;当你把该项目部署在Cloud Studio工作台启动时&#xff0c;要想访问到该服务&#xff0c;需要先在工作台右侧打开访问链接面板&#xff…...

Linux:命令行参数、环境变量

命令行参数 执行程序时&#xff0c;可以从命令行传值给 C 程序。这些值被称为命令行参数&#xff0c;它们对程序很重要&#xff0c;特别是当我们想从外部控制程序&#xff0c;而不是在代码内对这些值进行硬编码时&#xff0c;就显得尤为重要了。 在 C 语言中&#xff0c;命令…...

Linux CentOS 更改MySQL数据库目录位置

在 CentOS 系统上更改 MySQL&#xff08;或 MariaDB&#xff09;数据库目录位置&#xff08;datadir&#xff09;需要谨慎操作&#xff0c;以避免数据丢失或服务启动失败。以下是详细步骤&#xff1a; 1. 停止 MySQL/MariaDB 服务 bash 复制 sudo systemctl stop mysqld …...

Unity-微信截图功能简单复刻-03绘制空心矩形

思路-绘制空心矩形 拓展UGUI的Graphic类,实现拖拽接口。 开始拖拽时记录鼠标位置&#xff0c; 使用拖拽中的鼠标位置和记录的位置&#xff0c;计算矩形顶点&#xff0c;绘制矩形。 两个三角形合并为一个矩形&#xff0c;作为空心矩形的一条边&#xff0c;四个边合并为空心矩形…...

Flutter与FastAPI的OSS系统实现

作者&#xff1a;孙嘉成 目录 一、对象存储 二、FastAPI与对象存储 2.1 缤纷云S4服务API对接与鉴权实现 2.2 RESTful接口设计与异步路由优化 三、Flutter界面与数据交互开发 3.1 应用的创建 3.2页面的搭建 3.3 文件的上传 关键词&#xff1a;对象存储、FastAPI、Flutte…...

环境搭建与入门:Flutter SDK安装与配置

环境搭建与入门&#xff1a;Flutter SDK安装与配置 一、Flutter开发环境概述 1.1 Flutter开发环境组成 Flutter开发环境主要包含以下几个关键组件&#xff1a; Flutter SDK&#xff1a;Flutter的核心开发工具包Dart SDK&#xff1a;Flutter使用的编程语言环境IDE/编辑器&am…...

MATLAB 控制系统设计与仿真 - 35

MATLAB鲁棒控制器分析 所谓鲁棒性是指控制系统在一定(结构&#xff0c;大小)的参数扰动下&#xff0c;维持某些性能的特征。 根据对性能的不同定义&#xff0c;可分为稳定鲁棒性(Robust stability)和性能鲁棒性(Robust performance)。 以闭环系统的鲁棒性作为目标设计得到的…...

多模态记忆融合:基于LSTM的连续场景生成——突破AI视频生成长度限制

一、技术背景与核心挑战 2025年视频生成领域面临的关键难题是长时程连贯性——传统方法在生成超过5分钟视频时会出现场景跳变、物理规则不一致等问题。本研究提出时空记忆融合架构&#xff08;ST-MFA&#xff09;&#xff0c;通过LSTM记忆门控与多模态对齐技术&#xff0c;在R…...

架构师面试(三十二):注册中心数据结构

问题 提到【注册中心】&#xff0c;我们对它的基本功能&#xff0c;肯定可以顺手拈来&#xff0c;比如&#xff1a;【服务注册】【服务发现】【健康检查】【变更通知】等。 透过这些基本功能&#xff0c;一个普适的注册中心的数据结构应该如何设计呢&#xff1f; 可以结合着…...