Skip to content

Latest commit

 

History

History
502 lines (403 loc) · 12.5 KB

File metadata and controls

502 lines (403 loc) · 12.5 KB

Memory Graph Optimization

Overview

Optimizing the memory graph is crucial for maintaining performance as your A-MEM system scales. This guide covers techniques and best practices for ensuring your memory graph remains efficient, responsive, and reliable even with large datasets and high query volumes.

Performance Considerations

Scaling Factors

The performance of your memory graph is affected by several key factors:

  1. Node Count: Total number of knowledge nodes in the graph
  2. Edge Density: Number of relationships between nodes
  3. Query Complexity: Complexity of retrieval operations
  4. Update Frequency: How often nodes are created or modified
  5. Embedding Dimensions: Size of vector embeddings

Vector Database Optimization

LanceDb Configuration

Optimize your LanceDb configuration for your specific use case:

# Optimized LanceDb configuration
memory_graph = MemoryGraph(
    vector_db=LanceDb(
        uri="optimized-memory.db",
        table_name="knowledge_nodes",
        embedder=OpenAIEmbedder(id="text-embedding-3-small"),
        # Performance optimization parameters
        cache_size_mb=512,
        max_reader_threads=8,
        optimize_for="hybrid"  # Options: "read", "write", "hybrid"
    ),
    llm=OpenAIChat(id="gpt-4o")
)

Index Optimization

Create and optimize indexes for faster retrieval:

# Create optimized vector index
memory_graph.create_index(
    index_type="HNSW",  # Hierarchical Navigable Small World
    metric="cosine",    # Distance metric
    ef_construction=200,  # Higher values = more accurate but slower construction
    M=16                # Number of connections per layer
)

# Create additional indexes for metadata filtering
memory_graph.create_metadata_index(
    fields=["source", "confidence", "timestamp"],
    index_type="BTREE"
)

Batch Operations

Use batch operations for better performance:

# Batch node creation
nodes = [
    {"content": "Node 1 content", "metadata": {"source": "research"}},
    {"content": "Node 2 content", "metadata": {"source": "analysis"}},
    # ... more nodes
]
memory_graph.batch_create_nodes(nodes)

# Batch embedding generation
memory_graph.batch_generate_embeddings(
    contents=[node["content"] for node in nodes],
    batch_size=32
)

# Batch relationship creation
relationships = [
    {"source": "node1", "target": "node2", "type": "RELATED_TO"},
    {"source": "node2", "target": "node3", "type": "CONTAINS"},
    # ... more relationships
]
memory_graph.batch_create_relationships(relationships)

Query Optimization

Retrieval Strategies

Choose the appropriate retrieval strategy based on your needs:

# Fast retrieval with potentially lower relevance
quick_results = memory_graph.retrieve(
    query="quantum computing applications",
    strategy="vector_only",
    limit=5
)

# Balanced approach
balanced_results = memory_graph.retrieve(
    query="quantum computing applications",
    strategy="hybrid",
    vector_weight=0.7,
    semantic_weight=0.3,
    limit=5
)

# High relevance but slower
precise_results = memory_graph.retrieve(
    query="quantum computing applications",
    strategy="two_stage",
    first_stage_limit=20,
    reranker="cross_encoder",
    limit=5
)

Query Caching

Implement caching for frequently accessed queries:

# Enable query caching
memory_graph.enable_caching(
    cache_size=1000,  # Number of queries to cache
    ttl_seconds=3600  # Time-to-live for cache entries
)

# Perform cached query
cached_results = memory_graph.cached_retrieve(
    query="quantum computing applications",
    strategy="hybrid"
)

Parallel Query Processing

Use parallel processing for complex queries:

# Configure parallel query processing
memory_graph.configure_parallel_processing(
    max_workers=4,
    chunk_size=100
)

# Execute parallel query
results = memory_graph.parallel_retrieve(
    query="quantum computing applications",
    strategy="hybrid"
)

Memory Management

Node Pruning

Implement strategies to manage the size of your memory graph:

# Prune nodes by age
memory_graph.prune_nodes(
    older_than_days=90,
    exclude_tags=["permanent", "critical"]
)

