MRRA LogoMRRA
Getting Started

Configuration

Advanced configuration options for LLM providers, retrieval systems, and MCP services

Configuration

MRRA provides flexible configuration options for LLM providers, retrieval systems, caching, and MCP services. This guide covers all configuration aspects in detail.

LLM Provider Configuration

Supported Providers

MRRA supports multiple LLM providers through a unified interface:

# OpenAI Configuration
openai_cfg = dict(
    provider='openai',
    model='gpt-4o-mini',  # or gpt-4, gpt-3.5-turbo
    api_key='your-openai-api-key',
    temperature=0.2,
    max_tokens=1000,
    timeout=60
)

# With custom base URL (for proxies)
openai_proxy_cfg = dict(
    provider='openai',
    model='gpt-4o-mini',
    api_key='your-api-key',
    base_url='https://your-proxy.com/v1',
    temperature=0.2
)
# Qwen Configuration (Alibaba DashScope)
qwen_cfg = dict(
    provider='openai-compatible',  # Use OpenAI-compatible interface
    model='qwen-plus',  # or qwen-turbo, qwen-max
    base_url='https://dashscope.aliyuncs.com/compatible-mode/v1',
    api_key='your-dashscope-api-key',
    temperature=0.2,
    max_tokens=2000
)

# Alternative direct configuration
qwen_direct_cfg = dict(
    provider='qwen',
    model='qwen-plus',
    api_key='your-dashscope-api-key',
    temperature=0.2
)
# SiliconFlow Configuration
siliconflow_cfg = dict(
    provider='openai-compatible',
    model='deepseek-chat',  # or other available models
    base_url='https://api.siliconflow.cn/v1',
    api_key='your-siliconflow-api-key',
    temperature=0.2,
    max_tokens=1500
)
# DeepInfra Configuration
deepinfra_cfg = dict(
    provider='openai-compatible',
    model='meta-llama/Meta-Llama-3.1-8B-Instruct',
    base_url='https://api.deepinfra.com/v1/openai',
    api_key='your-deepinfra-api-key',
    temperature=0.2,
    max_tokens=2000
)
# Custom Provider Configuration
custom_cfg = dict(
    provider='openai-compatible',  # Use OpenAI-compatible interface
    model='your-model-name',
    base_url='https://your-api-endpoint.com/v1',
    api_key='your-api-key',
    headers={'Custom-Header': 'value'},  # Optional custom headers
    temperature=0.2,
    max_tokens=1000,
    timeout=120
)

Environment Variable Configuration

Set up environment variables for secure configuration:

# OpenAI
export MRRA_OPENAI_API_KEY="your-openai-key"
export MRRA_OPENAI_MODEL="gpt-4o-mini"

# Qwen
export MRRA_QWEN_API_KEY="your-dashscope-key"
export MRRA_QWEN_MODEL="qwen-plus"

# SiliconFlow
export MRRA_SILICONFLOW_API_KEY="your-siliconflow-key"

# Default provider
export MRRA_DEFAULT_PROVIDER="openai"
export MRRA_DEFAULT_MODEL="gpt-4o-mini"

Use environment variables in configuration:

import os

def create_llm_config_from_env():
    """Create LLM configuration from environment variables"""
    
    provider = os.getenv('MRRA_DEFAULT_PROVIDER', 'openai')
    
    if provider == 'openai':
        return dict(
            provider='openai',
            model=os.getenv('MRRA_OPENAI_MODEL', 'gpt-4o-mini'),
            api_key=os.getenv('MRRA_OPENAI_API_KEY'),
            temperature=float(os.getenv('MRRA_TEMPERATURE', '0.2'))
        )
    elif provider == 'qwen':
        return dict(
            provider='openai-compatible',
            model=os.getenv('MRRA_QWEN_MODEL', 'qwen-plus'),
            base_url='https://dashscope.aliyuncs.com/compatible-mode/v1',
            api_key=os.getenv('MRRA_QWEN_API_KEY'),
            temperature=float(os.getenv('MRRA_TEMPERATURE', '0.2'))
        )
    else:
        raise ValueError(f"Unsupported provider: {provider}")

