Spring AI 开发专属于你的AI聊天机器人
Spring AI 开发专属于你的AI聊天机器人
前言
随着人工智能技术的飞速发展,聊天机器人在现代商业中的应用越来越广泛
聊天机器人不仅提高了效率,还改善了用户体验,它们可以24/7不间断地为客户提供服务,解答常见问题
本文将介绍如何设计并实现一个AI聊天机器人,该机器人能够理解用户的文本输入并给出相应的回答
通过结合Spring Boot、Spring AI等技术,我们将构建一个AI聊天机器人,适用于各种对话场景
最终效果演示如下:
技术选型与设计
技术选型方面,我们选择Spring Boot自动装配简化开发,Spring AI定义模型的抽象,具体实现采用通义qwen系列大模型
Spring Boot:自动装配简化开发
Spring AI:定义文本、图片、音频等模型的抽象,具体实现由各大厂商接入实现,我们只需要调用顶层API进行开发
通义大模型:阿里qwen系列模型提供Spring AI接口的具体实现
对应版本:
JDK:17及以上
Spring Boot:3.2.4
Spring AI:【spring-ai-core 1.0.0-M1】
通义大模型:【spring-cloud-starter-alibaba-ai 2023.0.1.2】
环境搭建与开发
创建项目
创建Spring Boot项目,配置Maven 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>
<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.caicaijava</groupId>
<artifactId>SpringBoot-AI</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot-AI</name>
<description>SpringBoot-AI</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
<version>2023.0.1.2</version>
</dependency>
</dependencies>
<!-- 通义依赖的spring ai版本未发布maven中央仓库 要配置仓库才能引入 -->
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
开发接口
创建Controller
@RestController
@RequestMapping("/ai")
@CrossOrigin
public class AIController {
@Autowired
private AIService aiService;
@GetMapping("/msg")
public String sendMessage(String message) {
return aiService.chat(message);
}
}
创建AIService接口,可以定义其他模型相关功能,比如图片、音频等
public interface AIService {
/**
* 聊天
* @param msg
* @return
*/
String chat(String msg);
}
编写具体实现:其中TongYiChatModel为通义实现,由自动装配类放入容器
Prompt为Spring AI定义的模型请求,其中包含请求消息列表(上下文),以及模型中通用的请求选项参数,比如用于控制模型生成文本的多样性的top_p、temperature
@Component
public class AIServiceImpl implements AIService{
private final TongYiChatModel tongYiChatModel;
@Autowired
public AIServiceImpl(TongYiChatModel TongYiChatClient) {
this.tongYiChatModel = TongYiChatClient;
}
@Override
public String chat(String msg) {
//请求
Prompt prompt = new Prompt(new UserMessage(msg));
//获取响应
return tongYiChatModel.call(prompt).getResult().getOutput().getContent();
}
}
ChatModel文本聊天接口的call方法具体实现,如请求、响应的处理,与大模型平台网络通信的实现细节,通义已经进行实现,我们只需要调用即可
在访问大模型平台时,通常还需要携带密钥,如果没有密钥可以去百炼平台申请密钥
在application.yml配置文件中进行配置密钥
spring:
application:
name: springboot-ai
cloud:
ai:
tongyi:
connection:
api-key: 通义密钥 可以去申请
前端
前端界面采用通义示例中的前端界面进行微调
创建index.html文件放入resources/static目录中,关键代码如下:
html如下:
<div class="container">
<h1>AI聊天机器人</h1>
<form id="form">
<label for="message">用户发言:</label>
<input type="text" id="message" name="message" placeholder="请输入您要提问的内容~">
<br>
<br>
<input type="submit" value="发送">
</form>
<br>
<div id="loader" class="loader" style="display: none;"></div>
<div id="chat-box" class="chat-box"></div>
</div>
js如下:
<script>
var loader = document.getElementById("loader");
document.getElementById("form").addEventListener("submit", function(event) {
event.preventDefault();
var messageInput = document.getElementById("message");
var message = messageInput.value;
messageInput.value = "";
var chatBox = document.getElementById("chat-box");
var userMessage = document.createElement("div");
userMessage.className = "message user-message";
userMessage.textContent = "用户提问:" + message;
chatBox.appendChild(userMessage);
chatBox.scrollTop = chatBox.scrollHeight;
loader.style.display = "block";
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:8080/ai/msg?message=" + encodeURIComponent(message), true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
loader.style.display = "none";
if (xhr.status === 200) {
var response = xhr.responseText;
var botMessage = document.createElement("div");
botMessage.className = "message bot-message";
var botMessageText = document.createElement("span");
botMessageText.className = "message-text";
botMessage.appendChild(botMessageText);
botMessageText.innerHTML = marked.marked(response);
chatBox.appendChild(botMessage);
chatBox.scrollTop = chatBox.scrollHeight;
} else if (xhr.status === 400) {
var error = JSON.parse(xhr.responseText);
var errorMessage = document.createElement("div");
errorMessage.className = "message bot-message";
errorMessage.textContent = "Bot: " + error.message;
chatBox.appendChild(errorMessage);
chatBox.scrollTop = chatBox.scrollHeight;
} else {
var errorMessage = document.createElement("div");
errorMessage.className = "message bot-message";
errorMessage.textContent = "Bot: Failed to connect to the backend service. Please make sure the backend service is running.";
chatBox.appendChild(errorMessage);
chatBox.scrollTop = chatBox.scrollHeight;
}
}
};
xhr.onloadstart = function() {
loader.style.display = "block";
};
xhr.onloadend = function() {
loader.style.display = "none";
};
xhr.send();
});
</script>
启动项目,访问localhost:8080,即可开始与聊天机器人对话~
源码分析与探究
各个大模型厂商定义的协议规则不同,因此对应的具体实现也是不同的,这里以通义文档为例:
采用HTTP进行请求,请求头携带密钥用于校验,其中请求体格式为JSON:
#请求路径
curl --location "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation" \
#请求头携带密钥 用于校验
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
#请求头标识数据格式为JSON
--header "Content-Type: application/json" \
#请求体
--data '{
"model": "qwen-plus",
"input":{
"messages":[
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你是谁?"
}
]
},
"parameters": {
"result_format": "message"
}
}'
请求体
model为要使用的大模型
input为输入相关,messages为消息列表,可存上下文,其中具体消息会区分角色:
system表示系统角色设定,可以设定大模型平台为一位聊天达人
user表示提问的角色,assistant表示大模型的回复内容
它们组合在一起成为messages列表,能够让模型理解上下文的意思,便于给出满意的回复
parameters为选项参数,比如result_format规定响应格式为消息,还可以规定响应格式为json对象
{
"model": "qwen-plus",
"input": {
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你是谁?"
}
]
},
"parameters": {
"result_format": "message"
}
}
响应
从响应JSON可以看出通常只关心它回复的内容
因此API通过 result.getOutput().getChoices().get(0).getMessage().getContent()
获取回复
{
"status_code": 200,
"request_id": "902fee3b-f7f0-9a8c-96a1-6b4ea25af114",
"code": "",
"message": "",
"output": {
"text": null,
"finish_reason": null,
"choices": [
{
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "我是阿里云开发的一款超大规模语言模型,我叫通义千问。"
}
}
]
},
"usage": {
"input_tokens": 22,
"output_tokens": 17,
"total_tokens": 39
}
}
其他具体的参数说明还是参考文档
接下来,查看下它的具体实现:
public ChatResponse call(Prompt prompt) {
//封装通义聊天文本请求
ConversationParam params = toTongYiChatParams(prompt);
// TongYi models context loader.
//通义模型 上下文加载
com.alibaba.dashscope.common.Message message = new com.alibaba.dashscope.common.Message();
message.setRole(Role.USER.getValue());
message.setContent(prompt.getContents());
//将请求加入上下文msgManager
msgManager.add(message);
params.setMessages(msgManager.get());
logger.trace("TongYi ConversationOptions: {}", params);
//请求调用
GenerationResult chatCompletions = this.callWithFunctionSupport(params);
logger.trace("TongYi ConversationOptions: {}", params);
//将回复加入上下文msgManager
msgManager.add(chatCompletions);
//处理结果
List<org.springframework.ai.chat.model.Generation> generations =
chatCompletions
.getOutput()
.getChoices()
.stream()
.map(choice ->
new org.springframework.ai.chat.model.Generation(
choice
.getMessage()
.getContent()
).withGenerationMetadata(generateChoiceMetadata(choice)
))
.toList();
return new ChatResponse(generations);
}
上面的代码看似没问题,但实际上这个上下文管理器msgManager没有处理隔离性问题,当多个用户进行提问时记录的上下文会导致混乱
你可能觉得隔离性应该由我们手动来进行隔离,但即时手动进行隔离,只要调用该方法还是会被加入上下文,从而产生隔离性问题的bug
但是好在下一个版本2023.0.1.3移除上下文管理器,这样我们就可以手动进行隔离,将上下文填充到入参Prompt中即可
并且该案例中我们使用的是同步调用,当可能返回大量回复时同步等待的时间可能会很长,导致用户体验差,可以采用流式调用,将回复分为多个响应,依次进行返回
总结
本篇文章通过Spring Boot、Spring AI、Alibaba-AI等技术实现AI聊天机器人
Spring AI在定义文本、图像、音视频等模型顶层接口以及通用请求、响应,具体实现由各个大模型厂商来实现
在案例中,我们使用通义大模型来实现文本模型,并发现其版本隔离性问题的一个“bug”
本篇文章只是简单入门,其他额外的功能与玩法,各位同学都可以查看文档进行深入探索
最后(点赞、收藏、关注求求啦~)
我是菜菜,热爱技术交流、分享与写作,喜欢图文并茂、通俗易懂的输出知识,掘金优秀创作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家…
关注菜菜,分享更多技术干货,公众号:菜菜的后端私房菜
本篇文章被收入专栏 Java常用框架,感兴趣的同学可以持续关注喔
本篇文章笔记以及案例被收入 Gitee-CaiCaiJava、 Github-CaiCaiJava,除此之外还有更多Java进阶相关知识,感兴趣的同学可以starred持续关注喔~
有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~
- 点赞
- 收藏
- 关注作者
评论(0)