How ChocolateLMLite Memory Functionality Works for Long Conversations
ChocolateLMLite implements persistent long-conversation memory by storing per-persona facts in a local YAML file that the LLM reads and updates through thread-safe file operations, injecting stored entries directly into the system prompt on every request.
ChocolateLMLite provides a lightweight yet robust mechanism for maintaining context across extended dialogues through its persistent memory functionality. This architecture allows LLM-driven agents to remember facts, preferences, and history beyond the constraints of the active token window. According to the gpsnmeajp/chocolatelmlite source code, the implementation relies on a simple YAML-based storage layer with file-locking concurrency controls and seamless prompt injection.
Memory Storage Architecture
The memory system persists data in a per-persona YAML file located at data/persona_{id}/memory.yaml within the application directory. Each persona maintains its own isolated memory space, ensuring that facts learned during one conversation do not leak into another persona's context.
The underlying data structure uses a YamlMemory object containing a list of YamlMemoryEntry instances. Every entry tracks four key properties:
Id: Unique integer identifier for the entryText: The actual memory content (capped at 500 characters for new entries)CreatedAt: Timestamp when the entry was first createdUpdatedAt: Timestamp of the most recent modification
Loading Memory from Disk
When the system needs to retrieve stored facts, FileManager.GetActivePersonaMemory() constructs the path to memory.yaml and deserializes the contents into a YamlMemory instance. If the file does not exist, the method returns an empty memory object with an initialized but empty entry list.
This operation occurs in src/FileManager.cs at lines 609-618, providing the foundation for both prompt construction and API responses.
CRUD Operations and Thread Safety
All write operations pass through a centralized locking mechanism to prevent data corruption during concurrent LLM requests.
Upserting Memory Entries
The FileManager.UpsertActivePersonaMemory(int id, string newContent) method handles both creation and updates in src/FileManager.cs (lines 624-664). The method logic follows these rules:
- When
idequals 0, the system creates a new entry with an auto-incremented identifier - When
idmatches an existing entry, the method overwrites theTextandUpdatedAtfields - All changes persist immediately to disk via
SaveYaml
Deleting Entries
To remove specific facts, FileManager.RemoveActivePersonaMemory(int id) filters the entry list by the supplied identifier and rewrites the YAML file without the matching record. This implementation resides at lines 671-686 in src/FileManager.cs.
File Locking for Concurrency
Concurrent updates pose a risk when multiple LLM tool calls execute simultaneously. The FileManager.WithMemoryLock(Func<Task<T>> action) method implements a lock-file pattern using memory.yaml.lock:
- Creates a temporary lock file and waits for existing locks to clear
- Executes the supplied async lambda containing the read-modify-write operation
- Deletes the lock file upon completion
This safeguard appears in src/FileManager.cs at lines 803-867, ensuring atomic memory updates even under high concurrency.
Integrating Memory into LLM Prompts
Storage alone does not provide long-term context; the system must inject memories into the active prompt. During prompt construction, SystemPrompt.BuildSystemPrompt (lines 22-30 in src/SystemPrompt.cs) retrieves the current memory entries and formats them as XML-style tags:
<system>
<memory>ユーザーは猫が好きです</memory>
<memory>猫は黒と白が好きです</memory>
</system>
The concatenated memory entries append directly to the system prompt, giving the model immediate access to persistent facts without consuming the sliding context window.
API and Tool Interfaces
ChocolateLMLite exposes memory functionality through both REST endpoints and LLM-callable tools.
REST API Endpoint
The web server provides read-only access via GET /api/persona/active/memory, implemented in src/WebServer.cs at lines 525-529. This endpoint returns the raw YamlMemory JSON, enabling external dashboards or debugging tools to inspect the current knowledge base:
curl http://localhost:8010/api/persona/active/memory
LLM Tool Interface
The UpdatePersonaMemory tool in src/Tools.cs (lines 15-42) serves as the primary interface for the LLM to modify its own memory. The tool accepts two parameters:
id: 0 for new entries, or existing ID for updatesnewContent: The text to store (enforced 500-character limit for new entries)
When invoked, the tool delegates to WithMemoryLock and returns a confirmation string ("メモリを更新しました。") that the LLM can relay to users.
Practical Implementation Examples
The following patterns demonstrate common memory operations using the ChocolateLMLite API.
Retrieving current memory:
var memory = fileManager.GetActivePersonaMemory();
// memory.MemoryEntries is List<YamlMemoryEntry>
Adding a new fact with concurrency protection:
await fileManager.WithMemoryLock(async () =>
{
// id = 0 creates new entry
return fileManager.UpsertActivePersonaMemory(0, "ユーザーは猫が好きです");
}, Program.cts.Token);
Updating existing knowledge:
await fileManager.WithMemoryLock(async () =>
{
return fileManager.UpsertActivePersonaMemory(3, "猫は黒と白が好きです");
}, Program.cts.Token);
Removing outdated information:
await fileManager.WithMemoryLock(async () =>
{
return fileManager.RemoveActivePersonaMemory(2);
}, Program.cts.Token);
Tool call format for LLM integration:
{
"function": "UpdatePersonaMemory",
"arguments": {
"id": 0,
"newContent": "ユーザーは来週旅行に行く予定です"
}
}
Summary
- ChocolateLMLite stores long-conversation memory in per-persona YAML files (
data/persona_{id}/memory.yaml) withId,Text,CreatedAt, andUpdatedAtfields - The
FileManagerclass provides thread-safe CRUD operations throughGetActivePersonaMemory,UpsertActivePersonaMemory, andRemoveActivePersonaMemory - Concurrency protection uses file locking via
WithMemoryLockto prevent race conditions during simultaneous updates - Memories inject into the active prompt as XML
<memory>tags throughSystemPrompt.BuildSystemPrompt, ensuring the LLM retains context across turns - External access is available via the REST endpoint
GET /api/persona/active/memory, while LLM agents modify memory through theUpdatePersonaMemorytool with a 500-character content limit
Frequently Asked Questions
Where does ChocolateLMLite store conversation memory?
ChocolateLMLite persists memory in a YAML file named memory.yaml located inside the active persona's data directory at data/persona_{id}/. This file stores a serialized YamlMemory object containing a list of entries, ensuring data survives server restarts and remains isolated per persona.
How does the system prevent memory corruption during concurrent updates?
The implementation uses FileManager.WithMemoryLock, which creates a temporary lock file (memory.yaml.lock) before executing any read-modify-write operations. This mechanism queues concurrent requests and processes them sequentially, preventing race conditions when multiple LLM tool calls attempt simultaneous modifications.
What is the maximum length for a memory entry?
The UpdatePersonaMemory tool enforces a 500-character limit on new content to prevent the system prompt from growing excessively large. This constraint appears in src/Tools.cs and ensures that injected <memory> tags do not consume the majority of the model's available context window.
How does the LLM access stored memories during a conversation?
During prompt construction, SystemPrompt.BuildSystemPrompt retrieves all current entries via GetActivePersonaMemory and formats them as XML-style <memory> tags appended to the system prompt. This injection happens on every request, giving the model immediate access to persistent facts without requiring explicit retrieval tool calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →