阅读量:2
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
关于Spring AI
Spring Boot、Spring Cloud、Spring Data,作为一名Java程序员,相信您对这些概览早已耳熟能详,或者天天在用,在Spring体系中,它们都被称为project,而本系列的核心Spring AI,也是个project,和 Spring Boot、Spring Cloud属于同一级别的存在,其定位是面向人工智能的应用框架
Spring AI有以下特点:
- 在AI的聊天、文生图、嵌入模型等方面提供API级别的支持
- 与模型之间支持同步式和流式交互(还记得ChatGPT返回内容时,是逐字生成的吗,这就是流式交互的效果)
- 多种模型支持
关于《SpringAI实战》系列
- 这是欣宸作为一名Java程序员在LLM应用领域的原创,目标是与广大Java爱好者共同学习和进步,以编码实战的方式逐渐熟悉如何将AI应用在工程中,去实现业务功能
环境信息
- 首先是JDK版本,整个实战系列使用了JDK17,您也可以考虑使用更高版本
- maven版本是3.8.6
- SpringBoot版本是3.2.4
- Spring AI版本是0.8.1,这是目前最新的稳定版
- 另外就是开发环境,我的本地电脑上并未安装java、maven这些开发工具,而是通过GitHub提供的codespace功能,直接在浏览器上用web版的vscode实现了开发和运行的全部过程,自己的电脑只要有浏览器就行,无所谓windows还是mac,安卓平板一样能用
本篇概览
- 作为《SpringAI实战》系列的开篇,本文打算以最快的速度写一个demo并且运行成功,通过这种方法对LLM有个初步的认识
- 这个demo的功能是使用SpringAI提供的openai的starter,来实现对OpenAI接口的调用,得益于Spring框架的良好设计,使整个开发过程也非常简单
- 如果您对OpenAI有所了解,就应该质疑本篇实战的内容:OpenAI的服务就是http接口调用,所以本篇实战不就是调用了http接口吗?这也太水了吧
- 您说的没错,本质上就是写代码进行http调用,但具体的实现方式并不是写http请求和响应的代码,而是使用了SpringAI中和OpenAI相关的API和配置,在《SpringAI实战》系列的后续中,还会使用SpringAI去调用更多的大模型服务,它们并非全部都像OpenAI那样提供http接口,有的甚至是本地部署,这时候,SpringAI的优势就体现出来了:以一致的统一的风格使用各类LLM的能力
准备工作
- 实际上,要想使用OpenAI的服务还是有一定难度的,例如账号里要有钱,对应的API Key才能使用,否则收到的响应就是失败信息
- 如果只是学习用途,也能在网上发现一些免费使用ChatGPT的方法,本篇用的是这个项目提供的免费服务:https://gitcode.com/chatanywhere/GPT_API_free,真心感谢项目作者为我们的学习提供的宝贵帮助
- 上述项目可以为你提供访问OpenAI的地址和API Key,前提是你要有个GitHub账号,具体操作如下
源码下载
- 准备工作完成,接下来开始编码,如果您不想写代码,也可以从GitHub下载代码直接运行,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本篇的源码在leader-tutorials文件夹下,如下图红色箭头所示:
- tutorials目录下有多个项目,整个《SpringAI实战》系列的源码在springai-tutorials,这是个maven工程,里面有多个子工程,今天的实战就是子工程hello-openai
编码:《SpringAI实战》系列的父工程
- 首先是创建一个maven工程,这是《SpringAI实战》系列的父工程,作用是方便对所有实战源码的管理,另外对依赖库的版本管理也可以在这里统一处理,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> <modules> <module>hello-openai</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.bolingcavalry</groupId> <artifactId>springai-tutorials</artifactId> <version>1.0-SNAPSHOT</version> <name>springai-tutorials</name> <description>Simple AI Application demos</description> <packaging>pom</packaging> <properties> <java.version>17</java.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>0.8.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <releases> <enabled>false</enabled> </releases> </repository> </repositories> </project>
- 这个父工程只有这么一个pom.xml文件
编码:快速体验SpringAI的子工程hello-openai
- 接下来的内容就是今天的核心:使用SpringAI来调用
- 在父工程里面创建名为hello-openai的子工程,pom.xml如下,这里面最重要的就是spring-ai-openai-spring-boot-starter这个包的依赖,它给我们带来了
<?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"> <parent> <artifactId>springai-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hello-openai</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
- 通过命令看一下这个starter的依赖情况,看起来spring-ai-core是关键(后面会有专门的文章讲述),其他的都是工具性质的依赖
[INFO] | \- org.springframework.ai:spring-ai-openai:jar:0.8.1:compile [INFO] | +- org.springframework.ai:spring-ai-core:jar:0.8.1:compile [INFO] | | +- io.swagger.core.v3:swagger-annotations:jar:2.2.20:compile [INFO] | | +- com.github.victools:jsonschema-module-swagger-2:jar:4.33.1:compile [INFO] | | +- org.springframework.cloud:spring-cloud-function-context:jar:4.1.0:compile [INFO] | | | +- net.jodah:typetools:jar:0.6.2:compile [INFO] | | | \- org.springframework.cloud:spring-cloud-function-core:jar:4.1.0:compile [INFO] | | +- org.antlr:stringtemplate:jar:4.0.2:compile [INFO] | | | \- org.antlr:antlr-runtime:jar:3.3:compile [INFO] | | +- org.antlr:antlr4-runtime:jar:4.13.1:compile [INFO] | | +- io.projectreactor:reactor-core:jar:3.6.4:compile [INFO] | | | \- org.reactivestreams:reactive-streams:jar:1.0.4:compile [INFO] | | +- org.springframework:spring-messaging:jar:6.1.5:compile [INFO] | | \- com.knuddels:jtokkit:jar:1.0.0:compile [INFO] | +- org.springframework.ai:spring-ai-retry:jar:0.8.1:compile [INFO] | | +- org.springframework.retry:spring-retry:jar:2.0.5:compile [INFO] | | \- org.springframework:spring-webflux:jar:6.1.5:compile [INFO] | +- io.rest-assured:json-path:jar:5.3.2:compile [INFO] | | +- org.apache.groovy:groovy-json:jar:4.0.20:compile [INFO] | | +- org.apache.groovy:groovy:jar:4.0.20:compile [INFO] | | \- io.rest-assured:rest-assured-common:jar:5.3.2:compile [INFO] | | \- org.apache.commons:commons-lang3:jar:3.13.0:compile [INFO] | +- com.github.victools:jsonschema-generator:jar:4.31.1:compile [INFO] | | \- com.fasterxml:classmate:jar:1.6.0:compile [INFO] | +- com.github.victools:jsonschema-module-jackson:jar:4.31.1:compile [INFO] | \- org.springframework:spring-context-support:jar:6.1.5:compile
- 配置文件spring-ai-core,这是hello-openai工程的配置文件,内容如下,您在运行的时候,一定要将sk-xxxxxx改为您自己的API Key:
# 调用OpenAI接口时表明身份的API Key,前面的章节有提到如何生成一个免费的 spring.ai.openai.api-key=sk-xxxxxx # 调用OpenAI接口时的基础地址,如果用的是chatanywhere的API Key,这里就要用chatanywhere提供的地址, # 如果用的是OpenAI的原生API Key,就不用配置这个参数 spring.ai.openai.base-url=https://api.chatanywhere.tech # 用到的模型 spring.ai.openai.chat.options.model=gpt-3.5-turbo # temperature越小,回答的内容越严谨,temperature越大,回答的内容越有创造性 spring.ai.openai.chat.options.temperature=0.7
- 一共有两个java文件,第一个是普通的application类,负责启动服务
package com.bolingcavalry.helloopenai; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
- 另一个是controller类,收到请求后调用OpenAI的接口,将聊天内容发过去,并将OpenAI响应的内容返回,这也是本文的核心内容,completion方法负责接受post请求,并将请求通过chatClinet发送到OpenAI,而call的返回值就是OpenAI的返回内容
package com.bolingcavalry.helloopenai.controller; import org.springframework.ai.chat.ChatClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class SimpleAiController { // 负责处理OpenAI的bean,所需参数来自properties文件 private final ChatClient chatClient; public SimpleAiController(ChatClient chatClient) { this.chatClient = chatClient; } @PostMapping("/ai/simple") public Map<String, String> completion(@RequestBody Map<String,String> map) { return Map.of("generation", chatClient.call(map.get("message"))); } }
- 至此,编码完成,把服务运行起来试试吧
运行验证
- 启动服务后,执行以下命令即可发送请求调用服务的/ai/simple接口,我这边问了一个三国有关的问题
curl --request POST \ --url http://127.0.0.1:8080/ai/simple \ --header 'content-type: application/json' \ --data '{ "message":"你是精通中国历史的专家,请回答:三国时期,魏延的子午谷奇谋是否可行?能在潼关把魏国的援兵堵住吗?如果魏国援兵走武关进入,整个计划是否还有意义?" }'
- 稍等数秒,控制台就会显示返回内容,这就是OpenAI服务返回的内容了
{"generation":"作为专家,我可以告诉你,魏延的子午谷奇谋在理论上是可行的。子午谷是一个地势险要的地方,如果能够在那里设下埋伏,堵住魏国的援兵是有可能的。但是,这需要充分的准备和合理的部署,同时也需要对魏国援兵的行动有准确的情报。\n\n如果魏国援兵走武关进入,整个计划的意义可能会有所减弱。因为子午谷的奇谋是建立在堵住潼关的前提下,如果援兵绕道武关进入,那么计划的效果可能会受到影响。在这种情况下,需要及时调整计划,采取其他措施来应对敌人的行动。\n\n总的来说,魏延的子午谷奇谋在三国时期是一个具有一定可行性的战术计划,但是在实际执行过程中需要根据敌军的行动及时调整和变通。"}
- 至此,本篇实战已经完成,第一次使用SpringAI,似乎…没啥感觉,无非是几个配置,一个client bean,整个过程和接入数据库、消息服务一样简单,但是这样也挺好,可以让咱们的注意力集中到对LLM的使用上
- 接下来的章节,会有更多实战陪大家,一起投入大精彩纷呈的大模型时代,感谢Spring,这一次咱们不掉队