# Usage
llm_cfg = create_llm_config_from_env()

Activity Extraction Configuration

Parameter Guidelines

Configure activity extraction based on your data characteristics:

# Default configuration for urban mobility
default_cfg = dict(
    method="radius",           # 'radius' or 'grid'
    radius_m=300,             # Detection radius in meters
    min_dwell_minutes=30,     # Minimum stay time
    max_gap_minutes=90,       # Max gap between points
    grid_size_m=200          # Grid cell size (for grid method)
)

# Configuration for different scenarios
scenarios = {
    'urban_detailed': dict(
        method="radius",
        radius_m=200,           # Smaller radius for dense urban areas
        min_dwell_minutes=20,   # Shorter minimum dwell
        max_gap_minutes=60,     # Tighter gap tolerance
        grid_size_m=150
    ),
    
    'suburban': dict(
        method="radius", 
        radius_m=500,           # Larger radius for suburban areas
        min_dwell_minutes=45,   # Longer dwell times
        max_gap_minutes=120,    # More tolerance for gaps
        grid_size_m=300
    ),
    
    'sparse_data': dict(
        method="grid",          # Grid method more robust for sparse data
        radius_m=800,
        min_dwell_minutes=15,   # Lower threshold for sparse data
        max_gap_minutes=180,    # Higher tolerance
        grid_size_m=500
    ),
    
    'high_frequency': dict(
        method="radius",
        radius_m=100,           # Very fine-grained for high-frequency data
        min_dwell_minutes=10,   # Short dwells for frequent sampling
        max_gap_minutes=30,     # Tight gaps
        grid_size_m=100
    )
}

# Select based on your data type
data_type = 'urban_detailed'  # Change as needed
ext_cfg = scenarios[data_type]

acts = ActivityExtractor(tb, **ext_cfg).extract()

Adaptive Configuration

Automatically configure based on data analysis:

