SpringAI实现AI应用-内置顾问

SpringAI实战链接

1.SpringAl实现AI应用-快速搭建-CSDN博客

2.SpringAI实现AI应用-搭建知识库-CSDN博客

3.SpringAI实现AI应用-内置顾问-CSDN博客

4.SpringAI实现AI应用-使用redis持久化聊天记忆-CSDN博客

5.SpringAI实现AI应用-自定义顾问(Advisor)-CSDN博客

概述

通过前两篇帖子,对SpringAI的使用应该有了大致的了解,那么本篇就需要真正去看以下SpringAI里面真正的东西了

首先先看SpringAI官方文档-Advisors

中文版:顾问 API :: Spring AI 参考 - Spring 框架

官方:Advisors API :: Spring AI Reference

通过文档可以知道Spring AI 框架提供了几个内置顾问,以增强 AI 交互。以下是可用顾问的概览:

聊天记忆顾问

这些顾问在聊天记忆存储中管理对话历史:

MessageChatMemoryAdvisor

检索记忆并将其作为消息集合添加到提示中。这种方法保持了对话历史的结构。请注意,并非所有 AI 模型都支持这种方法。

PromptChatMemoryAdvisor

检索记忆并将其并入提示的系统文本中。

VectorStoreChatMemoryAdvisor

从 VectorStore 中检索记忆并将其添加到提示的系统文本中。这个顾问对于高效搜索和检索大型数据集中的相关信息非常有用。

问题回答顾问

QuestionAnswerAdvisor

这个顾问使用向量存储提供问题回答能力,实现了 RAG(检索增强生成)模式。

内容安全顾问

SafeGuardAdvisor

一个简单的顾问,旨在防止模型生成有害或不适当的内容。

聊天记忆实例

接下来继续使用之前的代码,逐个验证这些内置顾问的功能和作用

ChatMemory

想要使用聊天记忆顾问之前,先看一下聊天记忆顾问里的源码

MessageChatMemoryAdvisor

PromptChatMemoryAdvisor

VectorStoreChatMemoryAdvisor

从源码中可以看到,两个内置顾问的构造方法中都有ChatMemory ,而VectorStoreChatMemoryAdvisor的构造方法入参的是向量库,那就先看一下ChatMemory,它是一个内存管理容器,用于存储和管理多轮对话中的ChatMessage。它不仅允许开发者保存消息,还提供了消息驱逐策略(Eviction Policy)、持久化存储(Persistence)以及特殊消息处理(如SystemMessage和ToolExecutionMessage)等功能。此外,ChatMemory还与high-level组件(如AI服务)集成,便于开发者更方便地管理对话历史。

先看一下ChatMemory里的方法

1.add(String conversationId, Message message):将单个消息添加到指定会话的对话中。

2.add(String conversationId, List<Message> messages):将消息列表添加到指定会话的对话中。

3.get(String conversationId, int lastN):从指定会话的对话中检索最新的N条消息。

4.clear(String conversationId):清除指定会话的对话历史记录。

而ChatMemory的实现类是InMemoryChatMemory这个类再之前两篇帖子的配置类中有实现。

InMemoryChatMemory是Spring AI框架提供的一种ChatMemory实现,它将对话历史记录存储在内存中。这种实现方式具有快速访问和检索消息的优点,适用于快速原型开发和测试场景。由于内存是易失性存储介质,因此InMemoryChatMemory不适用于需要长期保存聊天记录的应用场景。

而其中的方法也为能持久化聊天记录提供了帮助,

修改之前接口代码中的call方法如下(简单实现)

java 复制代码
    /**
     * 根据消息直接输出回答
     * @param map
     * @return
     */
    @PostMapping("/ai/call")
    public String call(@RequestBody Map<String,String> map) {
        String message = map.get("message");
        Message message1 = new UserMessage(message);
        String trim = chatClient.prompt().user(message).call().content().trim();
        Message message2 = new UserMessage(MessageType.SYSTEM, trim, new ArrayList<>(), Map.of());
        inMemoryChatMemory.add("123456",List.of(message1,message2));
        return trim;
    }

然后再获取聊天记录进行(简单实现)

java 复制代码
    /**
     * 查询聊天记里
     * @return
     */
    @GetMapping("/ai/chatMemory")
    public List<Message> chatMemory(){
        List<Message> messages = inMemoryChatMemory.get("123456", 10);
        for (Message message : messages) {
            System.out.println(message.getText());
        }
        return messages;
    }

MessageChatMemoryAdvisor

了解完ChatMemory,那就开始看第一个内置顾问MessageChatMemoryAdvisor,这个也在之前的代码中实现过,当时为了简单实现多轮对话记忆,而且还是加在了方法上

这篇帖子,我们将代码加在配置类上,修改配置类如下,配置类中没有配置会话id和储存大小,是因为统一进行的配置,这种方式将chatClient注册到spring容器的时候,还没有办法知道用户的会话id,所以只能默认,储存大小也使用默认,默认大小为100

