Agentic Memory (A-MEM)

repository·main·Indexed 22 days ago

https://github.com/agiresearch/a-mem

A library for implementing agentic memory in LLM applications using Zettelkasten principles. It features the AgenticMemorySystem for managing dynamic, interconnected memories via ChromaDB and LLM backends (OpenAI or Ollama). The system supports semantic search, automated memory evolution through connection analysis, and multiple retrieval strategies including PersistentChromaRetriever for cross-session memory and CopiedChromaRetriever for isolated sandboxing.

Tokens
4.3K
Snippets
12
Records
17
Agent score
77%

What's inside agentic-memory

  1. How Agentic Memory evolution works

    main

    Unlike traditional storage, Agentic Memory is dynamic. When you add or update a memory, the system automatically performs the following steps:

    1. Note Generation: Generates comprehensive notes with structured attributes.
    2. Contextualization: Creates contextual descriptions and tags.
    3. Connection Analysis: Analyzes historical memories for relevant connections using ChromaDB.
    4. Link Establishment: Creates meaningful links based on semantic similarities.
    5. Evolution: Updates metadata and context to ensure the knowledge network remains interconnected and refined.
  2. Install Agentic Memory

    main

    To use the Agentic Memory system, clone the repository and install the package via pip. It is recommended to use a virtual environment.

    # Clone the repository
    git clone https://github.com/agiresearch/A-mem.git
    cd A-mem
    
    # Create and activate a virtual environment
    python -m venv .venv
    source .venv/bin/activate  # On Windows, use: .venv\Scripts\activate
    
    # Install the package
    pip install .
    
    # For development, install in editable mode
    pip install -e .
    git clone https://github.com/agiresearch/A-mem.git
    cd A-mem
    python -m venv .venv
    source .venv/bin/activate
    pip install .
  3. How memory evolution works

    main

    The AgenticMemorySystem features an automated evolution mechanism. When evo_cnt reaches the evo_threshold, the system calls consolidate_memories() to refresh the vector store.

    During add_note, the system uses an LLM to decide if a new memory should trigger evolution. The LLM analyzes the new note against its nearest_neighbors_memories and can suggest two types of actions:

    1. strengthen: Creates connections between the new memory and existing ones by updating suggested_connections and tags_to_update.
    2. update_neighbor: Updates the context and tags of existing neighbor memories to better reflect the newly integrated knowledge.

    This process ensures the knowledge base is not just a collection of isolated facts, but an evolving, interconnected web of information.

  4. Initialize the AgenticMemorySystem

    main

    The AgenticMemorySystem class is the primary entry point for managing memories. It requires an embedding model for ChromaDB and an LLM backend for agentic operations.

    Parameters:

    • model_name (str): The name of the embedding model used for ChromaDB (e.g., 'all-MiniLM-L6-v2').
    • llm_backend (str): The LLM provider. Supported values are "openai" or "ollama".
    • llm_model (str): The specific LLM model name (e.g., 'gpt-4o-mini').
    from agentic_memory.memory_system import AgenticMemorySystem
    
    memory_system = AgenticMemorySystem(
        model_name='all-MiniLM-L6-v2',
        llm_backend="openai",
        llm_model="gpt-4o-mini"
    )
  5. Read and Search memories

    main

    Retrieve stored information using direct ID lookup or semantic search:

    • read(memory_id): Retrieves a memory object. The returned object contains content, tags, context, and keywords.
    • search_agentic(query, k=5): Performs an intelligent semantic search. Returns a list of dictionaries containing id, content, and tags for the top k results.
    # Get memory by ID
    memory = memory_system.read(memory_id)
    print(f"Content: {memory.content}")
    print(f"Tags: {memory.tags}")
    
    # Search memories semantically
    results = memory_system.search_agentic("neural networks", k=5)
    for result in results:
        print(f"ID: {result['id']}")
        print(f"Content: {result['content']}")
  6. Add, Update, and Delete memories

    main

    Use the following methods to manage individual memory entries in the system:

    • add_note(content, tags=None, category=None, timestamp=None): Creates a new memory. If metadata like tags, category, or timestamp (format YYYYMMDDHHmm) is provided, it is stored with the note. The system automatically generates context and keywords.
    • update(memory_id, content): Updates the content of an existing memory identified by memory_id.
    • delete(memory_id): Removes a memory from the system.
    # Simple addition
    memory_id = memory_system.add_note("Deep learning neural networks")
    
    # Addition with metadata
    memory_id = memory_system.add_note(
        content="Machine learning project notes",
        tags=["ml", "project"],
        category="Research",
        timestamp="202503021500"
    )
    
    # Update
    memory_system.update(memory_id, content="Updated content about deep learning")
    
    # Delete
    memory_system.delete(memory_id)
  7. OllamaController

    main

    The OllamaController implements BaseLLMController for local models running via Ollama. It uses litellm internally to facilitate the connection.

    Initialization:

    • model (str): The Ollama model name (e.g., `
  8. OpenAIController

    main

    The OpenAIController implements BaseLLMController specifically for OpenAI models.

    Initialization:

    • model (str): The OpenAI model name (e.g., `
  9. Search for memories

    main

    The system provides several ways to retrieve information based on a query string:

    • search(query, k=5): Performs hybrid retrieval returning a list of dictionaries containing id, content, context, keywords, and a similarity score.
    • search_agentic(query, k=5): A specialized search that retrieves relevant memories via ChromaDB and then includes their linked "neighbor" memories (those explicitly connected via links) to provide a richer context. Returns a list of dictionaries with an is_neighbor boolean flag.
    • find_related_memories(query, k=5): Returns a formatted string of related memories and their indices, useful for feeding into an LLM prompt.
    # Standard hybrid search
    results = memory_system.search("foxes and dogs", k=3)
    for res in results:
        print(f"ID: {res['id']}, Content: {res['content']}")
    
    # Agentic search (includes neighbors)
    agentic_results = memory_system.search_agentic("foxes and dogs", k=5)
  10. Add a new memory note

    main

    Use add_note(content, time=None, **kwargs) to insert information into the system.

    When a note is added, the system automatically:

    1. Creates a MemoryNote object.
    2. Analyzes the content to extract semantic metadata (keywords, context, tags).
    3. Checks if the new memory should trigger an "evolution" (updating existing memories or strengthening connections) based on its similarity to existing notes.
    4. Stores the note in both a local dictionary and a ChromaDB vector store for retrieval.

    Returns the unique id of the created note.

    # Simple addition
    note_id = memory_system.add_note("The quick brown fox jumps over the lazy dog.")
    
    # Addition with metadata
    note_id = memory_system.add_note(
        "The quick brown fox jumps over the lazy dog.",
        category="Animals",
        tags=["nature", "speed"]
    )
  11. Use CopiedChromaRetriever for isolated memory sandboxing

    main

    The CopiedChromaRetriever creates an isolated, temporary copy of an existing ChromaDB collection. This is ideal for giving an agent a 'snapshot' of a shared memory base that it can modify without affecting the original source.

    How it works

    1. It connects to a source directory and collection.
    2. It creates a new temporary directory using tempfile.TemporaryDirectory.
    3. It clones all documents, metadatas, and embeddings from the source to the temporary destination.
    4. It automatically cleans up the temporary directory and deletes the cloned collection when the object is closed or goes out of scope (via __exit__).

    Initialization Parameters

    • directory (Optional[str]): Path to the source ChromaDB storage.
    • collection_name (str): Name of the source collection to copy.
    • _dest_collection_name (Optional[str]): Optional name for the new collection. Defaults to {collection_name}__clone.
    • _copy_batch_size (int): Number of documents to copy per batch (default 10).
    from agentic_memory.retrievers import CopiedChromaRetriever
    
    # Create an isolated sandbox from the main memory
    with CopiedChromaRetriever(directory="~/.chromadb", collection_name="shared_mem") as sandbox:
        # Sandbox can add/delete documents without affecting the source
        sandbox.add_document("Sandbox note", {}, "temp_id")
        results = sandbox.search("shared info")
        print(results)
    
    # Once the 'with' block exits, the sandbox is automatically destroyed.
  12. Request structured JSON responses with get_completion

    main

    The get_completion method in LLMController supports structured output via the response_format parameter. This is particularly useful for metadata generation where you need the LLM to return a valid JSON object following a specific schema.

    • prompt (str): The input text.
    • response_format (dict, optional): A dictionary defining the JSON schema. For OpenAI, this typically follows the OpenAI JSON schema format.
    • temperature (float, optional): Controls randomness. Defaults to 0.7.
    # Example of requesting a specific JSON structure
    response_format = {
        "type": "json_schema",
        "json_schema": {
            "name": "memory_metadata",
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {"type": "string"},
                    "importance": {"type": "number"}
                },
                "required": ["topic", "importance"]
            }
        }
    }
    
    response = controller.get_completion(
        prompt="Extract metadata from: The meeting about project X was very important.",
        response_format=response_format
    )