若依如何集成SpringAIAlibaba完成一个聊天机器人?
随着AI浪潮的推进,越来越多的传统项目融入AI服务已是一种趋势。若依作为目前最好用的后台管理框架之一,SpringAIAlibaba对Qwen系列模型的完美集成,如何在若依框架中集成AI服务来实现一个聊天机器人呢?
最近在写毕设用到了若依并集成了Qwen实现了一些AI服务业务,写这篇文章就是作为一个技术分享,为一些对于SpringAI不怎么熟悉的同学提供帮助:
1.环境要求
SpringAI要求JDK版本最低17,SpringBoot版本在3以上,因此选择的若依框架需要是SpringBoot3 jdk17+的:
https://gitee.com/y_project/RuoYi-Vue/tree/springboot3
2.阿里百炼获取API
前往阿里云百炼平台获取API Key,复制生成的API Key。
3.后端配置
3.1 在父POM中声明依赖
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
<!-- Spring AI ALIBABA的依赖配置-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
3.2 配置application.yml
首先,要在admin模块中引入依赖:
其中memory-jdbc依赖是MySQL的记忆存储
<!-- spring ai alibaba-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
</dependency>
其次,在yml中配置ai:
model是你选择的模型名称,${AI_DASHSCOPE_API_KEY}是你的APIKEY,通过idea的配置注入更安全
# spring ai alibaba
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen3.5-plus
4.接口配置
基础的配置完成了,下面就来进行代码的书写了,在你写业务代码的模块中或者若依其他模块中,这里我用我自己的业务模块:
3.1 pom中引入依赖
<?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>com.hl</groupId>
<artifactId>QwenDishRecomSys</artifactId>
<version>3.9.1</version>
</parent>
<artifactId>QwenDishRecomSys-service</artifactId>
<description>
service业务模块
</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 核心模块-->
<dependency>
<groupId>com.hl</groupId>
<artifactId>QwenDishRecomSys-framework</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- spring ai alibaba-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
</dependency>
</dependencies>
</project>
3.2 config配置chatclient
这里我们配置chatclient,chatclient已经封装好了大模型交流的一切复杂api,我们只需简单调用即可。
配置chatclient如下,添加日志拦截器和模型记忆存储
MySQL的记忆存储不需要我们像springai那样去手动创建存储表,运行之后springaialibaba会自动帮助我们创建。
package com.hl.service.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.hl.service.tools.FeedbackTool;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfig {
@Resource
private MessageWindowChatMemory messageWindowChatMemory;
@Bean
public ChatClient client() {
// 初始化 ChatModel
DashScopeApi dashScopeApi = DashScopeApi.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.build();
ChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.build();
// 初始化日志拦截器
SimpleLoggerAdvisor simpleLoggerAdvisor = new SimpleLoggerAdvisor(
request -> "问题: " + request.prompt().getUserMessage().getText(),
response -> "回答: " + response.getResult().getOutput().getText(),
0
);
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(
simpleLoggerAdvisor,
MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build()
)
.build();
return client;
}
}
MySQL的记忆配置如下,也可用Redis的:
package com.hl.service.config;
import com.alibaba.cloud.ai.memory.jdbc.MysqlChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class MysqlMemoryConfig {
@Bean
public MysqlChatMemoryRepository mysqlChatMemoryRepository(JdbcTemplate jdbcTemplate) {
return MysqlChatMemoryRepository.mysqlBuilder()
.jdbcTemplate(jdbcTemplate)
.build();
}
@Bean
public MessageWindowChatMemory messageWindowChatMemory(MysqlChatMemoryRepository mysqlChatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(mysqlChatMemoryRepository)
.maxMessages(10)
.build();
}
}
3.3 调用接口与模型交流
接口我们所必须得参数:quention(内容)和conversationId(模型实现记忆的必须参数)
@Resource
private ChatClient chatClient;
@Resource
private QwenAIChatMapper qwenAIChatMapper;
@Override
public String Chat(QwenAIChat qwenAIChat) {
// 大模型交流
String answer = chatClient.prompt(qwenAIChat.getQuestion())
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, qwenAIChat.getConversationId()))
.call().content();
return answer;
}
3.4 前端调用接口实现交互
首先需要在idea的环境配置中配置,运行后端
AI_DASHSCOPE_API_KEY=你的apikey
前端配置的api接口如下,因为大模型的响应不像一些普通的接口那样很快,因此我们转为为大模型接口复制一个若依的request,只修改它的timeout,否则的话大模型的一些响应会返回timeout。
import config from '@/config'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { useUserStore } from '@/store/modules/user'
import { toast, showConfirm, tansParams } from '@/utils/common'
let timeout = 200000
const baseUrl = config.baseUrl
const request = config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
config.header = config.header || {}
if (getToken() && !isToken) {
config.header['Authorization'] = 'Bearer ' + getToken()
}
// get请求映射params参数
if (config.params) {
let url = config.url + '?' + tansParams(config.params)
url = url.slice(0, -1)
config.url = url
}
return new Promise((resolve, reject) => {
uni.request({
method: config.method || 'get',
timeout: config.timeout || timeout,
url: config.baseUrl || baseUrl + config.url,
data: config.data,
header: config.header,
dataType: 'json'
}).then(response => {
const res = response
const code = res.data.code || 200
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (code === 401) {
showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
if (res.confirm) {
useUserStore().logOut().then(res => {
uni.reLaunch({ url: '/pages/login' })
})
}
})
reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
toast(msg)
reject('500')
} else if (code !== 200) {
toast(msg)
reject(code)
}
resolve(res.data)
})
.catch(error => {
let { message } = error
if (message === 'Network Error') {
message = '后端接口连接异常'
} else if (message && message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message &&message.includes('Request failed with status code')) {
message = '系统接口' + message.slice(-3) + '异常'
}
toast(message)
reject(error)
})
})
}
export function smartCommendChat(data) {
return request({
url: '/service/ai/chat',
method: 'get',
data: data
})
}
export function feedback(data) {
return request({
url: '/service/ai/feedback',
method: 'post',
data: data
})
}
export function getChatDetail(conversationId) {
return request({
url: '/service/ai/chatDetail/' + conversationId,
method: 'get',
})
}
写完前端页面,我们进行交互:

5.流式输出
大模型最常见的输出模式是流式输出,但是搭配若依框架,流式输出会报错Access Denied,原因是异步请求没有被正确放行,导致 Security 拦截了流式响应。
我们需要在 Spring Security 的 SecurityFilterChain 配置中,允许异步请求访问:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 放行异步请求,避免 SSE 被误拦截
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
.anyRequest().authenticated()
);
return http.build();
}
5.1 流式输出小技巧
响应前端进行流式输出除了后端配置Flux<String>,我们还可以后端响应String,前端接收后进行流式输出模拟来达到一样的效果:
// 模拟流式输出
let index = 0;
const interval = setInterval(() => {
if (index < fullAnswer.length) {
this.messages[assistantMessageIndex].content += fullAnswer.charAt(index);
index++;
} else {
clearInterval(interval);
}
}, 50);
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)