SpringAi整合大模型(进阶版)
进阶版是在基础的对话版之上进行新增功能。
如果还没弄出基础版的,请参考
https://blog.csdn.net/weixin_54925172/article/details/144143523?sharetype=blogdetail&sharerId=144143523&sharerefer=PC&sharesource=weixin_54925172&spm=1011.2480.3001.8118
一,进阶版需要实现的功能
- 给AI进行功能预设
- 记忆对话,能自动联系上下文语境
- 结合业务,通过对话操作系统业务
简单解释一下
给AI进行功能预设
比如当客户发送特定消息时,ai需要做出什么回应
或者
让ai充当淘宝客服之类的角色
记忆对话,能自动联系上下文语境
同一时间多个用户访问时,分别可以对应多个用户,不会混淆上下文语境。
比如用户A说了自己是A,用户B说了自己是B,那么A在问自己是谁时,AI能准确回答出用户是A,而不会混淆说A是B。
结合业务,通过对话操作系统业务
类似于现在的手机助手,叫声“小艺,帮我买杯霸王别姬的奶茶”。小艺就会自动下单购买奶茶。
二,代码编写
首先整合一下上次的样例代码,做一下简单的调整。
简单调整上次的代码
controller
package org.example.springaidemo.controller;import org.example.springaidemo.impl.SimpleControllerImpl;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
public class SimpleController {private final SimpleControllerImpl simpleControllerimpl;@Autowiredpublic SimpleController(OpenAiChatModel openAiChatModel, SimpleControllerImpl simpleControllerimpl) {this.simpleControllerimpl = simpleControllerimpl;}@GetMapping("/ai/generate")public String generate(@RequestParam(value = "message", defaultValue = "讲个笑话") String message) {return simpleControllerimpl.generate(message);}@GetMapping("/ai/generateStream")public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "讲个笑话") String message) {return simpleControllerimpl.generateStream(message);}}
impl
package org.example.springaidemo.impl;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;@Service
public class SimpleControllerImpl {private final ChatClient client;@Autowiredpublic SimpleControllerImpl(ChatClient.Builder clientBuilder) {this.client = clientBuilder.build();}public String generate(String msg) {return this.client.prompt().user(msg).call().content();}public Flux<String> generateStream(String msg) {return this.client.prompt().user(msg).stream().content();}
}
测试

给AI进行功能预设
修改我们的impl类
package org.example.springaidemo.impl;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;@Service
public class SimpleControllerImpl {private final ChatClient client;@Autowiredpublic SimpleControllerImpl(ChatClient.Builder clientBuilder) {this.client = clientBuilder.defaultSystem("""你是一家名叫“Rojer”的淘宝客服。当用户语句中包含“退款”时,你需要回复“不好意思,本店不支持7天无理由退款”""").build();}public String generate(String msg) {return this.client.prompt().user(msg).call().content();}public Flux<String> generateStream(String msg) {return this.client.prompt().user(msg).stream().content();}
}
这里需要注意,大模型是经过特定训练后的,它无法做出一些本身禁止于大模型的回复,
比如说污言秽语,比如说民族纠纷,比如说反人类语言。
这个时候,我们再进行一些简单测试

