JavaAI02-AI记忆
/</</</这个方法接受的是ChatResponse,ChatResponse可以简单理解为每次说话的内容。
·
手动传递历史记忆
- 先来思考一个问题,我们向AI提问,不能一直是一次性的吧?
AI回答我们之后,是不是我们要基于这个问题继续讨论?
所以我们需要把之前的对话传给AI - 代码使用最基础的openAI
<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>1.0.0-beta3</version> </dependency> - java
import com.qi.code.ApiKey; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.openai.OpenAiChatModel; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel .builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey(ApiKey.DEMO) .modelName("gpt-4o-mini") .build(); UserMessage userMessage1 = UserMessage.userMessage("你好,我是qi"); ChatResponse response1 = model.chat(userMessage1); AiMessage aiMessage1 = response1.aiMessage(); // 大模型的第一次响应 System.out.println(aiMessage1.text()); System.out.println("----"); // 下面一行代码是重点 ChatResponse response2 = model.chat(userMessage1, aiMessage1, UserMessage.userMessage("我叫什么")); AiMessage aiMessage2 = response2.aiMessage(); // 大模型的第二次响应 System.out.println(aiMessage2.text()); } }- 这里的ApiKey.DEMO = “demo”;
- 运行,AI就会基于上次的对话进行思考并回答

- 看一下刚才调用的 chat 方法,它位于ChatLanguageModel接口中
-
这个方法接受的是ChatResponse,ChatResponse可以简单理解为每次说话的内容。
-
这个方法可以传入无数次的对话,可以看到下方有另一个接收形式的方法:

