支持多用户
上篇文章中,我们使用Chat Memory实现了对聊天内容的储存,但是,其中的代码案例只支持存储一个用户的聊天数据。实际应用中,我们的系统肯定是多用户的,每个用户都需要维护各自的聊天数据。为了解决这个问题,我们可以使用ChatMemoryProvider实现,ChatMemoryProvider的源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package dev.langchain4j.memory.chat;
import dev.langchain4j.memory.ChatMemory;
@FunctionalInterface
public interface ChatMemoryProvider {
ChatMemory get(Object var1);
}
从源码中,我们可以看到,ChatMemoryProvider中仅包含一个get方法,我们可以使用用户id做为该方法的参数。
支持多用户的代码如下:
package com.renr.langchain4jnew.app;
import com.renr.langchain4jnew.constant.CommonConstants;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class App7 {
public static void main(String[] args) {
// 表示将聊天的上下文信息存储到内容中
InMemoryChatMemoryStore memoryStore = new InMemoryChatMemoryStore();
// 创建对象,通过lambda表达式重写其中的get方法
ChatMemoryProvider chatMemoryProvider = userId -> MessageWindowChatMemory.builder()
.id(userId)
.maxMessages(10)
.chatMemoryStore(memoryStore)
.build();
// 创建智谱的模型对象
ZhipuAiChatModel zhipuAiChatModel = ZhipuAiChatModel.builder()
// 模型key
.apiKey(CommonConstants.API_KEY)
// 精确度
.temperature(0.9)
.model("GLM-4-Flash")
.maxRetries(3)
.callTimeout(Duration.ofSeconds(60))
.connectTimeout(Duration.ofSeconds(60))
.writeTimeout(Duration.ofSeconds(60))
.readTimeout(Duration.ofSeconds(60))
// 请求的日志
//.logRequests(true)
// 响应数据的日志
//.logResponses(true)
.build();
// 用户发送的消息
UserMessage firstUserMessage = UserMessage.from("你好,我叫张三");
// 将问题添加到chat memory
// 假设用户id是111,根据chatMemoryProvider.get(111)获取该用户的ChatMemory对象
chatMemoryProvider.get(111).add(firstUserMessage);
// 发送消息时,直接通过ChatMemory获取聊天的上下文
AiMessage firstAiMessage = zhipuAiChatModel.chat(chatMemoryProvider.get(111).messages()).aiMessage();
System.out.println("第一次回答:" + firstAiMessage.text());
// 将回答的内容添加到chat memory
chatMemoryProvider.get(111).add(firstAiMessage);
UserMessage secondUserMessage = UserMessage.from("请问我叫什么名字?");
chatMemoryProvider.get(111).add(secondUserMessage);
AiMessage secondAiMessage = zhipuAiChatModel.chat(chatMemoryProvider.get(111).messages()).aiMessage();
System.out.println("第二次回答:" + secondAiMessage.text());
chatMemoryProvider.get(111).add(secondAiMessage);
}
}
本例中,我们将上下文信息存储在InMemoryChatMemoryStore对象中。我们创建了一个ChatMemoryProvider对象,并且通过lambda表达式作为参数,用于返回ChatMemory对象。
在创建MessageWindowChatMemory对象时,对其指定一个id属性(本例使用用户id表示)。
发送聊天消息时,需要先根据用户id获取ChatMemory对象,然后将消息内容添加到ChatMemory中。本例使用的用户id是111,实际业务中需要通过相关的业务逻辑获取用户id。
持久化存储
上面的例子中,我们将聊天信息是存储在内存中的,如果需要进行持久化存储,用户只需要实现ChatMemoryStore接口即可。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package dev.langchain4j.store.memory.chat;
import dev.langchain4j.data.message.ChatMessage;
import java.util.List;
public interface ChatMemoryStore {
List<ChatMessage> getMessages(Object var1);
void updateMessages(Object var1, List<ChatMessage> var2);
void deleteMessages(Object var1);
}
该接口包含3个方法:
getMessage:根据用户id获取聊天数据列表,当调用ChatMemoryProvider的get方法时,会自动调用该方法。
updateMessages:向聊天列表中添加聊天数据,或者聊天列表达到上限需要淘汰数据时,会自动调用该方法。我们可以在这个方法中进行聊天数据的持久化存储。
deleteMessages:根据用户id删除聊天列表。
本例使用mysql存储聊天数据,使用hutool进行数据库的crud操作
mysql中新建表

