Spring-GPT智谱清言AI项目(附源码)
一、项目介绍
本项目是Spring AI第三方调用整合智谱请言(官网是:https://open.bigmodel.cn)的案例,回答响应流式输出显示,这里使用的是免费模型,需要其他模型可以去 https://www.bigmodel.cn/pricing 切换。
在这里主要是完整地描述前后端开发和第三方调用的过程,SSE流式请求响应,MD稳定渲染显示等。
二、后端开发
后端使用的是Java,版本是JDK17,spring-boot版本是3.0.2,下面是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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com</groupId>
<artifactId>spring-gpt-service</artifactId>
<version>0.0.9</version>
<name>spring-gpt-service</name>
<description>spring-gpt-service</description>
<!-- 版本信息 -->
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.0.2</spring-boot.version>
</properties>
<!-- 依赖 -->
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- oapi -->
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.3.0</version>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<!-- flux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!--打包jar插件配置-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
由于Get请求参数直接在链接上会受到换行符\n或\r的限制,改用Post请求,使用json封装请求参数,下面是RequestData
类:
import lombok.Data;
@Data
public class RequestData {
private String msg;
}
接口层accumulator.getDelta().getContent()这句代码是返回每次的具体结果
/**
* 通过ModelApiResponse.getFlowable()获取流式数据,最后通过blockingGet()获取最终结果
* System.out.print(accumulator.getDelta().getContent()); // 这句代码是返回的具体结果
* 因为直接SSE传text,受结束符\n影响,可以使用base64传输
*/
@PostMapping(value = "/zp2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> testSseInvoke(@RequestBody RequestData requestData) {
String msg = requestData.getMsg();
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), msg);
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.TRUE)
.messages(messages)
.requestId(requestId)
.build();
Flux<String> flux = Flux.create(emitter -> {
ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
if (sseModelApiResp.isSuccess()) {
AtomicBoolean isFirst = new AtomicBoolean(true);
mapStreamToAccumulator(sseModelApiResp.getFlowable())
.doOnNext(accumulator -> {
// if (isFirst.getAndSet(false)) {
// String base64Msg = Base64.getEncoder().encodeToString("Response: ".getBytes(StandardCharsets.UTF_8));
// emitter.next(base64Msg);
// }
if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
try {
String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls());
String tool = "tool_calls: " + jsonString;
String base64Msg = Base64.getEncoder().encodeToString(tool.getBytes(StandardCharsets.UTF_8));
emitter.next(base64Msg);
} catch (Exception e) {
String err = "Error converting tool_calls to JSON: " + e.getMessage();
String base64Msg = Base64.getEncoder().encodeToString(err.getBytes(StandardCharsets.UTF_8));
emitter.next(base64Msg);
}
}
if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
String content = accumulator.getDelta().getContent();
String base64Msg = Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8));
// 具体结果
// System.out.print(content);
emitter.next(base64Msg);
}
})
.doOnComplete(() -> {
emitter.next("\n");
emitter.complete();
})
.subscribe();
} else {
emitter.next("Error: " + sseModelApiResp.getError() + "\n");
emitter.complete();
}
});
return flux;
}
三、前端实现
前端使用Vue3,具体其他库详见项目的package.json
文件
pnpm create vue@latest
其中使用md-editor-v3来做MD文档格式显示
pnpm add md-editor-v3
下面是在组件中的具体使用:
<script lang="ts" setup>
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/preview.css';
</script>
<LayoutContent class="content">
<!-- 聊天列表 -->
<ul class="chat-list" ref="messageListRef">
<li class="chat-item" :class="`chat-item--${msg.role}`" v-for="msg in chatMessages">
<MdPreview style="padding: 0; background-color: transparent;" type="String" :model-value="msg.content"/>
</li>
<li class="chat-item chat-item--system" :class="{ hidden: !isLoading }">
<MdPreview :model-value="messagePlaceholder"/>
<Spin/>
</li>
</ul>
</LayoutContent>
四、源码仓库
=======================================================================
联系开发者:2013994940@qq.com
Gitee源码地址:https://gitee.com/BuLiangShuai01033/spring-gpt
=======================================================================