Memory
The MemoryAgent provides a mechanism for agents to maintain state and remember information across interactions. It acts as a specialized orchestrator that does not process messages directly but manages memory operations through two key components: a Recorder for storing information and a Retriever for recalling it. This separation of concerns allows for flexible and pluggable memory storage solutions.
Core Components#
The memory system is comprised of three main classes:
MemoryAgent: The central agent that manages memory operations. It is configured with a recorder and a retriever and providesrecord()andretrieve()methods to interact with the memory store.MemoryRecorder: An agent responsible for writing information to a persistent storage backend (e.g., a database, file system, or vector store). You must provide the implementation for how and where the data is stored.MemoryRetriever: An agent responsible for fetching information from the storage backend based on specified criteria, such as a search query or a limit. You must provide the implementation for the retrieval logic.
How It Works#
The MemoryAgent delegates tasks to its subordinate agents. When you call the record() method on a MemoryAgent, it invokes its configured MemoryRecorder to persist the data. Similarly, when you call retrieve(), it invokes the MemoryRetriever to query and return the stored information.
This architecture allows developers to define custom storage and retrieval logic without altering the core agent workflows. For example, you can implement a recorder that saves conversation history to a PostgreSQL database and a retriever that uses vector embeddings to find semantically similar past interactions.
MemoryAgent#
The MemoryAgent is the primary interface for memory management. It is not designed to be called directly within a chain of processing agents but rather as a stateful service available to other agents or your application logic.
Configuration#
To create a MemoryAgent, you provide it with a recorder and a retriever. These can be instances of MemoryRecorder and MemoryRetriever or function definitions for their respective process methods.
Agent Initialization
import { MemoryAgent, MemoryRecorder, MemoryRetriever } from "@aigne/core";
import { v7 as uuidv7 } from "@aigne/uuid";
// 1. Define a simple in-memory store for demonstration
const memoryStore: Map<string, any> = new Map();
// 2. Implement the recorder logic
const recorder = new MemoryRecorder({
async process({ content }) {
const memories = content.map((item) => {
const memory = {
id: uuidv7(),
content: item,
createdAt: new Date().toISOString(),
};
memoryStore.set(memory.id, memory);
return memory;
});
return { memories };
},
});
// 3. Implement the retriever logic
const retriever = new MemoryRetriever({
async process({ search, limit = 10 }) {See all 15 lines
The example above demonstrates creating a MemoryAgent with a simple in-memory storage mechanism. In a production environment, you would replace this with a more robust solution like a database.
MemoryAgentOptions#
The agent or function responsible for storing memories. It can be a full MemoryRecorder instance, a configuration object, or just the processing function.
The agent or function responsible for retrieving memories. It can be a full MemoryRetriever instance, a configuration object, or just the processing function.
If true, the agent will automatically record information after completing operations to create a history of interactions.
MemoryRecorder#
The MemoryRecorder is an abstract agent class that defines the contract for storing memories. You must provide a concrete implementation of its process method.
MemoryRecorderInput#
The process method of a MemoryRecorder receives a MemoryRecorderInput object.
An array of objects to be stored as memories. Each object can contain an input, output, and source to contextualize the memory.
MemoryRecorderOutput#
The process method must return a MemoryRecorderOutput object.
MemoryRetriever#
The MemoryRetriever is an abstract agent class that defines the contract for fetching memories from storage. You must provide a concrete implementation of its process method.
MemoryRetrieverInput#
The process method of a MemoryRetriever receives a MemoryRetrieverInput object to filter and limit the results.
The maximum number of memories to return. Useful for pagination or keeping context windows small.
A search term or message object to filter memories. The implementation determines how this value is used (e.g., keyword search, vector similarity).
MemoryRetrieverOutput#
The process method must return a MemoryRetrieverOutput object.
Usage Example#
Once the MemoryAgent is configured, you can use it within your application's context to record and retrieve information.
AIGNE Interaction
import { AIGNE } from "@aigne/core";
// Assuming memoryAgent is configured as shown in the first example
const aigne = new AIGNE({
// ...other configurations
});
async function run() {
// Record a new memory
const recordedMemory = await aigne.invoke(memoryAgent.record.bind(memoryAgent), {
content: [{ input: { query: "What is the capital of France?" } }],
});
console.log("Recorded:", recordedMemory.memories[0].id);
// Retrieve memories
const retrievedMemories = await aigne.invoke(memoryAgent.retrieve.bind(memoryAgent), {
search: "France",
limit: 5,
});
console.log("Retrieved:", retrievedMemories.memories);
}
run();This example shows how to use the aigne.invoke method to call the record and retrieve functions on the memoryAgent instance, effectively managing the agent's state across interactions.
Summary#
The MemoryAgent provides a powerful and flexible abstraction for managing state in agentic applications. By separating the orchestration (MemoryAgent) from the implementation details (MemoryRecorder, MemoryRetriever), you can easily integrate various storage backends, from simple in-memory arrays to sophisticated vector databases.
For more information on the core execution engine, see the AIGNE documentation. To understand the fundamental building block of work, refer to the Agents page.