Quickstart Guide
Get your first Paladin agent running in 15 minutes! This guide will walk you through creating a simple Paladin agent that can answer questions using an LLM.
Prerequisites
- Rust: 1.70 or later (installation guide)
- LLM API Key: OpenAI, DeepSeek, or Anthropic account
- Basic Rust knowledge: Understanding of
cargoand async/await
Step 1: Installation
Add Paladin to your Cargo.toml:
[dependencies]
paladin = "0.1"
tokio = { version = "1", features = ["full"] }
Or create a new project:
cargo new my-paladin-agent
cd my-paladin-agent
cargo add paladin
cargo add tokio --features full
Step 2: Set Up Your Environment
Create a .env file in your project root:
# OpenAI
OPENAI_API_KEY=sk-your-api-key-here
# Or DeepSeek
DEEPSEEK_API_KEY=your-deepseek-key
# Or Anthropic
ANTHROPIC_API_KEY=your-anthropic-key
Security Note: Never commit API keys to version control. Add .env to your .gitignore.
Step 3: Create Your First Paladin
Create or edit src/main.rs:
use paladin::prelude::*; use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Load environment variables dotenv::dotenv().ok(); // Create an LLM adapter (OpenAI in this example) let llm_adapter = Arc::new( OpenAiAdapter::new() .with_api_key(std::env::var("OPENAI_API_KEY")?) .with_model("gpt-4") .build()? ); // Build a Paladin agent let paladin = PaladinBuilder::new(llm_adapter) .name("Assistant") .system_prompt("You are a helpful AI assistant. Be concise and accurate.") .temperature(0.7) .max_loops(3) .build()?; // Execute a query let response = paladin.execute("What is the capital of France?").await?; println!("Paladin: {}", response.content); Ok(()) }
Step 4: Run Your Agent
cargo run
Expected Output:
Paladin: The capital of France is Paris.
Next Steps
Congratulations! You've created your first Paladin agent. Here's what to explore next:
1. Add Memory (Garrison)
Enable conversation context:
#![allow(unused)] fn main() { let garrison = Arc::new(InMemoryGarrison::new()); let paladin = PaladinBuilder::new(llm_adapter) .name("Assistant") .system_prompt("You are a helpful assistant.") .with_garrison(garrison) .build()?; // Now the Paladin remembers previous interactions paladin.execute("My name is Alice").await?; paladin.execute("What is my name?").await?; // "Your name is Alice" }
2. Add Tools (Arsenal)
Give your Paladin capabilities:
#![allow(unused)] fn main() { use paladin::arsenal::*; // Connect an MCP tool server let web_search = MCPStdioAdapter::new("uvx", vec!["mcp-web-search"]).await?; let paladin = PaladinBuilder::new(llm_adapter) .name("Research Assistant") .system_prompt("You can search the web to answer questions.") .add_armament(Arc::new(web_search)) .build()?; paladin.execute("What's the latest Rust release?").await?; }
3. Multi-Agent Orchestration (Battalion)
Coordinate multiple Paladins:
#![allow(unused)] fn main() { use paladin::battalion::*; // Sequential execution (Formation) let analyst = /* create analyst Paladin */; let writer = /* create writer Paladin */; let formation = Formation::new() .add_paladin(analyst) .add_paladin(writer) .build()?; let result = formation.execute("Analyze market trends and write a summary").await?; }
4. Council Discussions
Enable multi-agent debate and consensus building:
#![allow(unused)] fn main() { use paladin::battalion::council::*; // Create expert Paladins with different perspectives let security_expert = PaladinBuilder::new(llm_adapter.clone()) .name("SecurityExpert") .system_prompt("You are a security expert. Focus on authentication and data protection.") .build()?; let legal_expert = PaladinBuilder::new(llm_adapter.clone()) .name("LegalExpert") .system_prompt("You are a legal expert. Focus on compliance and privacy regulations.") .build()?; let tech_lead = PaladinBuilder::new(llm_adapter.clone()) .name("TechLead") .system_prompt("You are a technical lead. Focus on implementation feasibility.") .build()?; let paladins = vec![security_expert, legal_expert, tech_lead]; // Build a Council for structured discussion let council = CouncilBuilder::new() .name("Feature Discussion") .participants(3) .turn_strategy(TurnStrategy::RoundRobin) // Each expert takes turns .termination_condition(TerminationCondition::MaxRounds(3)) // 3 rounds of debate .build()?; // Execute the discussion let service = CouncilExecutionService::new(llm_adapter); let result = service.execute( &council, &paladins, "Should we implement two-factor authentication?" ).await?; println!("Discussion Summary: {}", result.summary); println!("Total Turns: {}", result.total_turns); }
Council Features:
- Turn-based dialogue: Structured conversations with round-robin or custom turn strategies
- Termination conditions: End after max rounds, consensus detection, or time limits
- Discussion transcript: Full conversation history with speaker attribution
- Summary generation: Automatic discussion summary and recommendation synthesis
Example CLI Command:
paladin council "Should we adopt microservices?" -n 5 --rounds 3
See examples/council_discussion.rs for a complete working example.
5. Grove Routing
Route tasks to specialized experts based on content:
#![allow(unused)] fn main() { use paladin::battalion::grove::*; // Create specialized agent trees let security_tree = Tree::new("Security Experts") .add_agent(TreeAgent::new("SecurityAuditor") .with_keywords(vec!["security", "vulnerability", "authentication"])) .add_agent(TreeAgent::new("CryptoExpert") .with_keywords(vec!["encryption", "keys", "certificates"])); let performance_tree = Tree::new("Performance Experts") .add_agent(TreeAgent::new("DatabaseOptimizer") .with_keywords(vec!["database", "query", "index"])) .add_agent(TreeAgent::new("CachingExpert") .with_keywords(vec!["cache", "redis", "latency"])); // Build the Grove with keyword-based routing let grove = GroveBuilder::new() .name("Expert Router") .add_tree(security_tree) .add_tree(performance_tree) .config(GroveConfig { routing_strategy: RoutingStrategy::KeywordMatch, fallback_tree: Some("Performance Experts".to_string()), confidence_threshold: 0.6, }) .build()?; // Execute with automatic routing let grove_service = GroveExecutionService::new(llm_adapter); // Automatically routes to CryptoExpert let result = grove_service.execute( &grove, "How should we implement TLS certificate rotation?" ).await?; println!("Routed to: {}", result.selected_tree); println!("Agent: {}", result.selected_agent); println!("Confidence: {:.1}%", result.routing_confidence * 100.0); }
Grove Features:
- Intelligent routing: Keyword matching, semantic similarity, or performance-based selection
- Expert trees: Organize agents by domain (security, performance, frontend, backend)
- Fallback chains: Graceful degradation if no good match found
- Confidence scoring: Know how well the input matched the selected agent
- Dynamic learning: Performance-based routing improves over time
Routing Strategies:
KeywordMatch: Fast, rule-based routing (best for well-defined domains)SemanticSimilarity: Embedding-based context-aware routing (requires embedding model)PerformanceBased: Adaptive routing based on historical success rates
See examples/grove_routing.rs for a complete working example.
6. Stream Responses
Get real-time output:
#![allow(unused)] fn main() { let mut stream = paladin.execute_stream("Tell me a story").await?; while let Some(chunk) = stream.next().await { print!("{}", chunk?); } }
Common Patterns
Configuration from File
#![allow(unused)] fn main() { use paladin::config::ApplicationSettings; let config = ApplicationSettings::load()?; let paladin = PaladinBuilder::from_config(&config.paladin)?; }
Error Handling
#![allow(unused)] fn main() { match paladin.execute(input).await { Ok(response) => println!("Success: {}", response.content), Err(PaladinError::Timeout(secs)) => { eprintln!("Timed out after {} seconds", secs); } Err(PaladinError::LlmError(msg)) => { eprintln!("LLM error: {}", msg); } Err(e) => eprintln!("Error: {}", e), } }
Testing CLI Output
Paladin provides snapshot testing for CLI output consistency using insta:
#![allow(unused)] fn main() { use paladin::application::cli::formatters::table::TableFormatter; #[test] fn test_result_table() { let mut table = TableFormatter::new(); table .set_header(vec!["Agent", "Status", "Time"]) .add_row(vec!["Analyzer", "Success", "1.2s"]) .add_row(vec!["Generator", "Success", "0.8s"]); let output = table.render(); insta::assert_snapshot!("result_table", output); } }
Run tests and review snapshots:
# Run all tests
cargo test
# Review new/changed snapshots
cargo insta review
# Accept all snapshots
cargo insta accept
Snapshot testing ensures CLI output remains consistent across changes. See tests/cli/ for examples.
Async Context
Always run Paladins in an async context:
#[tokio::main] async fn main() { // Your Paladin code here }
Troubleshooting
"API key not found"
Ensure your .env file is in the project root and contains the correct variable name:
OPENAI_API_KEY=sk-...
"Connection timeout"
Check your network connection and API endpoint:
#![allow(unused)] fn main() { let llm_adapter = OpenAiAdapter::new() .with_timeout(Duration::from_secs(60)) // Increase timeout .build()?; }
"Rate limit exceeded"
Implement retry logic or use a rate limiter:
#![allow(unused)] fn main() { let config = PaladinConfig::default() .with_retry_attempts(3) .with_retry_delay(Duration::from_secs(5)); }
Example Projects
Check out complete examples in the examples/ directory:
basic_paladin.rs- Simple question answeringgarrison_in_memory.rs- Conversation with memoryarsenal_stdio_tools.rs- Tool integrationformation_sequential.rs- Multi-agent workflowsphalanx_parallel.rs- Concurrent processing
Learn More
- Installation Guide - Detailed setup for all platforms
- Configuration Guide - Advanced Paladin options
- Battalion Patterns - Multi-agent orchestration
- API Reference - Complete API documentation
Get Help
- Documentation: https://github.com/DF3NDR/paladin-dev-env/tree/main/docs
- Examples: https://github.com/DF3NDR/paladin-dev-env/tree/main/examples
- Issues: https://github.com/DF3NDR/paladin-dev-env/issues
Happy building with Paladin! 🏰