def auto_configure_extraction(tb):
    """Automatically configure extraction based on trajectory characteristics"""
    
    df = tb.df
    
    # Analyze data characteristics
    avg_points_per_user = len(df) / df['user_id'].nunique()
    time_span = (df['timestamp_local'].max() - df['timestamp_local'].min()).total_seconds() / 3600  # hours
    
    # Calculate typical sampling frequency
    sampling_freq_minutes = time_span * 60 / len(df) if len(df) > 0 else 60
    
    # Estimate typical movement distances
    distances = []
    for user_id in df['user_id'].unique()[:5]:  # Sample first 5 users
        user_df = df[df['user_id'] == user_id].sort_values('timestamp_local')
        
        for i in range(1, min(100, len(user_df))):  # Sample up to 100 points
            prev = user_df.iloc[i-1]
            curr = user_df.iloc[i]
            
            lat_diff = curr['latitude'] - prev['latitude']
            lon_diff = curr['longitude'] - prev['longitude']
            distance = ((lat_diff**2 + lon_diff**2)**0.5) * 111000  # Approximate meters
            
            distances.append(distance)
    
    median_distance = sorted(distances)[len(distances)//2] if distances else 300
    
    # Configure based on analysis
    if avg_points_per_user > 2000 and sampling_freq_minutes < 5:
        # High-frequency data
        config = dict(
            method="radius",
            radius_m=max(100, median_distance * 0.5),
            min_dwell_minutes=max(5, sampling_freq_minutes * 2),
            max_gap_minutes=max(30, sampling_freq_minutes * 10),
            grid_size_m=max(100, median_distance * 0.3)
        )
    elif avg_points_per_user < 200:
        # Sparse data
        config = dict(
            method="grid",
            radius_m=max(500, median_distance * 2),
            min_dwell_minutes=max(15, sampling_freq_minutes * 0.5),
            max_gap_minutes=max(120, sampling_freq_minutes * 5),
            grid_size_m=max(300, median_distance * 1.5)
        )
    else:
        # Standard data
        config = dict(
            method="radius",
            radius_m=max(200, median_distance),
            min_dwell_minutes=max(20, sampling_freq_minutes),
            max_gap_minutes=max(60, sampling_freq_minutes * 3),
            grid_size_m=max(150, median_distance * 0.7)
        )
    
    print(f"Auto-configured extraction parameters: {config}")
    print(f"Based on: {avg_points_per_user:.0f} avg points/user, {sampling_freq_minutes:.1f}min sampling, {median_distance:.0f}m median distance")
    
    return config

# Use adaptive configuration
ext_cfg = auto_configure_extraction(tb)
acts = ActivityExtractor(tb, **ext_cfg).extract()

Graph Configuration

Graph Construction Options

from mrra.graph.mobility_graph import GraphConfig

# Basic configuration
basic_cfg = GraphConfig(
    grid_size_m=200,              # Grid cell size
    min_dwell_minutes=5,          # Minimum dwell for graph nodes
    use_activities=True           # Use activity-based construction
)

# Advanced configuration
advanced_cfg = GraphConfig(
    grid_size_m=200,
    min_dwell_minutes=5,
    use_activities=True,
    include_transitions=True,      # Include location-to-location edges
    purpose_transitions=True,      # Include purpose-to-purpose edges
    temporal_granularity='both',   # 'hour', 'timebin', or 'both'
    weight_by_duration=True,       # Weight edges by time spent
    max_edge_weight=1000,         # Cap edge weights
    normalize_weights=True         # Normalize edge weights
)

# Memory-optimized configuration for large datasets
memory_optimized_cfg = GraphConfig(
    grid_size_m=500,              # Larger grid reduces node count
    min_dwell_minutes=10,         # Higher threshold reduces edges
    use_activities=True,
    include_transitions=False,     # Skip transition edges to save memory
    purpose_transitions=False,
    temporal_granularity='hour',   # Use only hour nodes, not time bins
    max_nodes=10000               # Limit total nodes
)

Retrieval Configuration

Configure GraphRAG retrieval weights and parameters:

from mrra.retriever.graph_rag import GraphRAGGenerate

# Create retriever with custom weights
retriever = GraphRAGGenerate(tb=tb, mobility_graph=mg)

# Configure retrieval weights
retriever.purpose_weight = 0.6    # How much to weight purpose context
retriever.hour_weight = 0.5       # How much to weight hour context  
retriever.dow_weight = 0.3        # How much to weight day-of-week
retriever.recent_weight = 0.2     # How much to weight recent locations
retriever.user_weight = 1.0       # How much to weight user context

# Configure retrieval parameters
retriever.max_results = 50        # Maximum locations to consider
retriever.min_score_threshold = 0.01  # Minimum relevance score
retriever.diversification_factor = 0.3  # Diversify results (0=no diversification, 1=maximum)

# Task-specific configurations
task_configs = {
    'next_position': {
        'purpose_weight': 0.4,
        'hour_weight': 0.6,
        'dow_weight': 0.4,
        'recent_weight': 0.8,  # High weight on recent context
    },
    'future_position': {
        'purpose_weight': 0.6,
        'hour_weight': 0.5,
        'dow_weight': 0.3,
        'recent_weight': 0.3,  # Lower weight on recent context
    },
    'full_day_traj': {
        'purpose_weight': 0.7,  # High weight on purpose patterns
        'hour_weight': 0.4,
        'dow_weight': 0.5,
        'recent_weight': 0.2,
    }
}

def configure_retriever_for_task(retriever, task):
    """Configure retriever weights based on prediction task"""
    if task in task_configs:
        config = task_configs[task]
        for param, value in config.items():
            setattr(retriever, param, value)
    return retriever

Cache Configuration

Cache Directory and Settings

from mrra.persist.cache import CacheManager

# Default cache configuration
default_cache = CacheManager()  # Uses .mrra_cache/

# Custom cache directory
custom_cache = CacheManager(base_dir='/path/to/custom/cache')

# Environment-based cache configuration
import os
cache_dir = os.getenv('MRRA_CACHE_DIR', '.mrra_cache')
cache_manager = CacheManager(base_dir=cache_dir)

Cache Settings and Policies

class ConfigurableCacheManager(CacheManager):
    def __init__(self, base_dir=None, settings=None):
        super().__init__(base_dir)
        
        # Default settings
        self.settings = {
            'max_cache_size_gb': 5.0,        # Maximum cache size
            'default_ttl_hours': 24,         # Time-to-live for cached items
            'cleanup_interval_hours': 6,     # How often to run cleanup
            'compression_level': 6,          # Compression level (0-9)
            'enable_compression': True,       # Enable cache compression
            'max_activities_per_cache': 10000,  # Limit activities per cache file
            'auto_cleanup_expired': True,     # Automatically clean expired items
        }
        
        if settings:
            self.settings.update(settings)
    
    def save_activities(self, tb_hash, key, activities):
        """Save activities with size limiting"""
        
        # Limit activities if too many
        if len(activities) > self.settings['max_activities_per_cache']:
            print(f"Limiting activities to {self.settings['max_activities_per_cache']}")
            activities = activities[:self.settings['max_activities_per_cache']]
        
        # Check cache size before saving
        if self._get_cache_size_gb() > self.settings['max_cache_size_gb']:
            print("Cache size limit exceeded, cleaning up...")
            self._cleanup_old_files()
        
        # Save with compression if enabled
        if self.settings['enable_compression']:
            self._save_activities_compressed(tb_hash, key, activities)
        else:
            super().save_activities(tb_hash, key, activities)
    
    def _get_cache_size_gb(self):
        """Calculate total cache size in GB"""
        import os
        
        total_size = 0
        for root, dirs, files in os.walk(self.base_dir):
            for file in files:
                file_path = os.path.join(root, file)
                if os.path.exists(file_path):
                    total_size += os.path.getsize(file_path)
        
        return total_size / (1024**3)  # Convert to GB

# Use configurable cache
cache_settings = {
    'max_cache_size_gb': 10.0,
    'default_ttl_hours': 48,
    'enable_compression': True
}

cache_manager = ConfigurableCacheManager(settings=cache_settings)

Multi-Agent Reflection Configuration

Agent Configuration Templates

# Template configurations for different use cases
agent_templates = {
    'basic': dict(
        max_round=1,
        subAgents=[
            {"name": "temporal", "prompt": "Focus on time patterns and select from Options."},
            {"name": "spatial", "prompt": "Focus on location patterns and select from Options."},
        ],
        aggregator="confidence_weighted_voting"
    ),
    
    'comprehensive': dict(
        max_round=2,
        subAgents=[
            {"name": "temporal", "prompt": "Analyze time patterns including hour-of-day and day-of-week preferences."},
            {"name": "spatial", "prompt": "Analyze spatial patterns including clustering and accessibility."},
            {"name": "purpose", "prompt": "Analyze activity purposes and sequential logic."},
            {"name": "routing", "prompt": "Analyze movement efficiency and transition patterns."},
        ],
        aggregator="confidence_weighted_voting",
        consensus_threshold=0.6
    ),
    
    'mcp_enhanced': dict(
        max_round=1,
        subAgents=[
            {
                "name": "context_aware", 
                "prompt": "Use external context (weather, POIs, traffic) for location selection.",
                "mcp": {
                    "weather": {},
                    "maps": {},
                }
            },
            {
                "name": "pattern_based",
                "prompt": "Focus on historical patterns and user preferences."
            }
        ],
        aggregator="confidence_weighted_voting"
    ),
    
    'fast': dict(
        max_round=1,
        subAgents=[
            {"name": "combined", "prompt": "Consider all factors quickly and select from Options."}
        ],
        aggregator="simple_selection"
    )
}

def get_agent_config(template_name, custom_overrides=None):
    """Get agent configuration from template with optional overrides"""
    
    if template_name not in agent_templates:
        raise ValueError(f"Unknown template: {template_name}")
    
    config = agent_templates[template_name].copy()
    
    if custom_overrides:
        config.update(custom_overrides)
    
    return config

# Usage
config = get_agent_config('comprehensive', {
    'max_round': 1,  # Override to speed up
    'consensus_threshold': 0.8  # Require higher consensus
})

Configuration Validation

def validate_mrra_config(config):
    """Validate MRRA configuration and suggest improvements"""
    
    issues = []
    suggestions = []
    
    # Check LLM configuration
    if 'llm' in config:
        llm_cfg = config['llm']
        
        if not llm_cfg.get('api_key'):
            issues.append("LLM API key not provided")
        
        if llm_cfg.get('temperature', 0.2) > 0.8:
            suggestions.append("High temperature may cause inconsistent results")
        
        if llm_cfg.get('max_tokens', 1000) > 4000:
            suggestions.append("High max_tokens may increase costs and latency")
    
    # Check extraction configuration
    if 'extraction' in config:
        ext_cfg = config['extraction']
        
        if ext_cfg.get('radius_m', 300) < 50:
            suggestions.append("Very small radius may miss activities")
        
        if ext_cfg.get('min_dwell_minutes', 30) > 120:
            suggestions.append("High minimum dwell may miss short activities")
    
    # Check agent configuration
    if 'reflection' in config:
        refl_cfg = config['reflection']
        
        if len(refl_cfg.get('subAgents', [])) > 5:
            suggestions.append("Many sub-agents may increase latency and costs")
        
        if refl_cfg.get('max_round', 1) > 3:
            suggestions.append("Many reflection rounds may be slow and expensive")
    
    return {
        'valid': len(issues) == 0,
        'issues': issues,
        'suggestions': suggestions
    }

# Example complete configuration
complete_config = {
    'llm': {
        'provider': 'openai',
        'model': 'gpt-4o-mini',
        'temperature': 0.2,
        'max_tokens': 1000
    },
    'extraction': {
        'method': 'radius',
        'radius_m': 300,
        'min_dwell_minutes': 30
    },
    'graph': {
        'grid_size_m': 200,
        'use_activities': True
    },
    'retrieval': {
        'purpose_weight': 0.6,
        'hour_weight': 0.5
    },
    'reflection': {
        'max_round': 1,
        'subAgents': [
            {"name": "temporal", "prompt": "Focus on time patterns."},
            {"name": "spatial", "prompt": "Focus on location patterns."}
        ]
    },
    'cache': {
        'base_dir': '.mrra_cache',
        'max_size_gb': 5.0
    }
}

validation = validate_mrra_config(complete_config)
print("Configuration validation:", validation)

Configuration Best Practices:

  • Start with default configurations and adjust based on your data characteristics
  • Use environment variables for sensitive information like API keys
  • Validate configurations before running expensive operations
  • Monitor performance and adjust parameters accordingly
  • Use caching extensively to avoid recomputation

Production Configuration

Scalable Configuration Management

import yaml
import os
from pathlib import Path

class MRRAConfigManager:
    """Production-ready configuration management"""
    
    def __init__(self, config_file=None):
        self.config_file = config_file or os.getenv('MRRA_CONFIG_FILE', 'mrra_config.yaml')
        self.config = self._load_config()
    
    def _load_config(self):
        """Load configuration from file with environment variable substitution"""
        
        if not Path(self.config_file).exists():
            return self._create_default_config()
        
        with open(self.config_file, 'r') as f:
            config = yaml.safe_load(f)
        
        # Substitute environment variables
        return self._substitute_env_vars(config)
    
    def _substitute_env_vars(self, config):
        """Recursively substitute environment variables in configuration"""
        
        if isinstance(config, dict):
            return {k: self._substitute_env_vars(v) for k, v in config.items()}
        elif isinstance(config, list):
            return [self._substitute_env_vars(item) for item in config]
        elif isinstance(config, str) and config.startswith('${') and config.endswith('}'):
            env_var = config[2:-1]  # Remove ${ and }
            default_value = None
            
            if ':' in env_var:
                env_var, default_value = env_var.split(':', 1)
            
            return os.getenv(env_var, default_value)
        else:
            return config
    
    def _create_default_config(self):
        """Create default configuration"""
        
        default_config = {
            'llm': {
                'provider': '${MRRA_LLM_PROVIDER:openai}',
                'model': '${MRRA_LLM_MODEL:gpt-4o-mini}',
                'api_key': '${MRRA_API_KEY}',
                'temperature': 0.2,
                'max_tokens': 1000,
                'timeout': 60
            },
            'extraction': {
                'method': 'radius',
                'radius_m': 300,
                'min_dwell_minutes': 30,
                'max_gap_minutes': 90
            },
            'graph': {
                'grid_size_m': 200,
                'min_dwell_minutes': 5,
                'use_activities': True
            },
            'retrieval': {
                'purpose_weight': 0.6,
                'hour_weight': 0.5,
                'dow_weight': 0.3,
                'recent_weight': 0.2
            },
            'cache': {
                'base_dir': '${MRRA_CACHE_DIR:.mrra_cache}',
                'max_size_gb': 5.0,
                'default_ttl_hours': 24
            }
        }
        
        # Save default config
        with open(self.config_file, 'w') as f:
            yaml.dump(default_config, f, default_flow_style=False)
        
        return self._substitute_env_vars(default_config)
    
    def get(self, key, default=None):
        """Get configuration value with dot notation"""
        keys = key.split('.')
        value = self.config
        
        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default
        
        return value
    
    def get_llm_config(self):
        """Get LLM configuration"""
        return self.config.get('llm', {})
    
    def get_extraction_config(self):
        """Get extraction configuration"""
        return self.config.get('extraction', {})

# Usage
config_manager = MRRAConfigManager('production_config.yaml')

# Get configurations
llm_cfg = config_manager.get_llm_config()
ext_cfg = config_manager.get_extraction_config()
cache_dir = config_manager.get('cache.base_dir', '.mrra_cache')

Example production configuration file (mrra_config.yaml):

# MRRA Production Configuration
llm:
  provider: ${MRRA_LLM_PROVIDER:openai}
  model: ${MRRA_LLM_MODEL:gpt-4o-mini}
  api_key: ${MRRA_API_KEY}
  temperature: 0.2
  max_tokens: 1000
  timeout: 120

extraction:
  method: radius
  radius_m: 300
  min_dwell_minutes: 30
  max_gap_minutes: 90
  grid_size_m: 200

graph:
  grid_size_m: 200
  min_dwell_minutes: 5
  use_activities: true
  include_transitions: true
  purpose_transitions: true

retrieval:
  purpose_weight: 0.6
  hour_weight: 0.5
  dow_weight: 0.3
  recent_weight: 0.2
  max_results: 50

reflection:
  max_round: 1
  aggregator: confidence_weighted_voting
  subAgents:
    - name: temporal
      prompt: "Focus on temporal patterns and select from Options."
      temperature: 0.1
    - name: spatial
      prompt: "Focus on spatial patterns and select from Options."
      temperature: 0.1

cache:
  base_dir: ${MRRA_CACHE_DIR:.mrra_cache}
  max_size_gb: 10.0
  default_ttl_hours: 48
  enable_compression: true
  auto_cleanup: true

logging:
  level: ${MRRA_LOG_LEVEL:INFO}
  format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

Security Considerations:

  • Never commit API keys or sensitive data to version control
  • Use environment variables for all secrets
  • Implement proper access controls for configuration files
  • Regularly rotate API keys and update configurations

Next Steps