导入jar
相对于之前的jar,增加了mysql驱动和hutool的jar。
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.0.0-beta2</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-zhipu-ai</artifactId>
<version>1.0.0-beta2</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.0.0-beta2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.30</version>
</dependency>
resource目录下增加db.setting文件
该文件中我们指定了连接数据库的驱动、用户名和密码登信息。hutool的数据库操作工具类,会自动加载该文件。
## db.setting文件
url = jdbc:mysql://localhost:3306/db2406
user = root
pass = root
## 可选配置
# 是否在日志中显示执行的SQL
showSql = true
# 是否格式化显示的SQL
formatSql = false
# 是否显示SQL参数
showParams = true
测试代码
package com.renr.langchain4jnew.app;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.renr.langchain4jnew.constant.CommonConstants;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import lombok.SneakyThrows;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;
public class App8 {
public static void main(String[] args) {
// 创建自定义持久化类对象
PersistentChatMemoryStore store = new PersistentChatMemoryStore();
// 通过chatMemoryStore(store)指定持久化对象
ChatMemoryProvider chatMemoryProvider = userId -> MessageWindowChatMemory.builder()
.id(userId)
.maxMessages(10)
.chatMemoryStore(store)
.build();
// 创建智谱的模型对象
ZhipuAiChatModel zhipuAiChatModel = ZhipuAiChatModel.builder()
// 模型key
.apiKey(CommonConstants.API_KEY)
// 精确度
.temperature(0.9)
.model("GLM-4-Flash")
.maxRetries(3)
.callTimeout(Duration.ofSeconds(60))
.connectTimeout(Duration.ofSeconds(60))
.writeTimeout(Duration.ofSeconds(60))
.readTimeout(Duration.ofSeconds(60))
// 请求的日志
//.logRequests(true)
// 响应数据的日志
//.logResponses(true)
.build();
// 删除原来的用户聊天数据
store.deleteMessages(111);
// 用户发送的消息
UserMessage firstUserMessage = UserMessage.from("你好,我叫张三");
// 将问题添加到chat memory
chatMemoryProvider.get(111).add(firstUserMessage);
// 发送消息时,直接通过ChatMemory获取聊天的上下文
AiMessage firstAiMessage = zhipuAiChatModel.chat(chatMemoryProvider.get(111).messages()).aiMessage();
System.out.println("第一次回答:" + firstAiMessage.text());
chatMemoryProvider.get(111).add(firstAiMessage);
// 用户发送的消息
UserMessage secondUserMessage = UserMessage.from("请问我叫什么名字?");
chatMemoryProvider.get(111).add(secondUserMessage);
// ai返回的额消息,这里将之前的消息也一并发送,ai根据上下文返回合适的信息
AiMessage secondAiMessage = zhipuAiChatModel.chat(chatMemoryProvider.get(111).messages()).aiMessage();
System.out.println("第二次回答:" + secondAiMessage.text());
chatMemoryProvider.get(111).add(secondAiMessage);
}
// 自定义实现持久化的类
static class PersistentChatMemoryStore implements ChatMemoryStore {
@SneakyThrows
@Override
public List<ChatMessage> getMessages(Object memoryId) {
Entity chatMsg = Db.use().get(Entity.create("chat_msg").set("uid", memoryId));
if (chatMsg != null) {
String message = chatMsg.getStr("message");
List<ChatMessage> chatMessages = messagesFromJson(message);
return chatMessages;
} else {
return new ArrayList<>();
}
}
@SneakyThrows
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
Entity chatMsg = Db.use().get(Entity.create("chat_msg").set("uid", memoryId));
if (chatMsg != null) {
Db.use().update(
Entity.create().set("message", messagesToJson(messages)), //修改的数据
Entity.create("chat_msg").set("uid", memoryId) //where条件
);
} else {
Db.use().insert(
Entity.create("chat_msg")
.set("uid", memoryId)
.set("message", messagesToJson(messages))
);
}
}
@SneakyThrows
@Override
public void deleteMessages(Object memoryId) {
Db.use().del(
Entity.create("chat_msg").set("uid", memoryId)//where条件
);
}
}
}
注意:Db.use()是hutool中提供的操作数据库的方法,关于该类的使用,我们可以才考hutool的官网,本文不再赘述。
执行后查看数据库数据

通过表中数据,可以看到聊天数据进行了正确的持久化。
需要说明的是,按照本例的写法,每次运行程序时都会先清空原来的聊天数据,并且按照json形式在一条记录中存储所有聊天记录。如果需要存储所有的聊天历史记录,读者可以自行对自定义的持久化类进行进一步的修改。
1130

被折叠的 条评论
为什么被折叠?



