chat - 聊天会话(对话)的记忆与持久化
2026年2月13日 下午9:40:07
大语言模型的接口是无状态的服务,如果需要形成有记忆的会话窗口。需要使用“多消息”提示语,把历史对话都输入。
1、使用“聊天会话”接口(ChatSession)
ChatSession 可以记录消息,还可以作为提示语的参数使用(直接输给 chatModel 的提示语,先输给 chatSession)。起到会话记忆的作用。
public void case3() throws IOException {
//聊天会话
ChatSession chatSession = InMemoryChatSession.builder()
.maxMessages(10)
.sessionId("session-1") //安排个会话id
.build();
//1.同步请求
chatModel.prompt("hello")
.session(chatSession) // 输入或输出的消息,自动记录到会话里(并从中获取历史消息)
.call();
//2.流式请求
chatModel.prompt("Who are you?")
.session(chatSession) // 输入或输出的消息,自动记录到会话里(并从中获取历史消息)
.stream();
}
2、ChatSession 内置实现对比
| 实现类 | 存储介质 | 适用场景 | 备注 |
|---|---|---|---|
| InMemoryChatSession | 本地内存 (Map) | 单机开发、单元测试、低频演示 | 临时性记忆,进程重启后数据即刻丢失 |
| FileChatSession | 本地 File | 本地客户端、CLI 智能体、单机工具 | 持久化到磁盘,程序关闭后记忆依然有效 |
| RedisChatSession | Redis 数据库 | 分布式环境、生产环境、高并发 | 多节点共享,确保分布式部署时记忆一致 |
生成情况复杂,可能需要进一步定制(可复制它们进行修改定制)
3、基于 Web 的聊天会话记忆参考
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.ChatSession;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.ai.chat.session.InMemoryChatSession;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Header;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.web.sse.SseEvent;
import reactor.core.publisher.Flux;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Controller
public class DemoController {
@Inject
ChatModel chatModel;
final Map<String, ChatSession> sessionMap = new ConcurrentHashMap<>();
//手动转为 sse
@Mapping("case3")
public Flux<SseEvent> case3(@Header("sessionId") String sessionId, String prompt) throws IOException {
ChatSession chatSession = sessionMap.computeIfAbsent(sessionId,
k -> InMemoryChatSession.builder().sessionId(k).build());
//注意:chatSession
return chatModel.prompt(prompt)
.session(chatSession)
.stream()
.filter(resp -> resp.hasContent())
.map(resp -> new SseEvent().data(resp.getContent()));
}
}
4、ChatMessage (消息)的序列化方法(可协助定制)
| 接口 | 描述 |
|---|---|
ChatMessage.toJson(message) | 把单条消息转为 json |
ChatMessage.fromJson(json) | 把 json 转为单条消息 |
ChatMessage.toNdjson(messages) | 把一批消息转为 ndjson |
ChatMessage.fromNdjson(ndjson) | 把 ndjson 转为一批消息 |
以上方法 v3.8.0 后支持。v3.8.0 前的可以 copy 里面的代码。
5、ChatSession 的接口字典(参考)
public interface ChatSession extends NonSerializable {
/**
* 获取会话id
*/
String getSessionId();
/**
* 获取消息
*/
List<ChatMessage> getMessages();
/**
* 添加消息
*/
default void addMessage(String userMessage) {
addMessage(ChatMessage.ofUser(userMessage));
}
/**
* 添加消息
*/
default void addMessage(ChatMessage... messages) {
addMessage(Arrays.asList(messages));
}
/**
* 添加消息
*/
default void addMessage(Prompt prompt) {
addMessage(prompt.getMessages());
}
/**
* 添加消息
*/
void addMessage(Collection<? extends ChatMessage> messages);
/**
* 是否为空
*/
boolean isEmpty();
/**
* 清空消息
*/
void clear();
/// //////////////////////////////////////
/**
* 转为 ndjson
*/
default String toNdjson() throws IOException {
return ChatMessage.toNdjson(getMessages());
}
/**
* 转为 ndjson
*/
default void toNdjson(OutputStream out) throws IOException {
ChatMessage.toNdjson(getMessages(), out);
}
/**
* 加载 ndjson
*/
default void loadNdjson(String ndjson) throws IOException {
ChatMessage.fromNdjson(ndjson, this::addMessage);
}
/**
* 加载 ndjson
*/
default void loadNdjson(InputStream ins) throws IOException {
ChatMessage.fromNdjson(ins, this::addMessage);
}
}