java 复制代码
    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                // 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。
                .defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")
                .defaultAdvisors(new MessageChatMemoryAdvisor(inMemoryChatMemory()))
                .build();
    }

此时所有的接口都实现了记忆对话的功能

测试

由此可见在全局都已经实现了对话记忆

PromptChatMemoryAdvisor

再来看PromptChatMemoryAdvisor ,它和MessageChatMemoryAdvisor的类似,也是直接修改配置类,如下

java 复制代码
    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                // 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。
                .defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")
                .defaultAdvisors(
//                        new MessageChatMemoryAdvisor(inMemoryChatMemory())
                        new PromptChatMemoryAdvisor(inMemoryChatMemory())
                )
                .build();
    }

VectorStoreChatMemoryAdvisor

VectorStoreChatMemoryAdvisor是 Spring AI 框架中的一个组件,它结合了向量存储(VectorStore)技术来增强聊天机器人的记忆能力。这个 Advisor 利用向量数据库存储用户与聊天机器人的交互历史,包括用户提出的问题和模型的回答。在生成新的回复时,VectorStoreChatMemoryAdvisor 会检索与当前问题相关的历史记录,并将其作为上下文信息添加到提示中,从而帮助大型语言模型(LLM)生成更连贯和准确的回复。

具体实现需要将聊天记录持久化到向量库,下篇帖子再详细说明聊天记录持久化的问题

区别

看了SpringAI的官方文档,网上也搜了一下这三个内置顾问,都说的太官方了,简单说一下:

**MessageChatMemoryAdvisor:**将对话历史以消息集合的形式存储和传递。它维护了一个结构化的对话历史记录,通常将用户消息和模型响应封装为消息对象。适用于需要保持对话历史结构的场景,例如需要明确区分用户消息和系统消息。

**PromptChatMemoryAdvisor:**将对话历史以纯文本的形式合并到系统提示中。它将历史消息转换为一个字符串,并将其附加到系统提示的文本中。适用于需要将历史信息作为上下文传递给模型的场景,尤其是当模型不支持消息集合时。

**VectorStoreChatMemoryAdvisor:**使用向量存储技术将对话历史存储在向量数据库中。它将对话记录封装为向量形式的文档,并通过向量检索来获取相关的历史信息。适用于需要高效检索和处理大规模数据集的场景。

问题回答实例

QuestionAnswerAdvisor

这个顾问访问的就是上篇帖子实现的向量库,当用户提出问题时,QuestionAnswerAdvisor会首先对知识库进行检索,并将匹配到的相关引用文本添加到用户提问的后面,从而为生成的回答提供更为丰富和准确的上下文。此外,该Advisor还设定了一个默认提示词,旨在确保回答的质量和相关性。如果在知识库中无法找到匹配的文本,系统将可能拒绝回答用户的问题。

实现如下:

java 复制代码
    @Bean
    ChatClient chatClient(ChatClient.Builder builder,VectorStore vectorStore) {
        return builder
                // 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。
                .defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")
                .defaultAdvisors(
//                        new MessageChatMemoryAdvisor(inMemoryChatMemory())
                        new PromptChatMemoryAdvisor(inMemoryChatMemory()),
                        QuestionAnswerAdvisor.builder(vectorStore).order(1).build()
                )
                .build();
    }

测试

经过测试可以看到,只有问向量库中的内容,才会进行回答

内容安全实例

SafeGuardAdvisor

这个顾问顾名思义就是过滤敏感词汇的,具体实现如下

java 复制代码
    @Bean
    ChatClient chatClient(ChatClient.Builder builder,VectorStore vectorStore) {
        return builder
                // 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。
                .defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")
                .defaultAdvisors(
//                        new MessageChatMemoryAdvisor(inMemoryChatMemory())
                        new PromptChatMemoryAdvisor(inMemoryChatMemory()),
                        QuestionAnswerAdvisor.builder(vectorStore).order(1).build(),
                        SafeGuardAdvisor.builder().sensitiveWords(List.of("色情", "暴力")) // 敏感词列表
                                .order(2) // 设置优先级
                                .failureResponse("抱歉,我无法回答这个问题。").build() // 敏感词过滤失败时的响应
                )
                .build();
    }

测试

经过测试,安全顾问已经把敏感词进行了过滤

完整代码

AiConfig(配置文件)

java 复制代码
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.*;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * @Author majinzhong
 * @Date 2025/4/28 10:34
 * @Version 1.0
 */
@Configuration
public class AiConfig {

