ƒxyzƒxyz Network
The NetworkKnowledge Graph

Graphiti Temporal Memory

Bi-temporal knowledge graph for AI agent memory with Neo4j integration

Graphiti Temporal Memory

Status: Production | Version: Neo4j 5.15+ | Integration: Letta AI Agents

Graphiti provides temporal knowledge graph capabilities for Fixie AI agents, enabling sophisticated memory management with bi-temporal tracking, entity deduplication, and semantic search.


Overview

What is Graphiti?

Graphiti is a temporal knowledge graph system that stores and retrieves facts with full time awareness:

FeatureDescription
Bi-Temporal TrackingWhen facts became true (event time) vs when they were recorded (transaction time)
Entity DeduplicationIntelligent merging of entities across episodes
Community DetectionAutomatic clustering of related entities
Vector EmbeddingsSemantic search across the knowledge graph
Group IsolationMulti-tenant memory separation by agent/member/project

Why Graphiti for Fixies?

Traditional LLM memory is either:

  • Short-term (context window): Limited to current conversation
  • Long-term (vector store): No relationship awareness

Graphiti combines both:

  • Entities extracted from conversations
  • Relationships between entities
  • Temporal validity tracking
  • Semantic search for recall

Architecture

Schema Overview

Graphiti Memory Architecture:
┌─────────────────────────────────────────────────────────────┐
│                    Graphiti Subgraph                         │
│                                                              │
│  ┌─────────────────┐    ┌─────────────────────────────┐    │
│  │ GraphitiEpisode │    │      GraphitiEntity         │    │
│  │                 │    │                             │    │
│  │ - Raw messages  │───▶│ - Extracted entities       │    │
│  │ - Source text   │    │ - Deduplicated             │    │
│  │ - Timestamps    │    │ - Vector embeddings        │    │
│  └─────────────────┘    └──────────┬──────────────────┘    │
│                                    │                        │
│                         GRAPHITI_RELATES_TO                 │
│                         (bi-temporal edges)                 │
│                                    │                        │
│                         ┌──────────▼──────────┐            │
│                         │  GraphitiCommunity  │            │
│                         │  (entity clusters)  │            │
│                         └─────────────────────┘            │
└─────────────────────────────────────────────────────────────┘

                   GRAPHITI_LINKS_TO


┌─────────────────────────────────────────────────────────────┐
│                    Domain Subgraph                           │
│  (Member, Circle, Role, Transaction, etc.)                  │
└─────────────────────────────────────────────────────────────┘

Node Types

GraphitiEpisode

Raw input storage - messages, conversations, documents:

// Episode structure
(:GraphitiEpisode {
  id: "ep-uuid",
  group_id: "fixie:member:123",
  source: "conversation",
  content: "User mentioned they work at TechCorp...",
  reference_time: datetime("2026-01-15T10:30:00Z"),
  created_at: datetime()
})

GraphitiEntity

Extracted, deduplicated entities:

// Entity structure
(:GraphitiEntity {
  id: "ent-uuid",
  group_id: "fixie:member:123",
  name: "TechCorp",
  entity_type: "Organization",
  summary: "Technology company where user is employed",
  created_at: datetime(),
  embedding: [0.123, 0.456, ...]  // 1536-dim vector
})

GraphitiCommunity

Entity clusters for domain understanding:

// Community structure
(:GraphitiCommunity {
  id: "comm-uuid",
  group_id: "fixie:member:123",
  name: "User's Work Context",
  summary: "Entities related to user's employment"
})

Bi-Temporal Model

Time Dimensions

Graphiti tracks two independent timelines:

DimensionDescriptionProperties
Event TimeWhen the fact became true in realityt_valid, t_invalid
Transaction TimeWhen the fact was recorded in the systemt_created, t_expired

Relationship Properties

// Bi-temporal relationship
(a:GraphitiEntity)-[r:GRAPHITI_RELATES_TO {
  relationship_type: "WORKS_AT",

  // Event timeline (real world)
  t_valid: datetime("2024-06-01"),   // When user started at TechCorp
  t_invalid: null,                    // Still true (null = current)

  // Transaction timeline (system)
  t_created: datetime("2026-01-15"), // When we learned this
  t_expired: null,                   // Not superseded

  group_id: "fixie:member:123"
}]->(b:GraphitiEntity)

