SpringAi整合大模型(进阶版)

发布于:2024-12-06 ⋅ 阅读:(20) ⋅ 点赞:(0)

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

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

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的进一步详细且方便的使用。欢迎各位大佬持续关注


网站公告

今日签到

点亮在社区的每一天
去签到