    @Bean
    ChatClient chatClient(ChatClient.Builder builder,VectorStore vectorStore) {
        return builder
                // 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。
                .defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")
                //这里可以添加多个顾问 order(优先级)越小,越先执行
                // 注意:顾问添加到链中的顺序至关重要,因为它决定了其执行的顺序。每个顾问都会以某种方式修改提示或上下文,一个顾问所做的更改会传递给链中的下一个顾问。
                // 在此配置中,将首先执行MessageChatMemoryAdvisor,将对话历史记录添加到提示中。然后,问答顾问将根据用户的问题和添加的对话历史进行搜索,从而可能提供更相关的结果。
                .defaultAdvisors(
                        //内存存储对话记忆
//                        new MessageChatMemoryAdvisor(inMemoryChatMemory()),
                        new PromptChatMemoryAdvisor(inMemoryChatMemory()),
                        // QuestionAnswerAdvisor 此顾问使用矢量存储提供问答功能,实现RAG(检索增强生成)模式
                        QuestionAnswerAdvisor.builder(vectorStore).order(1).build(),
                        // SafeGuardAdvisor是一个安全防护顾问,它确保生成的内容符合道德和法律标准。
                        SafeGuardAdvisor.builder().sensitiveWords(List.of("色情", "暴力")) // 敏感词列表
                                .order(2) // 设置优先级
                                .failureResponse("抱歉,我无法回答这个问题。").build(), // 敏感词过滤失败时的响应
                        // SimpleLoggerAdvisor是一个记录ChatClient的请求和响应数据的顾问。这对于调试和监控您的AI交互非常有用,建议将其添加到链的末尾。
                        new SimpleLoggerAdvisor()
                )
                .defaultOptions(ChatOptions.builder()
                        .topP(0.7) // 取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值为0.8
                        .build())
                .build();
    }

    @Bean
    ChatMemory inMemoryChatMemory() {
        return new InMemoryChatMemory();
    }
}

AdvisorController(测试接口)

java 复制代码
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

/**
 * @Author majinzhong
 * @Date 2025/5/6 14:22
 * @Version 1.0
 */
@CrossOrigin
@RestController
public class AdvisorController {

    // 负责处理OpenAI的bean,所需参数来自properties文件
    private final ChatClient chatClient;
    //对话记忆
    private final InMemoryChatMemory inMemoryChatMemory;

    public AdvisorController(ChatClient chatClient,InMemoryChatMemory inMemoryChatMemory) {
        this.chatClient = chatClient;
        this.inMemoryChatMemory = inMemoryChatMemory;
    }

    /**
     * 普通聊天
     * @param message
     * @param sessionId
     * @return
     */
    @GetMapping("/ai/generateCall")
    public String generateCall(@RequestParam(value = "message", defaultValue = "讲个笑话") String message, @RequestParam String sessionId) {
        return chatClient.prompt().user(message)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, sessionId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call().content().trim();
    }

    /**
     * 根据消息直接输出回答
     * @param map
     * @return
     */
    @PostMapping("/ai/callAdvisor")
    public String call(@RequestBody Map<String,String> map) {
        String message = map.get("message");
        Message message1 = new UserMessage(message);
        String trim = chatClient.prompt().user(message).call().content().trim();
        Message message2 = new UserMessage(MessageType.SYSTEM, trim, new ArrayList<>(), Map.of());
        inMemoryChatMemory.add("123456",List.of(message1,message2));
        return trim;
    }

    /**
     * 查询聊天记里
     * @return
     */
    @GetMapping("/ai/chatMemory")
    public List<Message> chatMemory(){
        List<Message> messages = inMemoryChatMemory.get("123456", 10);
        for (Message message : messages) {
            System.out.println(message.getText());
        }
        return messages;
    }
}

代码中的这个方法设置了记忆对话的对话id和存储的大小

相关推荐
Faylynn15 分钟前
AI入门:Prompt提示词写法
人工智能·ai·prompt
纪元A梦36 分钟前
华为OD机试真题——荒岛求生(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
java·c语言·javascript·c++·python·华为od·go
___Dream37 分钟前
【TF-BERT】基于张量的融合BERT多模态情感分析
人工智能·深度学习·bert
苹果酱056740 分钟前
iview 表单验证问题 Select 已经选择 还是弹验证提示
java·vue.js·spring boot·mysql·课程设计
Jamence44 分钟前
多模态大语言模型arxiv论文略读(六十三)
人工智能·语言模型·自然语言处理
hkNaruto2 小时前
【AI】Ubuntu 22.04 evalscope 模型评测 Qwen3-4B-FP8
人工智能·ubuntu·qwen3
电商数据girl2 小时前
【Python爬虫电商数据采集+数据分析】采集电商平台数据信息,并做可视化演示
java·开发语言·数据库·爬虫·python·数据分析
夏季疯2 小时前
学习笔记:黑马程序员JavaWeb开发教程(2025.3.30)
java·笔记·学习
源码云商2 小时前
基于 SpringBoot + Vue 的校园管理系统设计与实现
vue.js·spring boot·后端