Temporal Queries

// Find facts valid at a specific point in time
MATCH (a:GraphitiEntity)-[r:GRAPHITI_RELATES_TO]->(b:GraphitiEntity)
WHERE r.group_id = $groupId
  AND r.t_valid <= datetime($queryTime)
  AND (r.t_invalid IS NULL OR r.t_invalid > datetime($queryTime))
RETURN a, r, b

Group Isolation

Multi-Tenant Memory

Graphiti uses group_id for memory isolation:

PatternScopeExample
fixie:member:{id}Personal agent memoryUser's individual context
fixie:persona:{id}Persona-specific memoryProfessional vs personal
fixie:project:{id}Project-scoped memoryTeam shared context
fixie:circle:{id}Circle agent memoryDepartment knowledge
shared:circle:{id}Circle-wide factsShared by all circle members
shared:orgOrganization-wideCompany-level knowledge

Query Pattern

// Get entities for a specific agent's memory
MATCH (e:GraphitiEntity)
WHERE e.group_id = $agentGroupId
RETURN e.name, e.entity_type, e.summary
ORDER BY e.created_at DESC
LIMIT 50

Cypher Examples

Entity Management

Create Entity from Episode

// Extract entity from conversation
MATCH (ep:GraphitiEpisode {id: $episodeId})
CREATE (e:GraphitiEntity {
  id: randomUUID(),
  group_id: ep.group_id,
  name: $entityName,
  entity_type: $entityType,
  summary: $summary,
  created_at: datetime()
})
CREATE (e)-[:GRAPHITI_MENTIONED_IN {
  episode_id: ep.id
}]->(ep)
RETURN e

Deduplicate Entities

// Merge similar entities
MATCH (e1:GraphitiEntity), (e2:GraphitiEntity)
WHERE e1.group_id = e2.group_id
  AND e1.name = e2.name
  AND e1.entity_type = e2.entity_type
  AND id(e1) < id(e2)
// Merge relationships to e1
MATCH (e2)-[r:GRAPHITI_RELATES_TO]-(other)
CREATE (e1)-[r2:GRAPHITI_RELATES_TO]->(other)
SET r2 = properties(r)
// Delete duplicate
DELETE r, e2
RETURN count(*) as merged

Relationship Queries

// Get entity neighborhood
MATCH (e:GraphitiEntity {id: $entityId})
OPTIONAL MATCH (e)-[r:GRAPHITI_RELATES_TO]-(related:GraphitiEntity)
WHERE r.t_invalid IS NULL  // Only current facts
RETURN e,
       collect(DISTINCT {
         entity: related,
         relationship: r.relationship_type,
         since: r.t_valid
       }) as relationships

Temporal Relationship History

// Track how a relationship changed over time
MATCH (a:GraphitiEntity {name: $entityA})-[r:GRAPHITI_RELATES_TO]->(b:GraphitiEntity {name: $entityB})
WHERE a.group_id = $groupId
RETURN r.relationship_type,
       r.t_valid as valid_from,
       r.t_invalid as valid_until,
       r.t_created as recorded_at
ORDER BY r.t_created

Memory Retrieval

// Find entities similar to query (requires vector index)
CALL db.index.vector.queryNodes(
  'graphiti_entity_embedding',
  5,  // k nearest neighbors
  $queryVector
)
YIELD node, score
WHERE node.group_id = $groupId
RETURN node.name, node.summary, score

Context Assembly for Prompt

// Get relevant context for an agent response
MATCH (e:GraphitiEntity)
WHERE e.group_id = $groupId
WITH e
ORDER BY e.created_at DESC
LIMIT 20
OPTIONAL MATCH (e)-[r:GRAPHITI_RELATES_TO]-(related:GraphitiEntity)
WHERE r.t_invalid IS NULL
RETURN collect(DISTINCT e.summary) as entitySummaries,
       collect(DISTINCT {
         from: e.name,
         relation: r.relationship_type,
         to: related.name
       }) as relationships

Community Analysis

Find Entity Communities

