- 作者:老汪软件技巧
- 发表时间:2024-11-11 04:03
- 浏览量:
前言
Spring 官方自从发布了 Spring AI,AI 界的门槛儿算是被彻底踹飞了!为什么?因为这就意味着整天只会 CRUD 的 Javaer 们也能开发AI应用了,而且简单到让你怀疑人生。那么本文就基于 Spring AI Alibaba 开发一个简单的 AI 对话应用。
效果展示
实现步骤
基于 Spring AI Alibaba 实现这样一个AI对话非常简单,只需要几行代码就可以完成。可以跟着下面的步骤操作:
创建一个 SpringBoot 项目
Spring AI Alibaba 提供了 SpringBoot starter。所以,第一步就是创建一个SpringBoot项目引入依赖以及配置。
pom.xml文件添加spring-ai-alibaba-starter依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloud.aigroupId>
<artifactId>spring-ai-alibaba-starterartifactId>
<version>1.0.0-M3.1version>
dependency>
dependencies>
application.yml添加
server:
port: 8080
spring:
application:
name: spring-ai-alibaba-chat
ai:
dashscope:
api-key: xxxx
配置中的api-key是阿里云百炼大模型平台的凭证,AI的实现最终会调用该模型。
api-key 申请
这里有两个事项需要注意:
Spring AI Alibaba 基于 Spring Boot 3.x 开发,所以JDK 版本要求为 17 及以上。spring-ai 相关依赖包还没有发布到中央仓库,本地 Maven 仓库要添加以下配置。
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
编写与大模型交互的 Controller
项目创建成功之后,只需要写一个Controller就可以实现与AI对话,代码如下:
@RestController
@RequestMapping("/ai")
public class AIChatController {
private final ChatClient chatClient;
public AIChatController(ChatClient.Builder builder) {
this.chatClient = builder
.build();
}
@GetMapping("/chat")
public String chat(String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
}
代码中的ChatClient类似于应用程序开发中的服务层,只不过这里由spring-ai-alibaba-starter自动初始化了实例,可以直接提供 AI 服务,最终会调用百炼大模型。
此时就可以启动应用,通过浏览器访问localhost:8080/ai/chat?message= 进行对话。
编写通过页面进行对话的 html
为了直观的展示效果,简单实现一个前端和接口交互,通过页面进行AI对话。
html>
<html>
<head>
<meta charset="UTF-8">
<script src="js/marked.min.js">script>
<title>title>
<style>
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.container {
margin: 50px auto;
width: 800px;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
margin-bottom: 30px;
}
label {
display: block;
margin-bottom: 10px;
color: #333;
}
input[type="text"] {
width: 85%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 3px;
}
input[type="submit"] {
background-color: #2ecc71;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 3px;
cursor: pointer;
width: 10%;
}
.chat-box {
width: 100%;
height: 500px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 3px;
overflow-y: scroll;
}
.message {
margin-bottom: 10px;
padding: 10px;
background-color: #f1f1f1;
border-radius: 3px;
}
.user-message {
background-color: #2ecc71;
color: #fff;
}
.bot-message {
background-color: #e6aa6b;
color: #fff;
}
.loader {
text-align: center;
}
.loader::after {
content: "";
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #ccc;
border-top-color: #2ecc71;
animation: spin 1s infinite linear;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
style>
head>
<body>
<div class="container">
<h1>AI 对话h1>
<form id="form" style="width: 47%;position: absolute;bottom: 150px;margin-left:15px">
<input type="text" id="message" name="message" placeholder="输入你的问题">
<input type="submit" value="发送">
form>
<br>
<div id="loader" class="loader" style="display: none;">div>
<div id="chat-box" class="chat-box">div>
div>
<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";
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/chat?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>
body>
html>
js 引入的 marked.min.js 可以去GitHub或者联系我获取。
至此,就实现了文中开头的AI对话效果。
需要了解的基本知识
上面的示例其实就是一个SpringBoot项目,对于Javaer肯定不陌生。而对于没有人工智能相关背景的人来说,如果想要基于 Spring AI 开发一个AI应用产品,还是需要了解一些基本概念的。
对于本文示例中来说,每一次的对话就是模型的一次token输入和输出。
实现有记忆的对话
如果运行了上面的示例,并且进行了几轮对话,会发现大模型并不会理解上下文,也无法实现连贯的对话。
就如上面所说的“模型的认知”,大模型不会记录对话,所以不会产生上下文关联。例如我接下来的问答中,并没有按照我的提问生成“冒险的”故事,只是“冒险的”进行了说明。
那如何让大模型能够追踪、理解并利用先前对话上下文的能力?
spring-ai 支持基于chat memory的对话记忆。在上面的代码中,只需要在构建chatClient时添加两行代码。
private ChatMemory chatMemory = new InMemoryChatMemory();
public AIChatController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory, UUID.randomUUID().toString(), 10))
.build();
}
UUID 会作为此次对话的唯一标识,ChatMemory会将之前的对话内容记录下来,每次对模型的输入就是历史的对话记录,最终实现对话记忆。
这种做法最终会影响使用大模型的成本,就如上面所说,输入和输出都会影响总token数量,而token就相当于金钱。
最后看一下对话记忆效果
总结
Spring AI解决了AI集成的核心难题:即无缝连接企业数据、API与AI模型,同时也简化了Java 开发人员对AI应用的编程复杂度,还提供检索增强生成 (RAG)、函数调用等关键功能。对于Java 开发人员来讲,通过 Spring AI 可以探索更多的 AI 应用场景。