-
所以我们可以把全部历史对话都放到一个集合里,再调用方法即可:
import com.qi.code.ApiKey; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.openai.OpenAiChatModel; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel .builder() .baseUrl("http://langchain4j.dev/demo/openai/v1") .apiKey(ApiKey.DEMO) .modelName("gpt-4o-mini") .build(); UserMessage userMessage1 = UserMessage.userMessage("你好,我是qi"); ChatResponse response1 = model.chat(userMessage1); AiMessage aiMessage1 = response1.aiMessage(); // 大模型的第一次响应 System.out.println(aiMessage1.text()); System.out.println("----"); // 下面一行代码是重点 // ChatResponse response2 = model.chat(userMessage1, aiMessage1, UserMessage.userMessage("我叫什么")); // AiMessage aiMessage2 = response2.aiMessage(); // 大模型的第二次响应 // System.out.println(aiMessage2.text()); List<ChatMessage> history = new ArrayList<>(); history.add(userMessage1); history.add(aiMessage1); history.add(UserMessage.userMessage("我叫什么")); ChatResponse response3 = model.chat(history); AiMessage aiMessage3 = response3.aiMessage(); // 大模型的第二次响应 System.out.println(aiMessage3.text()); } }
-
- 现在想想,是不是可以实现AI的对话记忆了?
确实是,但是好像还是很麻烦,需要手动维护历史对话。
没事,LangChain4j提供了简单的方法。
ChatMemory记忆对话
- 需要引入langchain4j的核心依赖包,以及之前的SpringBoot相关的依赖
<dependencies> <!-- langchain4j核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>1.0.0-beta3</version> </dependency> <!-- SpringBoot 核心包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- SpringBoot Web容器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 阿里云百炼 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId> <version>1.0.0-beta3</version> </dependency> <!-- webflux --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> - application.yml
langchain4j: community: dashscope: chatModel: api-key: "sk-xxxxxxxxxx" model-name: qwen-plus streaming-chat-model: api-key: "sk-xxxxxxxxx" model-name: qwen-plus - 创建config配置类:AiConfig
import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.TokenStream; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AiConfig { public interface Assistant { String chat(String message); // 流式响应 TokenStream stream(String message); } @Bean public Assistant assistant(ChatLanguageModel qwenChatModel, StreamingChatLanguageModel qwenStreamingChatModel) { ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10); Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(qwenChatModel) .streamingChatLanguageModel(qwenStreamingChatModel) .chatMemory(chatMemory) .build(); return assistant; } }- 其实就是通过AiServices创建了代理,将原本调用的方法进行了代理。
- 在对话开始前以及结束后,帮我们把对话内容保存起来
- 编写接口:MemoryController
@RequestMapping("/Memory") @RestController public class MemoryController { @Autowired AiConfig.Assistant assistant; @RequestMapping(value = "/chat_test") public String test1(@RequestParam("message") String message) { return assistant.chat(message); } @RequestMapping(value = "/stream_test",produces ="text/stream;charset=UTF-8") public Flux<String> memoryStreamChat(@RequestParam("message") String message, HttpServletResponse response) { TokenStream stream = assistant.stream(message); return Flux.create(sink -> { stream.onPartialResponse(s -> sink.next(s)) .onCompleteResponse(c -> sink.complete()) .onError(sink::error) .start(); }); } } - 此时调用接口,先调用一个,告诉ai自己的名字

- 此时调用另一个方法,问AI自己的名字,测试它有没有记忆

对话隔离
-
有些时候,我们不希望将之前的对话给到AI,想让他从0开始个思考。
-
重新创建一下Config
@Configuration public class AiIsolationConfig { public interface AssistantUnique { String chat(@MemoryId int memoryId, @UserMessage String userMessage); // 流式响应 TokenStream stream(@MemoryId int memoryId, @UserMessage String userMessage); } @Bean public AssistantUnique assistantUnique(ChatLanguageModel qwenChatModel, StreamingChatLanguageModel qwenStreamingChatModel) { AssistantUnique assistant = AiServices.builder(AssistantUnique.class) .chatLanguageModel(qwenChatModel) .streamingChatLanguageModel(qwenStreamingChatModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder().maxMessages(10) .id(memoryId).build() ) .build(); return assistant; } } -
编写接口代码
@Autowired AiIsolationConfig.AssistantUnique assistantUnique; @RequestMapping(value = "/unique_chat_test") public String test1(@RequestParam("message") String message, @RequestParam("sessionId") Integer sessionId) { return assistantUnique.chat(sessionId, message); } -
此时可以根据不同的记忆(会话)ID,去灵活对话了


-
连续对话


记忆持久化
- 看一下对话存储的位置


- 存放在一个map中的,说明是在内存中存储,后续可以替换到数据库中。
- 可以看到这是ChatMemoryStore的实现类,说明我们只要自定义一个实现类,实现接口的方法,即可自定义记忆存储。
- 定义一个ChatMemoryStore的实现类:DBChatMemoryStore
import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.store.memory.chat.ChatMemoryStore; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DBChatMemoryStore implements ChatMemoryStore { //模拟一个数据库 private final Map<Integer, List<ChatMessage>> db =new HashMap<>(); @Override public List<ChatMessage> getMessages(Object o) { System.out.println("根据memoryId,从数据库中查询"); return null != db.get(o) ? db.get(o) : new ArrayList<>(); } @Override public void updateMessages(Object o, List<ChatMessage> list) { db.put((Integer) o, list); System.out.println("根据memoryId,向数据库中新增/修改记忆对话"); } @Override public void deleteMessages(Object o) { db.remove(o); System.out.println("根据memoryId,删除对话"); } } - 创建config配置类:AiAssistantDBConfig
import dev.langchain4j.memory.chat.ChatMemoryProvider; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.TokenStream; import dev.langchain4j.service.UserMessage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AiAssistantDBConfig { public interface AiAssistantDB { String chat(@MemoryId int memoryId, @UserMessage String userMessage); // 流式响应 TokenStream stream(@MemoryId int memoryId, @UserMessage String userMessage); } @Bean public AiAssistantDB aiAssistantDB(ChatLanguageModel qwenChatModel, StreamingChatLanguageModel qwenStreamingChatModel) { DBChatMemoryStore store = new DBChatMemoryStore(); ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(10) //最多储存记忆数 .chatMemoryStore(store) .build(); AiAssistantDB assistant = AiServices.builder(AiAssistantDB.class) .chatLanguageModel(qwenChatModel) .streamingChatLanguageModel(qwenStreamingChatModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder().maxMessages(10) .id(memoryId).build() ) .chatMemoryProvider(chatMemoryProvider) .build(); return assistant; } } - 编写接口
import com.qi.config.AiAssistantDBConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/DB") @RestController public class DbController { @Autowired AiAssistantDBConfig.AiAssistantDB aiAssistantDB; @RequestMapping(value = "/test1") public String test1(@RequestParam("message") String message, @RequestParam("sessionId") Integer sessionId) { return aiAssistantDB.chat(sessionId, message); } } - 第一次调用接口:http://127.0.0.1:8080/DB/test1?message=你是谁&sessionId=1
- 可以看到他会先查询历史记忆,但此时数据库是空的,所以返回了一个空的集合;

- 然后会把我们的提问对话作为第一条记忆,写入数据库

- 插入后,会再从数据中读取一遍,然后交给AI

- AI思考完成后,会去再一次调用数据库查询

- 然后,会把这次AI的回答写入数据库

- 控制台的打印记录

- 可以看到他会先查询历史记忆,但此时数据库是空的,所以返回了一个空的集合;
- 第二次调用接口,他会依次重复上面的步骤

- 当达到我们设置的最大记忆数(10)之后
- 接下来每新增一条,最会移除最早的一条记录

- 接下来每新增一条,最会移除最早的一条记录
更多推荐



所有评论(0)