PromptChatMemoryAdvisor 动态优化提示词

image.png

PromptChatMemoryAdvisor 类用于管理对话历史(Chat Memory)的组件,它结合了 对话记忆(Chat Memory) 和 提示词工程(Prompt Engineering),帮助在多轮对话中维护上下文,并动态优化提示词(Prompt)。

其主要核心功能如下:

  • 对话历史管理:自动保存用户与AI的交互消息,确保后续请求能携带上下文。支持限制记忆的容量,如只保留最近的N条消息。实际是通过内部的 ChatMemory 对象,以及 ChatMemory 对象内部的 ChatMemoryRepository 去完成保存操作。

  • 动态提示词增强:在发送请求给 AI 模型前,自动将历史对话注入到当前提示词中(系统提示词),形成完整的上下文感知 Prompt。内部维护了一个默认的系统提示词模板,如下:

private static final PromptTemplate DEFAULT_SYSTEM_PROMPT_TEMPLATE = new PromptTemplate("""
    {instructions}

    Use the conversation memory from the MEMORY section to provide accurate answers.

    ---------------------
    MEMORY:
    {memory}
    ---------------------

    """);
  • 与 ChatClient 集成:作为 ChatClient 的增强层,透明地处理上下文传递,无需手动拼接历史消息。使用时,简单配置到 defaultAdvisors() 方法中就可以了。

  

构造方法

和其他 Advisor 一样,PromptChatMemoryAdvisor 没有提供公开的构造方法,它的构造方法是私有的,源码:

private PromptChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int order, Scheduler scheduler,
        PromptTemplate systemPromptTemplate) {
    Assert.notNull(chatMemory, "chatMemory cannot be null");
    Assert.hasText(defaultConversationId, "defaultConversationId cannot be null or empty");
    Assert.notNull(scheduler, "scheduler cannot be null");
    Assert.notNull(systemPromptTemplate, "systemPromptTemplate cannot be null");
    this.chatMemory = chatMemory;
    this.defaultConversationId = defaultConversationId;
    this.order = order;
    this.scheduler = scheduler;
    this.systemPromptTemplate = systemPromptTemplate;
}

如果要构建 PromptChatMemoryAdvisor 的实例,可以通过静态方法 builder(ChatMemory chatMemory),源码如下:

public static Builder builder(ChatMemory chatMemory) {
    return new Builder(chatMemory);
}

该方法返回的是 PromptChatMemoryAdvisor.Builder 类型的对象,是静态内部类。

  

PromptChatMemoryAdvisor.Builder

PromptChatMemoryAdvisor.Builder 是 Spring AI 中用于构建 PromptChatMemoryAdvisor 的流式构建器,专门设计用于优化提示工程中的上下文管理。包含了多个配置成员:

public static final class Builder {
    // 系统提示词模板
    private PromptTemplate systemPromptTemplate = DEFAULT_SYSTEM_PROMPT_TEMPLATE;
    // 会话ID
    private String conversationId = ChatMemory.DEFAULT_CONVERSATION_ID;
    // Advisor 序号
    private int order = Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER;
    // 调度策略
    private Scheduler scheduler = BaseAdvisor.DEFAULT_SCHEDULER;
    //	保存会话记录实现
    private ChatMemory chatMemory;

    //...
}

其中属性详细说明:

systemPromptTemplate  基础系统提示模板,定义系统级别的初始提示模板,控制如何将记忆注入到系统提示中。注意,还可包含变量,如 {conversation_id}。默认系统模板如下图:

PromptChatMemoryAdvisor 动态优化提示词

conversationId  唯一会话 ID,必须保证唯一性才能正确隔离不同对话。注意,在生产环境建议使用组合ID,如 userId_sessionId。如果未设置该值时,可能导致记忆混淆。多个用户共用一个会话上下文,这不就乱套了吗。默认值为“default”。

order  控制当前 Advisor 执行顺序,数值越小优先级越高,越优先执行。

scheduler  控制异步处理线程,常用取值:

  • Schedulers.immediate()   同步执行(默认)

  • Schedulers.boundedElastic()   弹性线程池,推荐 IO 操作

  • Schedulers.parallel()   固定线程池,CPU 密集型

chatMemory  对话记忆存储实现,。常用实现如下图:

PromptChatMemoryAdvisor 动态优化提示词

