docs/docs/tutorials/chat-memory.md
Maintaining and managing ChatMessages manually is cumbersome.
Therefore, LangChain4j offers a ChatMemory abstraction along with multiple out-of-the-box implementations.
ChatMemory can be used as a standalone low-level component,
or as a part of a high-level component like AI Services.
ChatMemory acts as a container for ChatMessages (backed by a List), with additional features like:
SystemMessagePlease note that "memory" and "history" are similar, yet distinct concepts.
LangChain4j currently offers only "memory", not "history". If you need to keep an entire history, please do so manually.
An eviction policy is necessary for several reasons:
Currently, LangChain4j offers 2 out-of-the-box implementations:
MessageWindowChatMemory, functions as a sliding window,
retaining the N most recent messages and evicting older ones that no longer fit.
However, because each message can contain a varying number of tokens,
MessageWindowChatMemory is mostly useful for fast prototyping.TokenWindowChatMemory,
which also operates as a sliding window but focuses on keeping the N most recent tokens,
evicting older messages as needed.
Messages are indivisible. If a message doesn't fit, it is evicted completely.
TokenWindowChatMemory requires a TokenCountEstimator to count the tokens in each ChatMessage.By default, ChatMemory implementations store ChatMessages in memory.
If persistence is required, a custom ChatMemoryStore can be implemented
to store ChatMessages in any persistent store of your choice:
class PersistentChatMemoryStore implements ChatMemoryStore {
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// TODO: Implement getting all messages from the persistent store by memory ID.
// ChatMessageDeserializer.messageFromJson(String) and
// ChatMessageDeserializer.messagesFromJson(String) helper methods can be used to
// easily deserialize chat messages from JSON.
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
// TODO: Implement updating all messages in the persistent store by memory ID.
// ChatMessageSerializer.messageToJson(ChatMessage) and
// ChatMessageSerializer.messagesToJson(List<ChatMessage>) helper methods can be used to
// easily serialize chat messages into JSON.
}
@Override
public void deleteMessages(Object memoryId) {
// TODO: Implement deleting all messages in the persistent store by memory ID.
}
}
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.id("12345")
.maxMessages(10)
.chatMemoryStore(new PersistentChatMemoryStore())
.build();
The updateMessages() method is called every time a new ChatMessage is added to the ChatMemory.
This usually happens twice during each interaction with the LLM:
once when a new UserMessage is added and again when a new AiMessage is added.
The updateMessages() method is expected to update all messages associated with the given memory ID.
ChatMessages can be stored either separately (e.g., one record/row/object per message)
or together (e.g., one record/row/object for the entire ChatMemory).
:::note
Please note that messages evicted from ChatMemory will also be evicted from ChatMemoryStore.
When a message is evicted, the updateMessages() method is called
with a list of messages that does not include the evicted message.
:::
The getMessages() method is called whenever the user of the ChatMemory requests all messages.
This typically happens once during each interaction with the LLM.
The value of the Object memoryId argument corresponds to the id specified
during the creation of the ChatMemory.
It can be used to differentiate between multiple users and/or conversations.
The getMessages() method is expected to return all messages associated with the given memory ID.
The deleteMessages() method is called whenever ChatMemory.clear() is called.
If you do not use this functionality, you can leave this method empty.
SystemMessageSystemMessage is a special type of message, so it is treated differently from other message types:
SystemMessage is always retained.SystemMessage can be held at a time.SystemMessage with the same content is added, it is ignored.SystemMessage with different content is added, it replaces the previous one.
By default, the new SystemMessage is added to the end of the message list. You can change this by setting
alwaysKeepSystemMessageFirst property when creating ChatMemory.If an AiMessage containing ToolExecutionRequests is evicted,
the following orphan ToolExecutionResultMessage(s) are also automatically evicted
to avoid problems with some LLM providers (such as OpenAI)
that prohibit sending orphan ToolExecutionResultMessage(s) in the request.
AiServices:
Chains
All supported chat memory stores can be found here.