简绍
Spring AI 会自动配置一个 bean,您可以直接在应用程序中使用该 bean。默认情况下,它使用内存中的存储库来存储消息 () 和实现来管理对话历史记录。如果已经配置了不同的存储库(例如,Cassandra、JDBC 或 Neo4j),Spring AI 将改用它。
内存类型
该抽象允许您实现各种类型的内存以适应不同的用例。内存类型的选择会显着影响应用程序的性能和行为。本节介绍Spring AI提供的内置内存类型及其特点。ChatMemory
消息窗口聊天记忆
MessageWindowChatMemory将消息窗口保持到指定的最大大小。当消息数超过最大值时,将删除较旧的消息,同时保留系统消息。默认窗口大小为 20 封邮件。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder() .maxMessages(10) .build();
这是 Spring AI 用于自动配置 bean 的默认消息类型。ChatMemory
内存存储
Spring AI 提供了用于存储聊天内存的抽象。本节描述了 Spring AI 提供的内置存储库及其使用方法,但如果需要,您也可以实现自己的存储库。ChatMemoryRepository
InMemoryChatMemoryRepository使用 将消息存储在内存中。ConcurrentHashMap
默认情况下,如果尚未配置其他存储库,则 Spring AI 会自动配置可以直接在应用程序中使用的 bean 类型。ChatMemoryRepositoryInMemoryChatMemoryRepository
@AutowiredChatMemoryRepository chatMemoryRepository;
如果您更愿意手动创建 ,可以按如下方式创建:InMemoryChatMemoryRepository
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();
JdbcChatMemory存储库
JdbcChatMemoryRepository是使用 JDBC 在关系数据库中存储消息的内置实现。它支持多个开箱即用的数据库,适用于需要持久存储聊天内存的应用程序。
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId></dependency>
Spring AI 为 提供了 的自动配置,您可以直接在应用程序中使用。JdbcChatMemoryRepository
@AutowiredJdbcChatMemoryRepository chatMemoryRepository;ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果您更愿意手动创建 ,可以通过提供实例和 :JdbcChatMemoryRepositoryJdbcTemplateJdbcChatMemoryRepositoryDialect
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder() .jdbcTemplate(jdbcTemplate) .dialect(new PostgresChatMemoryDialect()) .build();ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
支持的数据库和 Dialect 抽象
Spring AI 通过 dialect 抽象支持多个关系数据库。以下数据库是开箱即用的:
- PostgreSQL 数据库MySQL / MariaDBSQL 服务器HSQLDB 数据库
Schema 初始化
自动配置将在启动时使用特定于供应商的数据库 SQL 脚本自动创建表。默认情况下,架构初始化仅针对嵌入式数据库(H2、HSQL、Derby 等)运行。SPRING_AI_CHAT_MEMORY您可以使用以下属性控制架构初始化:spring.ai.chat.memory.repository.jdbc.initialize-schema
# 控制何时初始化架构。值:(默认值)、、。embedded alway sneverspring.ai.chat.memory.repository.jdbc.initialize-schema=embedded # Only for embedded DBs (default)# 用于初始化的架构脚本的位置。支持 URL 和平台占位符。classpath:spring.ai.chat.memory.repository.jdbc.initialize-schema=always # Always initialize# 在初始化脚本中使用的平台(如果使用@@platform@@占位符)spring.ai.chat.memory.repository.jdbc.initialize-schema=never # Never initialize (useful with Flyway/Liquibase)
要覆盖架构脚本位置,请使用:
spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql```java扩展方言要添加对新数据库的支持,请实现接口并提供用于选择、插入和删除消息的 SQL。然后,您可以将自定义方言传递给存储库构建器。JdbcChatMemoryRepositoryDialect
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate).dialect(new MyCustomDbDialect()).build();
## 聊天客户端中的内存使用 ChatClient API 时,您可以提供一种实现来维护多个交互的对话上下文。ChatMemorySpring AI 提供了一些内置的 Advisor,您可以使用它们根据需要配置 的内存行为。ChatClient- MessageChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互中,它都会从内存中检索对话历史记录,并将其作为消息集合包含在提示中。ChatMemory- PromptChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互时,它都会从内存中检索对话历史记录,并将其作为纯文本附加到系统提示中。ChatMemory- VectorStoreChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互时,它都会从矢量存储中检索对话历史记录,并将其作为纯文本追加到系统消息中。VectorStore例如,如果要与 一起使用,可以按如下方式配置:MessageWindowChatMemoryMessageChatMemoryAdvisor```javaChatMemory chatMemory = MessageWindowChatMemory.builder().build();ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build();
当执行对 的调用时,内存将由 自动管理 。将根据指定的对话 ID 从内存中检索对话历史记录:ChatClientMessageChatMemoryAdvisor
String conversationId = "007";chatClient.prompt() .user("Do I have license to code?") .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .call() .content();
演示案例
private final ZhiPuAiChatModel chatModel;private final ChatClient chatClient;@Autowiredpublic ChatController(ZhiPuAiChatModel chatModel) { this.chatModel = chatModel; MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder() .maxMessages(10) .build(); chatClient = ChatClient.builder(chatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build();}@GetMapping("/ai/generate")public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) { String conversationId = "007"; String content = chatClient.prompt().user(message) .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .call() .content(); return Map.of("generation", content);// return Map.of("generation", chatModel.call(message));}
PromptChat记忆顾问
自定义模板它使用默认模板通过检索到的对话内存来扩充系统消息。您可以通过 builder 方法提供自己的对象来自定义此行为。PromptChatMemoryAdvisorPromptTemplate.promptTemplate()
自定义可以使用任何实现(默认情况下,它使用基于 StringTemplate 引擎)。重要的要求是模板必须包含以下两个占位符:PromptTemplateTemplateRendererStPromptTemplate
- 用于接收原始系统消息的占位符。instructions用于接收检索到的对话内存的占位符。memory
String answer = ChatClient.create(chatModel).prompt() .user(u -> u .text("Tell me the names of 5 movies whose soundtrack was composed by {composer}") .param("composer", "John Williams")) .call() .content();
在内部,ChatClient 使用该类来处理用户和系统文本,并将变量替换为运行时提供的值,具体取决于给定的实现。 默认情况下,Spring AI 使用该实现,该实现基于 Terence Parr 开发的开源 StringTemplate 引擎。PromptTemplateTemplateRedererStTemplateRendererSpring AI 还为不需要模板处理的情况提供了一个。NoOpTemplateRendererSpring AI 还提供了一个 .NoOpTemplateRenderer如果您更愿意使用其他模板引擎,您可以直接向 ChatClient 提供界面的自定义实现。您也可以继续使用默认的 ,但使用自定义配置。TemplateRendererStTemplateRenderer例如,默认情况下,模板变量由语法标识。如果计划在提示中包含 JSON,则可能需要使用不同的语法来避免与 JSON 语法冲突。例如,您可以使用 和 分隔符。{}<>
String answer = ChatClient.create(chatModel).prompt() .user(u -> u .text("Tell me the names of 5 movies whose soundtrack was composed by <composer>") .param("composer", "John Williams")) .templateRenderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build()) .call() .content();
聊天模型中的内存
如果直接使用 a 而不是 a ,则可以显式管理内存:ChatModelChatClient
// Create a memory instanceChatMemory chatMemory = MessageWindowChatMemory.builder().build();String conversationId = "007";// First interactionUserMessage userMessage1 = new UserMessage("My name is James Bond");chatMemory.add(conversationId, userMessage1);ChatResponse response1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));chatMemory.add(conversationId, response1.getResult().getOutput());// Second interactionUserMessage userMessage2 = new UserMessage("What is my name?");chatMemory.add(conversationId, userMessage2);ChatResponse response2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));chatMemory.add(conversationId, response2.getResult().getOutput());// The response will contain "James Bond"