SpringAi整合大模型(进阶版)

news2024/12/26 19:41:20

进阶版是在基础的对话版之上进行新增功能。

如果还没弄出基础版的,请参考

https://blog.csdn.net/weixin_54925172/article/details/144143523?sharetype=blogdetail&sharerId=144143523&sharerefer=PC&sharesource=weixin_54925172&spm=1011.2480.3001.8118

一,进阶版需要实现的功能

  1. 给AI进行功能预设
  2. 记忆对话,能自动联系上下文语境
  3. 结合业务,通过对话操作系统业务

简单解释一下

给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;

    @Autowired
    public 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;

    @Autowired
    public 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;

    @Autowired
    public 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();
    }
}

这里需要注意,大模型是经过特定训练后的,它无法做出一些本身禁止于大模型的回复,

比如说污言秽语,比如说民族纠纷,比如说反人类语言。

这个时候,我们再进行一些简单测试

记忆对话,能自动联系上下文语境

这里有两个方面

  1. 需要开启ai的记忆功能
  2. 需要对不同用户进行分别的处理

别的不多说,都在代码中,注释中

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  自定义的对话存储实现
     */
    @Autowired
    public 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<>();

    @Override
    public void add(String conversationId, List<Message> messages) {
        this.conversationHistory.computeIfAbsent(conversationId, id -> Collections.synchronizedList(new ArrayList<>()))
                .addAll(messages);
    }

    @Override
    public void add(String conversationId, Message message) {
        this.conversationHistory.computeIfAbsent(conversationId, id -> Collections.synchronizedList(new ArrayList<>()))
                .add(message);
    }

    @Override
    public 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())); // 返回一个新列表,避免外部修改

    }

    @Override
    public 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 {

    // 引用业务逻辑实现类 SimpleControllerImpl
    private final SimpleControllerImpl simpleImpl;

    /**
     * 构造方法,注入 SimpleControllerImpl 实例。
     *
     * @param simpleImpl SimpleControllerImpl 的实例
     */
    @Autowired
    public 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  自定义的对话存储实现
     */
    @Autowired
    public 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的进一步详细且方便的使用。欢迎各位大佬持续关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现网站资源批量下载【可转成exe程序运行】

Python实现网站资源批量下载【可转成exe程序运行】 背景介绍解决方案转为exe可执行程序简单点说详细了解下 声明 背景介绍 发现 宣讲家网 的PPT很好&#xff0c;作为学习资料使用很有价值&#xff0c;所以想下载网站的PPT课件到本地&#xff0c;但是由于网站限制&#xff0c;一…

CSS函数

目录 一、背景 二、函数的概念 1. var()函数 2、calc()函数 三、总结 一、背景 今天我们就来说一说&#xff0c;常用的两个css自定义属性&#xff0c;也称为css函数。本文中就成为css函数。先来看一下官方对其的定义。 自定义属性&#xff08;有时候也被称作CSS 变量或者级…

UG NX二次开发(C#)-选择对象居中(不是全部居中)

文章目录 1、前言2、什么是对象居中3、功能实现代码3.1 对象居中3.1 恢复原视图1、前言 在UG NX二次开发过程中,我们经常会用到居中以查看完整的模型,但是对于如果想展示某些对象,而不是全部模型时,那么我们就想将选择的对象(如体对象)居中查看,当查看结束后还能恢复到…

动态规划-----路径问题

动态规划-----路径问题 下降最小路径和1&#xff1a;状态表示2&#xff1a;状态转移方程3 初始化4 填表顺序5 返回值6 代码实现 总结&#xff1a; 下降最小路径和 1&#xff1a;状态表示 假设&#xff1a;用dp[i][j]表示&#xff1a;到达[i,j]的最小路径 2&#xff1a;状态转…

[C++设计模式] 为什么需要设计模式?

文章目录 什么是设计模式&#xff1f;为什么需要设计模式&#xff1f;GOF 设计模式再次理解面向对象软件设计固有的复杂性软件设计复杂性的根本原因如何解决复杂性&#xff1f;分解抽象 结构化 VS 面向对象(封装)结构化设计代码示例&#xff1a;面向对象设计代码示例&#xff1…

级联树结构TreeSelect和上级反查

接口返回结构 前端展示格式 前端组件 <template><div ><el-scrollbar height"70vh"><el-tree :data"deptOptions" :props"{ label: label, children: children }" :expand-on-click-node"false":filter-node-me…

Figma入门-自动布局

Figma入门-自动布局 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c;对…

【Unity基础】使用InputSystem实现物体跳跃

要在Unity中使用 InputSystem 实现小球按空格键跳起的效果&#xff0c;可以按照以下步骤进行&#xff1a; 1. 安装 InputSystem 包 首先&#xff0c;确保你已经安装了 Input System 包。你可以通过以下步骤安装&#xff1a; 打开 Unity 编辑器&#xff0c;点击菜单 Window -…

【ArkTS】使用AVRecorder录制音频 --内附录音机开发详细代码

系列文章目录 【ArkTS】关于ForEach的第三个参数键值 【ArkTS】“一篇带你读懂ForEach和LazyForEach” 【小白拓展】 【ArkTS】“一篇带你掌握TaskPool与Worker两种多线程并发方案” 【ArkTS】 一篇带你掌握“语音转文字技术” --内附详细代码 【ArkTS】技能提高–“用户授权”…

一种多功能调试工具设计方案开源

一种多功能调试工具设计方案开源 设计初衷设计方案具体实现HUB芯片采用沁恒微CH339W。TF卡功能网口功能SPI功能IIC功能JTAG功能下行USB接口 安路FPGA烧录器功能Xilinx FPGA烧录器功能Jlink OB功能串口功能RS232串口RS485和RS422串口自适应接口 CAN功能烧录器功能 目前进度后续计…

浏览器的事件循环机制

浏览器和Node的事件循环机制 引言浏览器的事件循环机制 引言 由于JS是单线程的脚本语言&#xff0c;所以在同一时间只能做一件事情&#xff0c;当遇到多个任务时&#xff0c;我们不可能一直等待任务完成&#xff0c;这会造成巨大的资源浪费。为了协调时间&#xff0c;用户交互…

Zabbix添加防火墙温度监控值实战

我们在Zabbix监控系统会监控诸如Server、network device、application等实例&#xff0c;通常我们在监控某个具体产品时&#xff0c;我们会找到具体的监控模板&#xff0c;在设备添加到平台以后&#xff0c;将模板链接到该设备&#xff0c;但很多时候我们企业内部的设备是没有标…

【k8s】创建基于sa的token的kubeconfig

需求 创建一个基于sa的token的kubeconfig文件&#xff0c;并用这个文件来访问集群。 具体创建sa 和sa的token请参考文章: 【k8s】给ServiceAccount 创建关联的 Secrets-CSDN博客 创建sa apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata:namespace: jtkjdevnam…

Fastapi + vue3 自动化测试平台---移动端App自动化篇

概述 好久写文章了&#xff0c;专注于新框架&#xff0c;新UI界面的实践&#xff0c;废话不多说&#xff0c;开搞 技术架构 后端&#xff1a; Fastapi Airtest multiprocessing 前端&#xff1a; 基于 Vue3、Vite、TypeScript、Pinia、Pinia持久化插件、Unocss 和 Elemen…

Apache Doris 现行版本 Docker-Compose 运行教程

特别注意&#xff01;Doris On Docker 部署方式仅限于开发环境或者功能测试环境&#xff0c;不建议生产环境部署&#xff01; 如有生产环境或性能测试集群部署诉求&#xff0c;请使用裸机/虚机部署或K8S Operator部署方案&#xff01; 原文阅读&#xff1a;Apache Doris 现行版…

Docker的彻底删除与重新安装(ubuntu22.04)

Docker的彻底删除与重新安装&#xff08;ubuntu22.04&#xff09; 一、首先我们彻底删除Docker1、删除docker及安装时自动安装的所有包2、删除无用的相关的配置文件3、删除相关插件4、删除docker的相关配置和目录 二、重新安装1、添加 Docker 的官方 GPG 密钥&#xff1a;2、将…

Nginx学习-安装以及基本的使用

一、背景 Nginx是一个很强大的高性能Web和反向代理服务&#xff0c;也是一种轻量级的Web服务器&#xff0c;可以作为独立的服务器部署网站&#xff0c;应用非常广泛&#xff0c;特别是现在前后端分离的情况下。而在开发过程中&#xff0c;我们常常需要在window系统下使用Nginx…

力扣hot100道【贪心算法后续解题方法心得】(三)

力扣hot100道【贪心算法后续解题方法心得】 十四、贪心算法关键解题思路1、买卖股票的最佳时机2、跳跃游戏3、跳跃游戏 | |4、划分字母区间 十五、动态规划什么是动态规划&#xff1f;关键解题思路和步骤1、打家劫舍2、01背包问题3、完全平方式4、零钱兑换5、单词拆分6、最长递…

系统--线程互斥

1、相关背景知识 临界资源多线程、多执行流共享的资源,就叫做临界资源临界区每个线程内部,访问临界资源的代码互斥在任何时刻,保证有且只有一个执行流进入临界区,访问临界资源,对临界资源起到保护作用原子性不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么…

Qt桌面应用开发 第十天(综合项目二 翻金币)

目录 1.主场景搭建 1.1重载绘制事件&#xff0c;绘制背景图和标题图片 1.2设置窗口标题&#xff0c;大小&#xff0c;图片 1.3退出按钮对应关闭窗口&#xff0c;连接信号 2.开始按钮创建 2.1封装MyPushButton类 2.2加载按钮上的图片 3.开始按钮跳跃效果 3.1按钮向上跳…