记忆对话,能自动联系上下文语境
这里有两个方面
- 需要开启ai的记忆功能
- 需要对不同用户进行分别的处理
别的不多说,都在代码中,注释中
impl
package org.example.springaidemo.impl;import org.example.springaidemo.config.MychatMemory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;/*** SimpleControllerImpl 是一个服务类,用于处理基于 AI 的对话。* 它利用 Spring AI Chat 框架,通过会话 ID(token)管理不同用户的上下文和对话。*/
@Service
public class SimpleControllerImpl {// AI 对话客户端实例private final ChatClient client;// 自定义的对话存储实现,用于保存用户会话上下文private final MychatMemory mychatMemory;/*** 构造方法,初始化 ChatClient 和自定义的对话存储。** @param clientBuilder 用于构建 ChatClient 的构建器* @param mychatMemory 自定义的对话存储实现*/@Autowiredpublic SimpleControllerImpl(ChatClient.Builder clientBuilder, MychatMemory mychatMemory) {this.mychatMemory = mychatMemory;// 初始化 ChatClient,并设置默认系统提示和对话存储this.client = clientBuilder.defaultSystem("""你是一家名叫“Rojer”的淘宝客服。当用户语句中包含“退款”时,你需要回复“不好意思,本店不支持7天无理由退款”""").defaultAdvisors(new PromptChatMemoryAdvisor(mychatMemory)).build();}/*** 生成基于用户消息和会话 token 的 AI 回复。** @param msg 用户输入的消息* @param token 表示会话唯一标识,用于区分不同用户的上下文* @return AI 的回复内容*/public String generate(String msg, String token) {return this.client.prompt().user(msg) // 用户的输入.advisors(adv -> adv// 设置检索的上下文记录条数.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 指定会话唯一标识,用于区分不同的用户对话.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, token)).call() // 调用 AI 服务,生成回复.content(); // 获取生成的文本内容}/*** 以流式方式生成基于用户消息和会话 token 的 AI 回复。* 适用于需要逐步接收回复内容的场景,例如聊天应用中的实时响应。** @param msg 用户输入的消息* @param token 表示会话唯一标识,用于区分不同用户的上下文* @return Flux<String> 流式的回复内容*/public Flux<String> generateStream(String msg, String token) {return this.client.prompt().user(msg) // 用户的输入.advisors(adv -> adv// 设置检索的上下文记录条数.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 指定会话唯一标识,用于区分不同的用户对话.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, token)).stream() // 以流式模式调用 AI 服务.content(); // 获取生成的文本流内容}
}
自定义的chatMemory
package org.example.springaidemo.config;import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@Component
public class MychatMemory implements ChatMemory {Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();@Overridepublic void add(String conversationId, List<Message> messages) {this.conversationHistory.computeIfAbsent(conversationId, id -> Collections.synchronizedList(new ArrayList<>())).addAll(messages);}@Overridepublic void add(String conversationId, Message message) {this.conversationHistory.computeIfAbsent(conversationId, id -> Collections.synchronizedList(new ArrayList<>())).add(message);}@Overridepublic List<Message> get(String conversationId, int lastN) {List<Message> allMessages = conversationHistory.get(conversationId);if (allMessages == null || allMessages.isEmpty()) {return List.of(); // 如果没有历史记录,返回空列表}// 计算获取的起始位置int start = Math.max(0, allMessages.size() - lastN);return new ArrayList<>(allMessages.subList(start, allMessages.size())); // 返回一个新列表,避免外部修改}@Overridepublic void clear(String conversationId) {conversationHistory.remove(conversationId); // 移除该会话的历史记录}
}
看看 实际对话是否有分别存储到自定义的chatMemory中



这里可以看出已经按照我的需求将两条不同的会话进行分别处理了。
这里可以根据自己的需要,使用标准的token,或者直接使用sessionID都可以。
结合业务,通过对话操作系统业务
这里需要用到springAI提供的fuction方法
详细都在注释 中
SimpleFunction
package org.example.springaidemo.config;import org.example.springaidemo.impl.SimpleControllerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;import java.util.function.Function;/*** SimpleFunction 是一个 Spring 配置类,定义了应用中使用的函数 Bean。* 主要用于暴露基于 Lambda 表达式的业务逻辑函数。*/
@Configuration
public class SimpleFunction {// 引用业务逻辑实现类 SimpleControllerImplprivate final SimpleControllerImpl simpleImpl;/*** 构造方法,注入 SimpleControllerImpl 实例。** @param simpleImpl SimpleControllerImpl 的实例*/@Autowiredpublic SimpleFunction(SimpleControllerImpl simpleImpl) {this.simpleImpl = simpleImpl;}/*** 内部静态记录类,用于封装输入参数。* 在这里,PriceAll 用于传递商品的数量。** @param count 商品的数量*/public record PriceAll(int count){}/*** 定义一个 Function 类型的 Bean,用于计算总价格。** @return 一个函数,接收 PriceAll 类型的输入,返回计算结果(总价格)的字符串表示*/@Bean@Description("获取总价格")public Function<PriceAll, String> getPrice(){return priceCount -> {// 从输入中获取商品数量,并调用业务逻辑计算总价格Double pricedAll = simpleImpl.priceAll(priceCount.count);// 返回总价格的字符串表示return pricedAll.toString();};}
}
修改我们的Impl
package org.example.springaidemo.impl;import org.example.springaidemo.config.MychatMemory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;/*** SimpleControllerImpl 是一个服务类,用于处理基于 AI 的对话。* 它利用 Spring AI Chat 框架,通过会话 ID(token)管理不同用户的上下文和对话。*/
@Service
public class SimpleControllerImpl {// AI 对话客户端实例private final ChatClient client;// 自定义的对话存储实现,用于保存用户会话上下文private final MychatMemory mychatMemory;/*** 构造方法,初始化 ChatClient 和自定义的对话存储。** @param clientBuilder 用于构建 ChatClient 的构建器* @param mychatMemory 自定义的对话存储实现*/@Autowiredpublic SimpleControllerImpl(ChatClient.Builder clientBuilder, MychatMemory mychatMemory) {this.mychatMemory = mychatMemory;// 初始化 ChatClient,并设置默认系统提示和对话存储this.client = clientBuilder.defaultSystem("""你是一家名叫“Rojer”的淘宝客服。当用户语句中包含“退款”时,你需要回复“不好意思,本店不支持7天无理由退款”""").defaultAdvisors(new PromptChatMemoryAdvisor(mychatMemory)).build();}/*** 生成基于用户消息和会话 token 的 AI 回复。** @param msg 用户输入的消息* @param token 表示会话唯一标识,用于区分不同用户的上下文* @return AI 的回复内容*/public String generate(String msg, String token) {return this.client.prompt().user(msg) // 用户的输入.advisors(adv -> adv// 设置检索的上下文记录条数.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 指定会话唯一标识,用于区分不同的用户对话.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, token)).call() // 调用 AI 服务,生成回复.content(); // 获取生成的文本内容}/*** 以流式方式生成基于用户消息和会话 token 的 AI 回复。* 适用于需要逐步接收回复内容的场景,例如聊天应用中的实时响应。** @param msg 用户输入的消息* @param token 表示会话唯一标识,用于区分不同用户的上下文* @return Flux<String> 流式的回复内容*/public Flux<String> generateStream(String msg, String token) {return this.client.prompt().user(msg) // 用户的输入.advisors(adv -> adv// 设置检索的上下文记录条数.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 指定会话唯一标识,用于区分不同的用户对话.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, token)).functions("getPrice")// 指定需要调用的功能.stream() // 以流式模式调用 AI 服务.content(); // 获取生成的文本流内容}public Double priceAll(int count) {double price = 3.25;double re = price * count;System.out.println("打印这条内容,代表已经执行了priceAll该方法。");return re;}
}
看看测试结果


以上,后面还会出AI的进一步详细且方便的使用。欢迎各位大佬持续关注
相关文章:
SpringAi整合大模型(进阶版)
进阶版是在基础的对话版之上进行新增功能。 如果还没弄出基础版的,请参考 https://blog.csdn.net/weixin_54925172/article/details/144143523?sharetypeblogdetail&sharerId144143523&sharereferPC&sharesourceweixin_54925172&spm1011.2480.30…...
为什么爱用低秩矩阵
目录 为什么爱用低秩矩阵 一、定义与性质 二、区别与例子 为什么爱用低秩矩阵 我们更多地提及低秩分解而非满秩分解,主要是因为低秩分解在数据压缩、噪声去除、模型简化和特征提取等方面具有显著的优势。而满秩分解虽然能够保持数据的完整性,但在实际应用中的场景较为有限…...
React 自定义钩子:useOnlineStatus
我们今天的重点是 “useOnlineStatus” 钩子,这是 React 自定义钩子集合中众多精心制作的钩子之一。 Github 的:https://github.com/sergeyleschev/react-custom-hooks import { useState } from "react" import useEventListener from &quo…...
uniapp 小程序 监听全局路由跳转 获取路由参数
uniapp 小程序 监听全局路由跳转 获取路由参数 app.vue中 api文档 onLaunch: function(options) {let that this;let event [navigateTo, redirectTo, switchTab, navigateBack];event.forEach(item > {uni.addInterceptor(item, { //监听跳转//监听跳转success(e) {tha…...
12.02 深度学习-卷积
# 卷积 是用于图像处理 能够保存图像的一些特征 卷积层 如果用全连接神经网络处理图像 计算价格太大了 图像也被转为线性的对象导致失去了图像的空间特征 只有在卷积神经网络cnn的最后一层使用全连接神经网络 # 图像处理的三大任务 # 目标检测 对图像中的目标进行框出来 # 图…...
MySQL 主从同步一致性详解
MySQL主从同步是一种数据复制技术,它允许数据从一个数据库服务器(主服务器)自动同步到一个或多个数据库服务器(从服务器)。这种技术主要用于实现读写分离、提升数据库性能、容灾恢复以及数据冗余备份等目的。下面将详细…...
Spring源码导入idea时gradle构建慢问题
当我们将spring源码导入到idea进行构建的时候,spring采用的是gradle进行构建,默认下注在依赖是从https://repo.maven.apache.org会特别慢,需要改为国内的镜像地址会加快速度。 将项目中build.gradle配置进行调整: repositories …...
Dockerfile 安装echarts插件给java提供服务
java调用echarts插件,生成图片保存到磁盘然后插入到pptx中报表。 Dockerfile文件内容: #基础镜像,如果本地仓库没有,会从远程仓库拉取 openjdk:8 FROM docker.io/centos:centos7 #暴露端口 EXPOSE 9311 # 避免centos 日志输出 …...
Springboot小知识(1):启动类与配置
一、启动类(引导类) 在通常情况下,你创建的Spring应用项目都会为你自动生成一个启动类,它是这个应用的起点。 在Spring Boot中,引导类(也称为启动类,通常是main方法所在的类)是整个…...
[CISCN 2019华东南]Web11
[CISCN 2019华东南]Web11 给了两个链接但是都无法访问 这里我们直接抓包试一下 我们插入X-Forwarded-For:127.0.0.1 发现可以修改了右上角的IP地址,从而可以进行注入 {$smarty.version} 查看版本号 if标签执行PHP命令 {if phpinfo()}{/if} 查看协议 {if system(…...
Cypress内存溢出奔溃问题汇总
内存溢出报错信息 <--- Last few GCs ---> [196:0xe58001bc000] 683925 ms: Scavenge 1870.7 (1969.9) -> 1865.6 (1969.9) MB, 6.07 / 0.00 ms (average mu 0.359, current mu 0.444) task; [196:0xe58001bc000] 683999 ms: Scavenge 1872.4 (1969.9) -> 1867.1…...
树莓派4B--OpenCV安装踩坑
报错: Source directory: /tmp/pip-install-pv7l9r25/opencv-python_08fdf5a130a5429f89b0e0eaab39a329 Working directory: /tmp/pip-install-pv7l9r25/opencv-python_08fdf5a130a5429f89b0e0eaab39a329/_skbuild/linux-armv7l-3.7/cmake-build Please check the i…...
电子电气架构 --- 面向服务的汽车诊断架构
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…...
Pytest --capture 参数详解:如何控制测试执行过程中的输出行为
--capture 选项用于控制测试用例执行过程中标准输出(stdout)和标准错误输出(stderr)的捕获行为。 --capture 的选项值: fd(默认) 捕获文件描述符级别的输出(stdout 和 stderr&#x…...
IS-IS的原理
IS-IS的基本概念: 概述: IS-IS,中间系统到中间系统,是ISO国际标准化组织为它的无连接网络协议设计的一种动态路由协议 IS-IS支持CLNP网络和IP网络,采用数据链路层封装,区别于ospf只支持IP网络࿰…...
C++(4个类型转换)
1. C语言中的类型转换 1. 隐式 类型转换: 具有相近的类型才能进行互相转换,如:int,char,double都表示数值。 2. 强制类型转换:能隐式类型转换就能强制类型转换,隐式类型之间的转换类型强相关,强制类型转换…...
Ubuntu20.04安装NVIDIA显卡驱动
Ubuntu20.04安装NVIDIA显卡驱动 参考资料:https://blog.csdn.net/weixin_39244242/article/details/136282614?fromshareblogdetail&sharetypeblogdetail&sharerId136282614&sharereferPC&sharesourceqq_37397652&sharefromfrom_link 成功配置…...
速盾:介绍一下高防cdn的缓存响应事什么功能?
高防CDN(Content Delivery Network)是一种基于分布式缓存技术的网络加速服务,能够提供强大的缓存响应功能。它的缓存响应功能主要包括缓存加速和智能缓存两个方面。 首先,高防CDN的缓存加速功能是指通过在全球范围内部署大量的缓…...
Nuclei-快速漏洞扫描器
Nuclei-快速漏洞扫描器 声明 学习内容来自 B 站UP主泷羽sec,如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 ✍Ἷ…...
linux网络抓包工具
linux网络抓包工具 一、tcpdump1.1 基本用法1.2 龙芯平台实例操作 二、wireshark2.1 主要功能2.2 龙芯平台实例操作 一、tcpdump tcpdump 指令可列出经过指定网络界面的数据包文件头,可以将网络中传送的数据包的 “头” 完全截获下来提供分析。它支持针对网络层、协…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
持续交付的进化:从DevOps到AI驱动的IT新动能
文章目录 一、持续交付的本质:从手动到自动的交付飞跃关键特性案例:电商平台的高效部署 二、持续交付的演进:从CI到AI驱动的未来发展历程 中国…...
