进阶版是在基础的对话版之上进行新增功能。
如果还没弄出基础版的,请参考
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;
    @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();
    }
}
 
这里需要注意,大模型是经过特定训练后的,它无法做出一些本身禁止于大模型的回复,
比如说污言秽语,比如说民族纠纷,比如说反人类语言。
这个时候,我们再进行一些简单测试

记忆对话,能自动联系上下文语境
这里有两个方面
- 需要开启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  自定义的对话存储实现
     */
    @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的进一步详细且方便的使用。欢迎各位大佬持续关注



![[C++设计模式] 为什么需要设计模式?](https://i-blog.csdnimg.cn/img_convert/e9eefb25ddcfbea4a2cb79ac80dba1bc.gif)














