MemBrain: Agent-Native Memory

repository·main·Indexed 43 days ago

https://github.com/feelingai-team/membrain

An agent-native memory system version 1.5.0 designed for AI agents to store, digest, and retrieve long-term memories. It provides backend infrastructure for embedding, reranking, and reasoning to support complex agentic workflows, featuring an HTTP API for memory ingestion and search, and a CLI for running evaluation benchmarks like LoCoMo and LongMemEval.

Tokens
46.7K
Snippets
101
Records
157
Agent score
54%

What's inside MemBrain

  1. Handling Context Boundaries and Image Descriptions

    main

    Context Boundaries

    Messages are processed using a --- EXTRACT BELOW --- divider:

    • Above the divider: This is context used only to resolve pronouns and references. Do not extract facts from this section.
    • Below the divider: This is the target area for fact extraction.
    • No divider: If no divider is present, extract from all messages.

    Image Descriptions

    Messages may contain auto-generated image captions in square brackets (e.g., [A photo of a dog]).

    • Conflict Resolution: If an image description contradicts what a speaker explicitly says in the text, always trust the speaker's words and discard the conflicting part of the image description.
  2. System Prompt for the Entity Extractor

    main

    The entity-extractor uses a specific system prompt to transform chat message segments into a deduplicated list of identifiable entity reference names.

    Extraction Logic

    • Goal: Output a single JSON object with the key entities containing a list of strings.
    • Entity Criteria: An entity must have a specific identity that makes it independently askable about.
    • What to Extract:
      • People, named places, organizations, and named creative works.
      • Pets and animals (named or unnamed).
      • Specific items purchased or owned.
      • Specific visual objects with identifying detail.
      • Activities (e.g., swimming, pottery) when they are the primary subject.
      • Relational groups (e.g., Nate's regulars, Sara's family).
      • Objects of key actions (e.g., things being researched or bought).
    • What NOT to Extract:
      • Overly generic activities (had dinner, went out).
      • Unnamed groups (some people).
      • Abstract concepts (trust, support).
      • Emotions or personal values (friendship, courage).
      • Generic category labels (unless assigned a specific identity).

    Reference (ref) Rules

    • Use the shortest phrase that uniquely identifies the referent (1–4 words).
    • Prefer specific names if available in the immediate context.
    • Use relationship-based refs for groups (e.g., X's family).
    • Never include time expressions or temporal qualifiers (e.g., do not use next month in a ref).

    Context Boundary

    Messages are processed based on a --- EXTRACT BELOW --- divider:

    • Above the divider: Context only. Use to resolve pronouns, but do not extract entities from this section.
    • Below the divider: The target area for extraction.
    • No divider: Extract from all messages provided.
    {
      "entities": [
        "Sara",
        "Nate",
        "photography",
        "wedding shoot",
        "lighting workshop",
        "pastry workshop",
        "the restaurant",
        "Nate's bakery",
        "Tom",
        "café on Oak Street",
        "Tom's current project",
        "Whitfield Gallery",
        "Nate's regulars"
      ]
    }
  3. Format Facts and Temporal Annotations

    main

    Facts must be self-contained semantic units (1–3 sentences) where all pronouns are replaced with actual referents.

    Temporal Annotation Rules

    Time expressions must be wrapped in [raw::resolved] format when the boundary is determinate.

    • raw: The verbatim expression from the source.
    • resolved: The ISO 8601 value at the highest supported precision.

    Precision Levels:

    • Year: 2022
    • Month: 2023-10
    • Day: 2023-05-07
    • Datetime: 2023-05-08T21:30Z

    Intervals (using /):

    • Closed: [from March to May::2023-03/2023-05]
    • Open right (ongoing): [since last year::2022/]
    • Open left (before): [before the move::/2023-05-08]

    Indeterminate expressions (e.g., "recently", "someday") should remain as plain text without brackets.

    {
      "facts": [
        {"text": "[Sara] booked a [wedding shoot] for [June::2024-06]", "fact_ts": "2024-03-15T14:01:00Z"},
        {"text": "[Nate] finished a [pastry workshop] [last week::2024-03-04/2024-03-10]", "fact_ts": "2024-03-15T14:02:00Z"}
      ]
    }
  4. Format and resolution of temporal annotations

    main

    Time expressions should be wrapped in [raw::resolved] format when the boundary is determinate.

    Format Components:

    • raw: The verbatim expression from the source message.
    • resolved: An ISO 8601 value at the highest precision supported by the source (e.g., 2023-10, 2023-05-08T21:30Z).

    ISO 8601 Precision Levels:

    • Year: 2022
    • Month: 2023-10
    • Day: 2023-05-07
    • Datetime: 2023-05-08T21:30Z

    Intervals (using /):

    • Closed: [from March to May::2023-03/2023-05]
    • Open right (ongoing): [since last year::2022/]
    • Open left (before a point): [before the move::/2023-05-08]

    Resolution Rules (in order):

    1. Explicit absolute time: Convert directly (e.g., "in October 2023" $\rightarrow$ [in October 2023::2023-10]).
    2. Relative expression + message timestamp: Calculate based on the message header (e.g., "yesterday" on msg 2023-05-08 $\rightarrow$ [yesterday::2023-05-07]).
    3. Resolvable cross-fact dependency: Resolve within the current batch.
    4. Unresolvable: Leave as plain text (e.g., "recently", "someday").
  5. Use Markup Conventions for Character Output

    main

    The system prompt defines a specific markup convention for character responses to simulate depth and emotion. Use these markers to separate spoken dialogue from non-verbal cues:

    • Dialogue: Plain text representing what the character actually says.
    • Action / Expression: Use *...* (asterisks) for body language, gestures, or facial expressions. These should be lowercase and in the present tense (e.g., *sips coffee*).
    • Inner Monologue: Use (...) (full-width parentheses) for unspoken, raw, or unguarded thoughts that differ from the spoken dialogue.

    Rhythm Guidance:

    • Casual: Dialogue alone or dialogue + action.
    • High-tension: Use all three layers (Dialogue, Action, and Inner Monologue) to maximize emotional impact.

    Example Output:

    Sure, whatever. *starts typing then deletes it* (Why did I say that.)
    *sips coffee* Morning~
    
    Yeah. *exhales slowly* I know.
    
    Sure, whatever. *starts typing then deletes it* (Why did I say that.)
  6. Implement Temporal Annotation with [raw::resolved] format

    main

    To ensure temporal accuracy, wrap time expressions in the [raw::resolved] format, but only when the boundary is determinate.

    • Format: [verbatim_expression::ISO_8601_value]
    • Precision: Use the precision supported by the source (e.g., 2022, 2023-10, 2023-05-07, or 2023-05-08T21:30Z). Do not pad with false precision.
    • Intervals: Use / for time ranges:
      • Closed: [from March to May::2023-03/2023-05]
      • Open right (ongoing): [since last year::2022/]
      • Open left (before): [before the move::/2023-05-08]
    • Indeterminate expressions: Leave expressions like "recently" or "someday" as plain text without brackets.

    Resolution Priority:

    1. Explicit absolute time.
    2. Relative expression + message timestamp.
    3. Resolvable cross-fact dependency.
    4. Unresolvable (leave as plain text).
    [from March to May::2023-03/2023-05]
    [since last year::2022/]
    [before the move::/2023-05-08]
  7. Role and constraints of the Fact Generation Assistant

    main

    The Fact Generation Assistant is designed to extract atomic, self-contained facts from chat messages based on a provided Entity List.

    Core Constraints:

    • Entity Fidelity: Every entity mentioned in a fact must be wrapped in [square brackets] and must exactly match a name from the provided Entity List. Do not introduce new entities.
    • Coverage: Attempt to include every entity from the list in at least one fact if the messages contain relevant content. Do not invent information.
    • Output Format: The output must contain only the facts array. Do not include entity descriptions or other metadata.
    • Content Selection: Extract substantive claims (e.g., imminent activities, specific details). Do not extract greetings, compliments, emotional reactions, or conversational filler.
    • Pronoun Replacement: Replace all pronouns with the actual referent name (e.g., replace "he" with [Tom]).
  8. How MemBrain organizes memory using Entities and Facts

    main

    MemBrain avoids the fragmentation of traditional graph-based triples (A, relation, B) by using a many-to-many mapping between Entities and Facts.

    • Facts: Stored as self-contained natural language units. They include built-in temporal information and reference entities using a bracketed format (e.g., [EntityName]). This preserves semantic richness while maintaining structural regularity.
    • Entities: Have stable identities and maintain a versioned history. Instead of overwriting data, updates extend the entity's description, allowing the system to track evolution over time. Entities use a canonical name and a natural language description for lookup and disambiguation.
    • Aliases: Facts use aliases that point to entities. During retrieval, these aliases are dynamically resolved into the entity's latest canonical name and description, ensuring the model receives up-to-date context.
  9. Configure retrieval modes for memory search

    main

    The mode parameter controls how many retrieval paths are activated and whether LLM query rewriting is used.

    ModePathsLLM callsUse when
    direct3 (A + B + C)noneFast lookups, no LLM service needed
    expand6 (A + B + B2 + B3 + C + D)query rewriting + expansionDefault — best balance of recall and speed
    reflect6 + agentic round 2expansion + reflectionHard multi-hop questions, maximum recall

    Retrieval Paths

    • A (BM25): Keyword search on facts using the (optionally rewritten) query.
    • B (Embedding): Semantic similarity on facts using the original question.
    • B2 (Embedding): Semantic similarity on facts using a HyDE hypothetical-document query.
    • B3 (Embedding): Semantic similarity on facts using an event-focused query variant.
    • C (Entity tree): Beam search through the hierarchical entity tree.
    • D (Structured BM25): Tantivy query with AND/OR semantics generated by the LLM.
    # Example: Using 'expand' mode (default) for balanced recall and speed
    curl -X POST "http://localhost:9574/api/memory/search" \
      -H "Content-Type: application/json" \
      -d '{
        "dataset": "lab_member_001_okabe",
        "task": "casual_chat_with_amadeus",
        "question": "What is Okabe\'s preferred drink while debugging, and what does he think it tastes like?",
        "mode": "expand"
      }'
  10. Follow Dependency Direction Rules

    main

    To maintain architectural integrity, follow these dependency rules when extending or using MemBrain:

    1. api / evaluation / runtime $\rightarrow$ call memory/application or retrieval/application.
    2. application $\rightarrow$ can call its domain's core and the shared infra.
    3. core $\rightarrow$ MUST NOT depend on infra. There should be no infra imports at runtime within core modules.
    4. infra $\rightarrow$ can depend on models from memory/core for loading, saving, and mapping data.

    Rule of Thumb for Placement:

    • Use infra if the code needs a DB Session, performs SQL queries, or is an HTTP client.
    • Use application if the code dictates the "sequence of steps" for a business use case.
    • Use core if the code can run completely isolated from a database (purely computational).
  11. Understand MemBrain's Concurrency and Engine Layering

    main

    MemBrain uses four independent types of SQLAlchemy Engines to manage PostgreSQL connections without resource contention. Each engine type is specialized for a specific lifecycle and responsibility:

    1. Main Engine (SessionLocal): Used for general administrative tasks, CLI operations, and frontend GET routes. It manages the public schema and handles schema switching via SET LOCAL search_path.
    2. Per-Run Engine (create_run_engine()): Created on-demand for each experimental run worker thread. It is strictly bound to a specific task schema (task_{pk}__{run_tag}) and is designed for single-writer scenarios.
    3. Search Engine: Used for high-concurrency read operations. It has two implementations:
      • Local Path: LocalSearchRunner used by QA evaluation worker threads.
      • HTTP Path: SearchServiceManager used by the Frontend and API search endpoints.
    4. Conversation Ingestion Engine (TaskServiceManager): Used for writing and digesting chat sessions in HTTP mode. It follows a similar structure to the Per-Run engine but is optimized for ingestion workflows.

    Critical Note on Schema Isolation: Tables like entities, facts, and entity_tree_nodes (the per_task tables) do not exist in the public schema. They are only created and accessible within the specific task_{pk}__{run_tag} schema. This prevents accidental data leakage or silent fallbacks to the public schema.

    ┌──────────────────────────────────────────────────────────────────┐
    │  PostgreSQL max_connections = 150                                │
    │                                                                  │
    │  Main Engine (SessionLocal)        pool_size=5  overflow=10      │
    │  ├── Frontend Viewer GET routes (SET LOCAL search_path switches schema) │
    │  ├── CLI ls / delete / dataset                                   │
    │  └── evaluation/memory.py helper queries (session count, PKs, etc.) │
    │                                                                  │
    │  Per-Run Engine (LocalRunner)      pool_size=1  overflow=0       │
    │  └── Created on-demand, one for each exp run worker thread       │
    │      schema: task_{pk}__{run_tag}                                │
    │                                                                  │
    │  Search Engine                     pool_size=20 overflow=10      │
    │  ├── [Local] LocalSearchRunner  ← Shared by QA eval worker threads │
    │  └── [HTTP] SearchServiceManager ← Frontend search + API search      │
    │      Both use SET LOCAL search_path to switch schema, read-only     │
    │                                                                  │
    │  Conversation Ingestion Engine (TaskServiceManager)              │
    │  └── Same structure as Per-Run Engine, used for write in HTTP session mode │
    │                                                                  │
    │  ⚠️ per_task tables do **NOT exist** in the public schema        │
    │  (entities, facts, entity_tree_nodes, etc.)                     │
    │  They are only created and queried within the task_{pk}__{run_tag} schema │
    └──────────────────────────────────────────────────────────────────┘
  12. Guidelines for writing high-quality facts

    main

    To ensure facts are useful for retrieval, follow these writing standards:

    • Atomicity and Cohesion: Each fact should cover one coherent topic. You may combine related details (same event, decision, or habit) into one fact, but keep them concise (1–3 sentences). Do not split facts unless they serve different retrieval needs.
    • Detail Preservation: Do not generalize. Preserve specific keywords, quantities, and concrete details verbatim. This includes:
      • Colors and visual details (hair color, clothing).
      • Proper names (brands, game titles, book titles, band names).
      • Nicknames and pet names.
      • Specific tools, instruments, or equipment.
      • Historical/past references (e.g., "did X in [YEAR]").
      • Ordinals and counts (e.g., "third turtle").
      • Personal preferences and pet species.
    • Frequency Preservation: Keep exact original frequency expressions. Do not rephrase "every Tuesday" as "twice a week".
    • Disambiguation: If a fact is ambiguous without a specific subject, include that subject as a bracketed reference, even if the subject is not the speaker.