// Get community members
MATCH (c:GraphitiCommunity {id: $communityId})
MATCH (e:GraphitiEntity)-[m:GRAPHITI_MEMBER_OF]->(c)
RETURN c.name as community,
       c.summary as summary,
       collect({
         entity: e.name,
         type: e.entity_type,
         weight: m.weight
       }) as members
ORDER BY m.weight DESC

Integration with Fixies

Letta Memory Architecture

Graphiti integrates with Letta's memory system:

Letta Memory Layers:
┌─────────────────────────────────────────────┐
│              Core Memory                     │
│  (Agent persona, user info, in-context)     │
└──────────────────────┬──────────────────────┘

┌──────────────────────▼──────────────────────┐
│           Archival Memory                    │
│  (Searchable vector store, unlimited)       │
└──────────────────────┬──────────────────────┘

┌──────────────────────▼──────────────────────┐
│      Graphiti External Memory (NEW)          │
│  (Temporal knowledge graph, relationships)  │
└─────────────────────────────────────────────┘

Fixie Tool Integration

// Fixie tool for Graphiti search
const graphitiSearchTool = {
  name: "search_memory_graph",
  description: "Search temporal knowledge graph for facts and relationships",
  parameters: {
    query: "string - natural language query",
    time_context: "string - 'current' | 'historical' | ISO timestamp"
  },
  handler: async ({ query, time_context }) => {
    // Convert to vector
    const embedding = await embedQuery(query);

    // Search Graphiti
    const results = await neo4j.run(`
      CALL db.index.vector.queryNodes('graphiti_entity_embedding', 5, $embedding)
      YIELD node, score
      WHERE node.group_id = $groupId
      RETURN node.name, node.summary, score
    `, { embedding, groupId: agent.groupId });

    return results;
  }
};

Tier Availability

TierGraphiti AccessFeatures
FreeNoBasic conversation only
StandardNoCore + archival memory
PremiumYesFull temporal graph + communities

Indexes

Required Indexes

// Episode indexes
CREATE INDEX index_graphiti_episode_group IF NOT EXISTS
FOR (e:GraphitiEpisode) ON (e.group_id);

CREATE INDEX index_graphiti_episode_source IF NOT EXISTS
FOR (e:GraphitiEpisode) ON (e.source);

CREATE INDEX index_graphiti_episode_created IF NOT EXISTS
FOR (e:GraphitiEpisode) ON (e.created_at);

// Entity indexes
CREATE INDEX index_graphiti_entity_group IF NOT EXISTS
FOR (n:GraphitiEntity) ON (n.group_id);

CREATE INDEX index_graphiti_entity_name IF NOT EXISTS
FOR (n:GraphitiEntity) ON (n.name);

CREATE INDEX index_graphiti_entity_type IF NOT EXISTS
FOR (n:GraphitiEntity) ON (n.entity_type);

// Relationship indexes
CREATE INDEX index_graphiti_relates_to_valid IF NOT EXISTS
FOR ()-[r:GRAPHITI_RELATES_TO]-() ON (r.t_valid);

CREATE INDEX index_graphiti_relates_to_group IF NOT EXISTS
FOR ()-[r:GRAPHITI_RELATES_TO]-() ON (r.group_id);

Vector Index (Neo4j 5.15+)

// Create vector index for semantic search
CREATE VECTOR INDEX graphiti_entity_embedding IF NOT EXISTS
FOR (n:GraphitiEntity) ON n.embedding
OPTIONS {
  indexConfig: {
    `vector.dimensions`: 1536,
    `vector.similarity_function`: 'cosine'
  }
}

Best Practices

Entity Extraction

  1. Be Specific: Extract concrete entities, not abstract concepts
  2. Include Context: Summary should explain relationship to user
  3. Deduplicate Early: Check for existing entities before creating

Temporal Management

  1. Invalidate, Don't Delete: Set t_invalid instead of removing edges
  2. Record Source: Always link to originating episode
  3. Group Consistently: Use standard group_id patterns

Query Performance

  1. Filter by Group First: Always include group_id in WHERE
  2. Limit Results: Use LIMIT for exploration queries
  3. Use Indexes: Ensure queries hit indexed properties