# Prune nodes by relevance
memory_graph.prune_nodes(
    relevance_threshold=0.3,
    relevance_context="current project goals"
)

# Prune nodes by access frequency
memory_graph.prune_nodes(
    min_access_count=2,
    access_period_days=30
)

Node Consolidation

Consolidate similar or redundant nodes:

# Find candidate nodes for consolidation
candidates = memory_graph.find_consolidation_candidates(
    similarity_threshold=0.85,
    namespace="project_alpha"
)

# Consolidate nodes
memory_graph.consolidate_nodes(
    node_ids=candidates,
    strategy="merge",  # Options: "merge", "summarize", "link"
    llm=OpenAIChat(id="gpt-4o")
)

Memory Hierarchies

Implement hierarchical memory structures for better organization:

# Create memory hierarchy
memory_graph.create_hierarchy(
    root_node_id="project:quantum_computing",
    levels=[
        {"name": "concepts", "node_type": "concept"},
        {"name": "applications", "node_type": "application"},
        {"name": "implementations", "node_type": "implementation"}
    ],
    organization_strategy="semantic_clustering"
)

# Query within hierarchy
results = memory_graph.hierarchical_retrieve(
    query="quantum error correction",
    root_node_id="project:quantum_computing",
    max_depth=2
)

Monitoring and Optimization

Performance Metrics

Monitor key performance metrics:

# Enable performance monitoring
memory_graph.enable_monitoring(
    metrics=[
        "query_latency",
        "index_size",
        "cache_hit_rate",
        "node_count",
        "edge_count"
    ],
    log_file="memory_performance.log",
    alert_thresholds={
        "query_latency_ms": 100,
        "cache_hit_rate_percent": 50
    }
)

# Generate performance report
report = memory_graph.generate_performance_report(
    start_time=datetime(2025, 4, 1),
    end_time=datetime(2025, 4, 29),
    include_charts=True
)

Automatic Optimization

Configure automatic optimization routines:

# Schedule automatic optimization
memory_graph.schedule_optimization(
    interval_hours=24,
    tasks=[
        "reindex",
        "vacuum",
        "analyze_query_patterns",
        "adjust_cache_size"
    ]
)

# Manual optimization
memory_graph.optimize(
    tasks=["reindex", "vacuum"],
    aggressive=True
)

Scaling Strategies

Horizontal Scaling

Implement sharding for very large memory graphs:

# Create sharded memory graph
sharded_memory = ShardedMemoryGraph(
    shards=[
        MemoryGraph(vector_db=LanceDb(uri="shard1.db")),
        MemoryGraph(vector_db=LanceDb(uri="shard2.db")),
        MemoryGraph(vector_db=LanceDb(uri="shard3.db"))
    ],
    shard_strategy="content_based",  # Options: "content_based", "round_robin", "timestamp"
    llm=OpenAIChat(id="gpt-4o")
)

# Query across shards
results = sharded_memory.retrieve(
    query="quantum computing applications",
    strategy="hybrid"
)

Vertical Scaling

Optimize for higher resource utilization:

# Configure resource utilization
memory_graph.configure_resources(
    max_memory_percent=70,
    max_cpu_percent=80,
    io_priority="high"
)

Distributed Processing

Implement distributed processing for large-scale operations:

# Configure distributed processing
memory_graph.configure_distributed_processing(
    worker_nodes=["worker1:9000", "worker2:9000", "worker3:9000"],
    coordinator="coordinator:9000",
    task_distribution="dynamic"  # Options: "static", "dynamic", "workload_aware"
)

# Execute distributed query
results = memory_graph.distributed_retrieve(
    query="quantum computing applications",
    strategy="hybrid"
)

Benchmarking

Performance Testing

Implement benchmarking to measure and optimize performance:

# Run benchmark tests
benchmark_results = memory_graph.run_benchmark(
    tests=[
        "vector_search_latency",
        "node_creation_throughput",
        "relationship_traversal_speed",
        "concurrent_query_handling"
    ],
    iterations=10,
    concurrency_levels=[1, 5, 10, 20]
)

