Agent Configuration System Architecture
This document describes the technical architecture of Radium's agent configuration system for developers.
Overviewβ
The agent configuration system provides a standardized way to define, discover, and configure AI agents in Radium. It supports TOML-based configuration files, hierarchical agent discovery, prompt template processing with caching, and a thread-safe agent registry with advanced search and filtering capabilities.
System Componentsβ
Core Componentsβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Agent Configuration System β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββΌββββββββββ¬βββββββββββββββ¬βββββββββββββββ
βΌ βΌ βΌ βΌ βΌ
βββββββββββ ββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
β Config β βDiscoveryβ β Registry β β Templates β β Metadata β
β Parser β β Service β β Service β β System β β Parser β
βββββββββββ ββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
Architecture Detailsβ
Agent Configuration Formatβ
Location: crates/radium-core/src/agents/config.rs
The agent configuration system uses TOML files to define agent properties, capabilities, and behaviors.
Configuration Structureβ
[agent]
id = "arch-agent"
name = "Architecture Agent"
description = "Defines system architecture and technical design decisions"
prompt_path = "prompts/agents/core/arch-agent.md"
engine = "gemini" # optional
model = "gemini-2.0-flash-exp" # optional
reasoning_effort = "medium" # optional: low, medium, high
[agent.loop_behavior] # optional
steps = 2
max_iterations = 5
skip = ["step-1", "step-3"]
[agent.trigger_behavior] # optional
trigger_agent_id = "code-agent"
[agent.capabilities] # optional
model_class = "reasoning" # fast, balanced, reasoning
cost_tier = "high" # low, medium, high
max_concurrent_tasks = 3
[agent.persona] # optional
[agent.persona.models]
primary = "gemini-2.0-flash-thinking"
fallback = "gemini-2.0-flash-exp"
premium = "gemini-1.5-pro"
[agent.persona.performance]
profile = "thinking" # speed, balanced, thinking, expert
estimated_tokens = 2000
[agent.sandbox] # optional
type = "docker"
image = "rust:latest"
profile = "restricted"
network_mode = "isolated"
Data Modelsβ
pub struct AgentConfig {
pub id: String,
pub name: String,
pub description: String,
pub prompt_path: PathBuf,
pub mirror_path: Option<PathBuf>,
pub engine: Option<String>,
pub model: Option<String>,
pub reasoning_effort: Option<ReasoningEffort>,
pub loop_behavior: Option<AgentLoopBehavior>,
pub trigger_behavior: Option<AgentTriggerBehavior>,
pub category: Option<String>, // derived from path
pub file_path: Option<PathBuf>, // set during loading
pub capabilities: AgentCapabilities,
pub sandbox: Option<SandboxConfig>,
pub persona_config: Option<PersonaConfig>,
}
pub enum ReasoningEffort {
Low,
Medium,
High,
}
pub enum ModelClass {
Fast, // Speed-optimized models
Balanced, // Balanced speed and quality
Reasoning, // Deep reasoning models
}
pub enum CostTier {
Low, // $0.00 - $0.10 per 1M tokens
Medium, // $0.10 - $1.00 per 1M tokens
High, // $1.00 - $10.00 per 1M tokens
}
Agent Discovery Systemβ
Location: crates/radium-core/src/agents/discovery.rs
The discovery system scans multiple directories for agent configuration files and loads them with hierarchical precedence.
Discovery Flowβ
1. Scan Search Paths (in order of precedence):
ββ Project-local: ./agents/
ββ User agents: ~/.radium/agents/
ββ Workspace agents: $RADIUM_WORKSPACE/agents/
ββ Extension agents: ./.radium/extensions/*/agents/
and ~/.radium/extensions/*/agents/
2. For each path:
ββ Recursively scan for *.toml files
ββ Load and parse TOML configuration
ββ Extract YAML frontmatter from prompt files
ββ Parse metadata and generate persona config
ββ Derive category from directory structure
ββ Build agent registry (later entries override earlier)
3. Apply filters:
ββ Sub-agent filter (if specified)
Search Path Precedenceβ
Agents are discovered from multiple directories with the following precedence (highest to lowest):
- Project-local agents:
./agents/- Project-specific agents - User agents:
~/.radium/agents/- User's personal agents - Workspace agents:
$RADIUM_WORKSPACE/agents/- Workspace-level agents - Extension agents:
./.radium/extensions/*/agents/- Project-level extensions~/.radium/extensions/*/agents/- User-level extensions
Precedence Rules:
- Agents with the same ID from higher-precedence directories override those from lower-precedence directories
- This allows project-specific agents to override user-level or extension agents
Category Derivationβ
The agent's category is automatically derived from the directory structure:
agents/core/arch-agent.tomlβ category:"core"agents/custom/my-agent.tomlβ category:"custom"agents/rad-agents/design/design-agent.tomlβ category:"rad-agents/design"
The category is determined by the parent directory path relative to the agents root.
Key Methodsβ
pub struct AgentDiscovery {
options: DiscoveryOptions,
}
impl AgentDiscovery {
pub fn new() -> Self;
pub fn with_options(options: DiscoveryOptions) -> Self;
pub fn discover_all(&self) -> Result<HashMap<String, AgentConfig>>;
fn discover_in_directory(&self, dir: &Path, agents: &mut HashMap<String, AgentConfig>) -> Result<()>;
fn scan_directory(&self, root: &Path, current: &Path, agents: &mut HashMap<String, AgentConfig>) -> Result<()>;
fn load_agent_config(&self, path: &Path, root: &Path, agents: &mut HashMap<String, AgentConfig>) -> Result<()>;
}
Metadata Extraction During Discoveryβ
During discovery, the system:
- Loads the agent configuration from TOML
- Reads the prompt file specified in
prompt_path - Extracts YAML frontmatter from the prompt file (if present)
- Parses metadata using
AgentMetadata::from_markdown() - Generates
PersonaConfigfrom metadata if model recommendations are present - Attaches metadata and persona config to the agent configuration
Prompt Template Systemβ
Location:
crates/radium-core/src/prompts/templates.rs- Core template loading and renderingcrates/radium-core/src/prompts/processing.rs- Advanced processing (caching, file injection)
The prompt template system loads markdown templates and processes them with placeholder replacement, file injection, and caching.
Template Processing Flowβ
1. Load Template:
ββ Check cache (if enabled)
ββ Load from file (if cache miss)
ββ Store in cache
2. Process Template:
ββ File injection ({{file:path}}, {{file:path:code}}, {{file:path:markdown}})
ββ Placeholder replacement ({{VAR}})
ββ Default values (if non-strict mode)
ββ Validation (if strict mode)
3. Render Output:
ββ Return processed template string
Placeholder Syntaxβ
Basic Placeholders:
Hello {{name}}! Your task is {{task}}.
File Injection:
{{file:path/to/file.txt}} # Plain text injection
{{file:path/to/file.rs:code}} # Code block with syntax highlighting
{{file:path/to/file.md:markdown}} # Markdown section
Template Rendering Modesβ
Strict Mode:
- Errors if any placeholder is missing
- No default values used
- Ensures all required context is provided
Non-Strict Mode:
- Uses default values for missing placeholders
- Continues processing even with missing values
- More forgiving for optional context
Prompt Cachingβ
The system includes a thread-safe prompt cache to avoid repeated file I/O:
pub struct PromptCache {
cache: Arc<RwLock<HashMap<PathBuf, CacheEntry>>>,
ttl: Option<Duration>,
}
impl PromptCache {
pub fn new() -> Self; // No TTL (indefinite caching)
pub fn with_ttl(ttl: Duration) -> Self; // TTL-based caching
pub fn load(&self, path: impl AsRef<Path>) -> Result<PromptTemplate>;
pub fn clear(&self) -> Result<()>;
}
Cache Behavior:
- Templates are cached by file path
- TTL-based expiration (if configured)
- Thread-safe concurrent access using
Arc<RwLock<>> - Automatic cache invalidation on TTL expiration
Key Methodsβ
pub struct PromptTemplate {
content: String,
file_path: Option<PathBuf>,
}
impl PromptTemplate {
pub fn load(path: impl AsRef<Path>) -> Result<Self>;
pub fn from_string(content: impl Into<String>) -> Self;
pub fn render(&self, context: &PromptContext) -> Result<String>;
pub fn render_strict(&self, context: &PromptContext) -> Result<String>;
pub fn list_placeholders(&self) -> Vec<String>;
pub fn content(&self) -> &str;
pub fn file_path(&self) -> Option<&Path>;
}
pub fn process_with_file_injection(
template: &PromptTemplate,
context: &PromptContext,
options: &FileInjectionOptions,
) -> Result<String>;
Agent Registryβ
Location: crates/radium-core/src/agents/registry.rs
The agent registry provides thread-safe runtime management of discovered agents with advanced search, filtering, and sorting capabilities.
Registry Architectureβ
pub struct AgentRegistry {
agents: Arc<RwLock<HashMap<String, AgentConfig>>>,
}
Thread Safety:
- Uses
Arc<RwLock<HashMap>>for concurrent access - Multiple readers can access simultaneously
- Writers acquire exclusive lock
- Lock poisoning is handled gracefully
Search Modesβ
Exact Match:
- Case-insensitive exact string matching
- Fast lookup by agent ID or exact name
Contains Match:
- Substring matching (case-insensitive)
- Default search mode
- Searches in name, description, and ID
Fuzzy Match:
- Levenshtein distance-based matching
- Configurable similarity threshold (0.0 to 1.0, default 0.7)
- Useful for typo tolerance and approximate matching
Filtering Systemβ
Filter Criteria:
pub struct FilterCriteria {
pub category: Option<String>, // Partial match
pub engine: Option<String>, // Exact match
pub model: Option<String>, // Partial match
pub tags: Option<Vec<String>>, // Any tag matches
pub search_mode: SearchMode, // Exact, Contains, Fuzzy
pub logic_mode: LogicMode, // AND or OR
pub fuzzy_threshold: f64, // 0.0-1.0
}
Logic Modes:
- AND: All criteria must match (default)
- OR: Any criterion can match
Example:
let criteria = FilterCriteria {
category: Some("core".to_string()),
engine: Some("gemini".to_string()),
logic_mode: LogicMode::And,
..Default::default()
};
let agents = registry.filter(&criteria)?;
Sortingβ
Sort Orders:
- By name (alphabetical)
- By category (alphabetical)
- By engine (alphabetical)
- Multi-field sorting (chained)
Example:
let sorted = registry.sort(agents, SortOrder::Multiple(vec![
SortField::Category,
SortField::Name,
]));
Key Methodsβ
impl AgentRegistry {
pub fn new() -> Self;
pub fn with_discovery() -> Result<Self>;
pub fn register(&self, agent: AgentConfig) -> Result<()>;
pub fn register_or_replace(&self, agent: AgentConfig) -> Result<()>;
pub fn get(&self, id: &str) -> Result<AgentConfig>;
pub fn list_all(&self) -> Result<Vec<AgentConfig>>;
pub fn search(&self, query: &str, mode: SearchMode) -> Result<Vec<AgentConfig>>;
pub fn filter(&self, criteria: &FilterCriteria) -> Result<Vec<AgentConfig>>;
pub fn sort(&self, agents: Vec<AgentConfig>, order: SortOrder) -> Vec<AgentConfig>;
pub fn count(&self) -> Result<usize>;
pub fn discover_and_register(&self) -> Result<()>;
}
Agent Metadata Systemβ
Location: crates/radium-core/src/agents/metadata.rs
The metadata system parses YAML frontmatter from agent prompt files to extract rich metadata including model recommendations, performance profiles, and capabilities.
YAML Frontmatter Formatβ
---
name: arch-agent
display_name: Architecture Agent
category: engineering
color: blue
description: Defines system architecture and technical design decisions
recommended_models:
primary:
engine: gemini
model: gemini-2.0-flash-thinking
priority: thinking
cost_tier: high
fallback:
engine: gemini
model: gemini-2.0-flash-exp
priority: balanced
cost_tier: medium
premium:
engine: gemini
model: gemini-1.5-pro
priority: expert
cost_tier: premium
performance_profile:
thinking_depth: high
iteration_speed: medium
context_requirements: extensive
output_volume: high
capabilities:
- system_design
- architecture_decisions
- technology_selection
quality_gates:
- code_review
- architecture_review
works_well_with:
- code-agent
- review-agent
---
Metadata Structureβ
pub struct AgentMetadata {
pub name: String,
pub display_name: Option<String>,
pub category: Option<String>,
pub color: String,
pub description: String,
pub recommended_models: Option<RecommendedModels>,
pub capabilities: Option<Vec<String>>,
pub performance_profile: Option<PerformanceProfile>,
pub quality_gates: Option<Vec<String>>,
pub works_well_with: Option<Vec<String>>,
pub typical_workflows: Option<Vec<String>>,
pub tools: Option<Vec<String>>,
pub constraints: Option<HashMap<String, serde_yaml::Value>>,
}
Model Recommendationsβ
Priority Levels:
speed: Fast models, lower costbalanced: Balanced speed and qualitythinking: Deep reasoning modelsexpert: Expert-level reasoning, highest cost
Cost Tiers:
low: $0.00 - $0.10 per 1M tokensmedium: $0.10 - $1.00 per 1M tokenshigh: $1.00 - $10.00 per 1M tokenspremium: $10.00+ per 1M tokens
Persona Configuration Generationβ
The metadata system automatically generates PersonaConfig from metadata:
impl AgentMetadata {
pub fn to_persona_config(&self) -> Option<PersonaConfig>;
}
This conversion:
- Extracts model recommendations (primary, fallback, premium)
- Maps performance profile to persona performance config
- Sets up model selection priorities
- Configures cost optimization settings
Key Methodsβ
impl AgentMetadata {
pub fn from_markdown(content: &str) -> Result<(Self, String)>;
pub fn from_file(path: impl AsRef<Path>) -> Result<(Self, String)>;
pub fn validate(&self) -> Result<()>;
pub fn to_persona_config(&self) -> Option<PersonaConfig>;
pub fn get_display_name(&self) -> &str;
}
Data Flowβ
Agent Discovery Flowβ
βββββββββββββββ
β Discovery β
β Service β
ββββββββ¬βββββββ
β
βββΊ Scan directories (recursive)
β
βββΊ Load TOML config files
β
βββΊ Parse agent configuration
β
βββΊ Load prompt file
β
βββΊ Extract YAML frontmatter
β
βββΊ Parse metadata
β
βββΊ Generate persona config
β
βββΊ Build registry (HashMap)
Template Processing Flowβ
βββββββββββββββ
β Template β
β Request β
ββββββββ¬βββββββ
β
βββΊ Check cache
β βββΊ Cache hit β Return cached
β βββΊ Cache miss β Continue
β
βββΊ Load from file
β
βββΊ Store in cache
β
βββΊ Process file injections
β βββΊ Resolve file paths
β βββΊ Read file content
β βββΊ Format (plain/code/markdown)
β
βββΊ Replace placeholders
β βββΊ Get values from context
β βββΊ Use defaults (if non-strict)
β βββΊ Error if missing (if strict)
β
βββΊ Return rendered template
Registry Search Flowβ
βββββββββββββββ
β Search β
β Query β
ββββββββ¬βββββββ
β
βββΊ Acquire read lock
β
βββΊ Select search mode
β βββΊ Exact: Direct lookup
β βββΊ Contains: Substring match
β βββΊ Fuzzy: Levenshtein distance
β
βββΊ Apply filters (if specified)
β βββΊ Category filter
β βββΊ Engine filter
β βββΊ Model filter
β βββΊ Tags filter
β βββΊ Logic mode (AND/OR)
β
βββΊ Sort results (if specified)
β
βββΊ Return filtered & sorted agents
Thread Safetyβ
The agent configuration system is designed for concurrent access:
Registry Thread Safetyβ
- Read Operations: Multiple threads can read simultaneously using
RwLock::read() - Write Operations: Exclusive access using
RwLock::write() - Lock Poisoning: Handled gracefully with error propagation
Cache Thread Safetyβ
- Concurrent Reads: Multiple threads can access cached templates simultaneously
- Cache Updates: Exclusive lock for cache modifications
- TTL Expiration: Thread-safe expiration checking
Discovery Thread Safetyβ
- Discovery Operations: Not thread-safe by design (typically called during initialization)
- Result Storage: Thread-safe registry for storing discovered agents
Integration Pointsβ
With Workflow Systemβ
- Agents are loaded during workflow initialization
- Agent configurations are used to set up workflow steps
- Loop and trigger behaviors are integrated with workflow execution
With Engine Systemβ
- Agent engine/model preferences are used for model selection
- Persona configurations guide intelligent model selection
- Cost tiers inform budget-aware model selection
With CLIβ
rad agents list- Lists discovered agentsrad agents search- Searches agents using registryrad agents info- Shows agent detailsrad agents validate- Validates agent configurations
With Extension Systemβ
- Extension agents are discovered from extension directories
- Extension agents can override built-in agents
- Extension metadata is integrated with discovery
Error Handlingβ
Configuration Errorsβ
- Invalid TOML: Returns
AgentConfigError::Toml - Missing Fields: Returns
AgentConfigError::Invalid - I/O Errors: Returns
AgentConfigError::Io
Discovery Errorsβ
- Directory Not Found: Skips directory (non-fatal)
- Invalid Config: Logs warning and skips (non-fatal)
- Missing Prompt File: Logs warning but continues (validation happens later)
Template Errorsβ
- File Not Found: Returns
PromptError::NotFound - Missing Placeholder: Returns
PromptError::MissingPlaceholder(strict mode) - Invalid Syntax: Returns
PromptError::InvalidSyntax
Registry Errorsβ
- Agent Not Found: Returns
RegistryError::NotFound - Lock Poisoned: Returns
RegistryError::LockPoisoned - Already Registered: Returns
RegistryError::AlreadyRegistered
Performance Considerationsβ
Discovery Performanceβ
- Recursive Scanning: Efficient directory traversal
- Lazy Loading: Configs loaded only when needed
- Caching: Prompt templates cached to avoid repeated I/O
Registry Performanceβ
- HashMap Lookup: O(1) average case for ID lookups
- Search Operations: O(n) for contains/fuzzy search
- Filtering: O(n) with early termination where possible
Cache Performanceβ
- Cache Hits: O(1) lookup from memory
- Cache Misses: File I/O operation
- TTL Management: Periodic cleanup of expired entries
Testingβ
Unit Testsβ
- Configuration parsing tests in
config.rs - Template rendering tests in
templates.rs - Registry operation tests in
registry.rs - Metadata parsing tests in
metadata.rs
Integration Testsβ
- Full discovery workflow tests
- Registry with discovery tests
- Template processing with file injection tests
- Metadata extraction during discovery tests
See crates/radium-core/tests/agent_config_integration_test.rs for comprehensive integration tests.
Future Enhancementsβ
Planned Featuresβ
- Agent versioning and migration
- Agent dependency management
- Dynamic agent reloading
- Agent performance metrics
- Agent collaboration workflows
Potential Improvementsβ
- Incremental discovery (watch for file changes)
- Distributed agent discovery
- Agent marketplace integration
- Advanced caching strategies
- Template compilation and optimization