其中:

  • InMemoryChatMemoryRepository  基于内存的对话记忆存储实现,适用于开发测试环境或轻量级生产场景,提供零延迟的对话记忆访问。

  • JdbcChatMemoryRepository  基于 关系型数据库 的对话记忆存储实现,通过 JDBC 提供标准化的 SQL 数据库访问,支持主流关系型数据库如 MySQL、PostgreSQL、Oracle 等。

  • Neo4jChatMemoryRepository   为 Neo4j 图数据库设计的对话记忆存储实现,它利用图数据库的天然优势来高效存储和检索复杂的对话关系。

  • CassandraChatMemoryRepository  基于 Apache Cassandra 数据库的对话记忆存储实现,专为大规模、高并发的对话系统设计,利用 Cassandra 的分布式特性实现高性能的记忆存储与检索。

  

简单示例

下面示例将通过 SimpleLoggerAdvisor 来观察 PromptChatMemoryAdvisor 对 AI 会话增强的请求信息,是否自动帮助我们将历史信息添加到会话中。

准备工作

由于我们还是通过 MySQL 数据库来保存历史会话信息,配置可以参考“MessageChatMemoryAdvisor 对话历史管理”。

添加依赖

依赖项也参考“MessageChatMemoryAdvisor 对话历史管理”。

配置文件

配置文件请参考“MessageChatMemoryAdvisor 对话历史管理”。

创建配置类

创建名为 AiConfig 的配置类,添加 PromptChatMemoryAdvisor 相关配置,以及自定义一个系统提示词模板,如下:

package com.hxstrive.springai.springai_openai.advisor_PromptChatMemoryAdvisor.config;

import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * AI配置
 * @author Administrator
 */
@Configuration
public class AiConfig {

    // 实际存储历史会话,这里通过数据库存储
    @Bean
    public ChatMemoryRepository chatMemoryRepository(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate).build();
    }

    // 回话记忆
    @Bean
    public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository) // 会话存储实现
                .maxMessages(10) // 消息大小,10条,包含请求和响应,即仅仅保存5个对话数据
                .build();
    }

    @Bean
    public PromptChatMemoryAdvisor promptChatMemoryAdvisor(ChatMemory chatMemory) {
        // 自定义系统提示模板
        PromptTemplate promptTemplate = new PromptTemplate("""
			{instructions}

			请使用 “MEMORY” 部分中的对话记忆来提供准确答案。

			---------------------
			MEMORY:
			{memory}
			---------------------

			""");
        return PromptChatMemoryAdvisor.builder(chatMemory).systemPromptTemplate(promptTemplate).build();
    }

    // 添加日志 Advisor,要显示需要设置日志级别为 DEBUG
    @Bean
    public SimpleLoggerAdvisor simpleLoggerAdvisor() {
        return new SimpleLoggerAdvisor();
    }

}

📢注意,上述配置的系统提示词模板中的 {instructions} {memory} 占位符,Spring AI 会自动帮我们替换。我们还可以添加自定义的占位符,但需要使用 PromptTemplate 的 add(String name, Object value) 添加占位符具体的值。

创建 Controller

创建名为 AIController 的 Controller,使用 @Autowired 将配置的 Advisor 注入进去,然后通过 ChatClient 的 defaultSystem() 方法进行配置,如下:

package com.hxstrive.springai.springai_openai.advisor_PromptChatMemoryAdvisor.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.model.ChatModel;
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;

@RestController
public class AIController {

    @Autowired
    private ChatModel chatModel;

    @Autowired
    private PromptChatMemoryAdvisor promptChatMemoryAdvisor;

    @Autowired
    private SimpleLoggerAdvisor simpleLoggerAdvisor;

    @GetMapping("/ai/simple")
    public String completion(@RequestParam("userText") String userText) {
        // 构建 RAG advisor 时需要完成 vectorStore 初始化
        ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultSystem("请使用中文进行回复")
        .defaultAdvisors(
            simpleLoggerAdvisor,
            promptChatMemoryAdvisor
        )
        .build();

        // conversationId 区分不同对话,定位对应的历史记录
        String conversationId = "sessionId-20250705220950";
        return chatClient.prompt()
        // 运行时设置会话ID参数
        .advisors(advisor -> {
            advisor.param(ChatMemory.CONVERSATION_ID, conversationId);
        })
        .user(userText)
        .call()
        .content();
    }

}

运行&验证

启动应用程序,通过浏览器访问,问“成都大学是 211 吗?”,由于首次访问,你会发现打印的日志中“MEMORY”部分内容为空,如下图:

PromptChatMemoryAdvisor 动态优化提示词

PromptChatMemoryAdvisor 动态优化提示词

再次,问“是 985?”,此时日志中的“MEMORY”部分出现了上次对话的问题和答案。如下图:

PromptChatMemoryAdvisor 动态优化提示词

PromptChatMemoryAdvisor 动态优化提示词

而且,你发现了吗?“是 985?”我们没有说什么是 985,AI 根据上下文推断出你问的是“成都大学”是 985?。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号