# Compare configurations
comparison = memory_graph.compare_configurations(
    configs=[
        {"index_type": "HNSW", "M": 16, "ef_construction": 200},
        {"index_type": "HNSW", "M": 32, "ef_construction": 100},
        {"index_type": "FLAT", "metric": "cosine"}
    ],
    test="vector_search_latency",
    query_set="benchmark_queries.json"
)

Best Practices

  1. Regular Maintenance: Schedule regular maintenance tasks:

    # Daily maintenance schedule
    memory_graph.schedule_maintenance(
        tasks=["vacuum", "analyze", "reindex"],
        schedule="0 3 * * *"  # Cron syntax: 3 AM daily
    )
  2. Incremental Optimization: Start with default settings and optimize incrementally based on monitoring data.

  3. Query Analysis: Regularly analyze query patterns to optimize indexes:

    # Analyze query patterns
    query_analysis = memory_graph.analyze_query_patterns(
        time_period_days=30,
        min_frequency=5
    )
    
    # Optimize based on analysis
    memory_graph.optimize_for_query_patterns(query_analysis)
  4. Embedding Strategy: Choose embedding dimensions and models appropriate for your scale:

    • Small-scale (< 10k nodes): 1536-dim embeddings (text-embedding-3-large)
    • Medium-scale (10k-100k nodes): 768-dim embeddings (text-embedding-3-small)
    • Large-scale (> 100k nodes): Consider dimension reduction techniques
  5. Batch Processing: Always use batch operations for bulk changes:

    # Process in batches
    for batch in chunked_list(nodes, batch_size=100):
        memory_graph.batch_create_nodes(batch)
  6. Isolation Levels: Configure appropriate isolation levels for your use case:

    # Configure isolation level
    memory_graph.set_isolation_level(
        level="SNAPSHOT"  # Options: "READ_UNCOMMITTED", "READ_COMMITTED", "SNAPSHOT", "SERIALIZABLE"
    )
  7. Backup Strategy: Implement regular backups:

    # Schedule backups
    memory_graph.schedule_backup(
        backup_dir="/backups/memory_graph",
        schedule="0 2 * * *",  # Cron syntax: 2 AM daily
        retention_days=14,
        compression=True
    )

Advanced Optimization Techniques

Custom Vector Quantization

Implement vector quantization for memory-efficient storage:

# Configure vector quantization
memory_graph.configure_vector_quantization(
    method="product_quantization",
    subvector_count=8,
    bits_per_subvector=8
)

Adaptive Indexing

Implement adaptive indexing based on query patterns:

# Enable adaptive indexing
memory_graph.enable_adaptive_indexing(
    analysis_interval_hours=24,
    adaptation_threshold=0.2  # Minimum change to trigger reindexing
)

Tiered Storage

Implement tiered storage for optimal performance:

# Configure tiered storage
memory_graph.configure_tiered_storage(
    tiers=[
        {
            "name": "hot",
            "storage": "memory",
            "capacity": "10%",
            "selection_criteria": "access_frequency > 10"
        },
        {
            "name": "warm",
            "storage": "ssd",
            "capacity": "30%",
            "selection_criteria": "access_frequency > 2"
        },
        {
            "name": "cold",
            "storage": "hdd",
            "capacity": "60%",
            "selection_criteria": "access_frequency <= 2"
        }
    ]
)

Case Studies

E-commerce Product Recommendation

An e-commerce platform using A-MEM for product recommendations optimized their memory graph by:

  1. Implementing hierarchical product categories
  2. Using tiered storage with frequently accessed products in the "hot" tier
  3. Scheduling daily consolidation of similar product descriptions
  4. Implementing query caching for popular search terms

Result: 70% reduction in query latency and 35% improvement in recommendation relevance.

Research Knowledge Base

A research organization managing a knowledge base of 500,000+ scientific papers optimized their memory graph by:

  1. Implementing sharding based on research domains
  2. Using distributed processing for large-scale updates
  3. Implementing adaptive indexing based on researcher query patterns
  4. Using vector quantization to reduce storage requirements

Result: Successfully scaled to handle 10x more documents while maintaining sub-100ms query latency.