Skip to main content

agent_swarm/
main.rs

1//! AgentSwarm Orchestrator - Rust Implementation
2//!
3//! Inspired by kimik25.com - Moonshot AI's Kimi K2.5 with agent swarm capabilities.
4//! This Rust implementation focuses on memory safety, zero-cost abstractions, and
5//! high-performance concurrent agent management.
6//!
7//! Features:
8//! - Memory-safe with Rust's ownership model
9//! - Zero-cost abstractions for efficient agent coordination
10//! - 256K context window support
11//! - Multiple swarm topologies
12//! - Compile-time guaranteed thread safety
13
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::Arc;
17use std::time::{SystemTime, UNIX_EPOCH};
18use tokio::sync::{mpsc, RwLock};
19use uuid::Uuid;
20
21/// Supported swarm topology types
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Topology {
24    /// Tree-based coordination with leader election
25    Hierarchical,
26    /// Peer-to-peer communication with gossip protocol
27    Mesh,
28    /// Combines hierarchical and mesh patterns
29    Hybrid,
30}
31
32impl Topology {
33    pub fn as_str(&self) -> &'static str {
34        match self {
35            Topology::Hierarchical => "hierarchical",
36            Topology::Mesh => "mesh",
37            Topology::Hybrid => "hybrid",
38        }
39    }
40}
41
42/// Configuration for initializing an agent swarm
43#[derive(Debug, Clone)]
44pub struct SwarmConfig {
45    /// Maximum number of agents in the swarm
46    pub max_agents: usize,
47    /// Maximum token context size (256K like Kimi K2.5)
48    pub context_window: usize,
49    /// Swarm topology pattern
50    pub topology: Topology,
51}
52
53impl Default for SwarmConfig {
54    fn default() -> Self {
55        Self {
56            max_agents: 100,
57            context_window: 256_000,
58            topology: Topology::Mesh,
59        }
60    }
61}
62
63/// Message structure for inter-agent communication
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct TaskMessage {
66    /// Unique message identifier
67    pub id: String,
68    /// Message type identifier
69    #[serde(rename = "type")]
70    pub msg_type: String,
71    /// Message data payload
72    pub payload: serde_json::Value,
73    /// Source agent ID
74    #[serde(rename = "from")]
75    pub from_agent: String,
76    /// Target agent ID
77    #[serde(rename = "to")]
78    pub to_agent: String,
79    /// Message timestamp
80    pub timestamp: u64,
81}
82
83impl TaskMessage {
84    /// Create a new task message
85    pub fn new(
86        msg_type: impl Into<String>,
87        payload: serde_json::Value,
88        from_agent: impl Into<String>,
89    ) -> Self {
90        let now = SystemTime::now()
91            .duration_since(UNIX_EPOCH)
92            .unwrap()
93            .as_secs();
94
95        Self {
96            id: Uuid::new_v4().to_string(),
97            msg_type: msg_type.into(),
98            payload,
99            from_agent: from_agent.into(),
100            to_agent: String::new(),
101            timestamp: now,
102        }
103    }
104
105    /// Set the target agent for this message
106    pub fn to(mut self, agent: impl Into<String>) -> Self {
107        self.to_agent = agent.into();
108        self
109    }
110}
111
112/// Represents a single autonomous agent in the swarm
113#[derive(Debug, Clone)]
114pub struct Agent {
115    /// Unique agent identifier
116    pub id: String,
117    /// List of agent capabilities
118    pub capabilities: Vec<String>,
119    /// Current context tokens used by this agent
120    pub context_used: Arc<RwLock<usize>>,
121}
122
123impl Agent {
124    /// Create a new agent instance
125    pub fn new(id: impl Into<String>, capabilities: Vec<String>) -> Self {
126        Self {
127            id: id.into(),
128            capabilities,
129            context_used: Arc::new(RwLock::new(0)),
130        }
131    }
132
133    /// Dispatch a message from this agent
134    pub fn dispatch_message(&self, msg_type: &str, payload: serde_json::Value) -> TaskMessage {
135        TaskMessage::new(msg_type, payload, &self.id)
136    }
137}
138
139/// Main coordinator for managing a swarm of autonomous agents
140pub struct AgentSwarm {
141    /// Swarm configuration
142    config: SwarmConfig,
143    /// Registered agents in the swarm
144    agents: Arc<RwLock<HashMap<String, Agent>>>,
145    /// Token usage counter
146    tokens_used: Arc<RwLock<usize>>,
147    /// Message channel for routing
148    message_tx: mpsc::Sender<TaskMessage>,
149}
150
151impl AgentSwarm {
152    /// Create a new agent swarm with the specified configuration
153    pub fn new(config: SwarmConfig) -> Self {
154        let (message_tx, mut message_rx) = mpsc::channel::<TaskMessage>(1000);
155        let agents = Arc::new(RwLock::new(HashMap::new()));
156        let tokens_used = Arc::new(RwLock::new(0));
157
158        // Spawn message routing task
159        let agents_clone = Arc::clone(&agents);
160        let tokens_clone = Arc::clone(&tokens_used);
161        let context_limit = config.context_window;
162
163        tokio::spawn(async move {
164            while let Some(msg) = message_rx.recv().await {
165                let agents = agents_clone.read().await;
166                if agents.contains_key(&msg.to_agent) {
167                    // Process message (in a real implementation, would queue to agent)
168                    let mut tokens = tokens_clone.write().await;
169                    let estimated = Self::estimate_tokens(&msg);
170                    *tokens += estimated;
171
172                    if *tokens > context_limit {
173                        log::warn!(
174                            "[AgentSwarm] Context window at {}/{} tokens",
175                            *tokens,
176                            context_limit
177                        );
178                    }
179                }
180            }
181        });
182
183        log::info!(
184            "[AgentSwarm] Initialized swarm with {} max agents, {} context window",
185            config.max_agents,
186            config.context_window
187        );
188
189        Self {
190            config,
191            agents,
192            tokens_used,
193            message_tx,
194        }
195    }
196
197    /// Spawn a new agent with the specified capabilities
198    pub async fn spawn_agent(&self, id: impl Into<String>, capabilities: Vec<String>) -> Option<Agent> {
199        let id = id.into();
200        let mut agents = self.agents.write().await;
201
202        if agents.len() >= self.config.max_agents {
203            log::warn!(
204                "[AgentSwarm] Max agents ({}) reached, cannot spawn {}",
205                self.config.max_agents,
206                id
207            );
208            return None;
209        }
210
211        let agent = Agent::new(&id, capabilities);
212        log::info!("[AgentSwarm] Spawned agent {} with capabilities: {:?}", id, agent.capabilities);
213        agents.insert(id.clone(), agent.clone());
214
215        Some(agent)
216    }
217
218    /// Broadcast a message to all agents except the sender
219    pub async fn broadcast_message(
220        &self,
221        from_agent: &str,
222        msg_type: &str,
223        payload: serde_json::Value,
224    ) -> usize {
225        let agents = self.agents.read().await;
226        let mut count = 0;
227
228        for (id, _) in agents.iter() {
229            if id != from_agent {
230                let msg = TaskMessage::new(msg_type, payload.clone(), from_agent).to(id);
231                let _ = self.message_tx.send(msg).await;
232                count += 1;
233            }
234        }
235
236        log::info!("[AgentSwarm] Broadcasted {} message to {} agents", msg_type, count);
237        count
238    }
239
240    /// Retrieve an agent by ID
241    pub async fn get_agent(&self, id: &str) -> Option<Agent> {
242        let agents = self.agents.read().await;
243        agents.get(id).cloned()
244    }
245
246    /// Estimate token count for a message (rough approximation)
247    fn estimate_tokens(msg: &TaskMessage) -> usize {
248        serde_json::to_string(msg)
249            .map(|s| s.len() / 4)
250            .unwrap_or(100)
251    }
252
253    /// Get current swarm statistics
254    pub async fn get_stats(&self) -> SwarmStats {
255        let agents = self.agents.read().await;
256        let tokens = *self.tokens_used.read().await;
257
258        SwarmStats {
259            agent_count: agents.len(),
260            max_agents: self.config.max_agents,
261            tokens_used: tokens,
262            context_window: self.config.context_window,
263            topology: self.config.topology.as_str().to_string(),
264            utilization_percent: (agents.len() as f64 / self.config.max_agents as f64) * 100.0,
265        }
266    }
267
268    /// Gracefully shutdown the swarm
269    pub async fn shutdown(&self) {
270        log::info!("[AgentSwarm] Shutting down swarm...");
271        let mut agents = self.agents.write().await;
272        agents.clear();
273        log::info!("[AgentSwarm] Shutdown complete");
274    }
275}
276
277/// Swarm statistics snapshot
278#[derive(Debug, Clone, Serialize)]
279pub struct SwarmStats {
280    pub agent_count: usize,
281    pub max_agents: usize,
282    pub tokens_used: usize,
283    pub context_window: usize,
284    pub topology: String,
285    pub utilization_percent: f64,
286}
287
288#[tokio::main]
289async fn main() -> Result<(), Box<dyn std::error::Error>> {
290    println!("=== AgentSwarm Rust Demo ===\n");
291
292    // Initialize swarm inspired by kimik25.com
293    let config = SwarmConfig {
294        max_agents: 100,
295        context_window: 256_000, // 256K like Kimi K2.5
296        topology: Topology::Mesh,
297    };
298
299    let swarm = AgentSwarm::new(config);
300
301    // Spawn specialized agents
302    let coordinator = swarm
303        .spawn_agent(
304            "coordinator",
305            vec!["routing".to_string(), "scheduling".to_string()],
306        )
307        .await;
308
309    let processor = swarm
310        .spawn_agent(
311            "processor",
312            vec!["data-analysis".to_string(), "transform".to_string()],
313        )
314        .await;
315
316    let _analyzer = swarm
317        .spawn_agent(
318            "analyzer",
319            vec!["pattern-detection".to_string(), "reporting".to_string()],
320        )
321        .await;
322
323    // Create and dispatch messages
324    if let Some(agent) = &coordinator {
325        let msg = agent.dispatch_message(
326            "coordinate",
327            serde_json::json!({"task": "initialize-pipeline"}),
328        );
329        let _ = swarm.message_tx.send(msg.to("coordinator")).await;
330    }
331
332    if let Some(agent) = &processor {
333        let msg = agent.dispatch_message("process", serde_json::json!({"data": "sample-input"}));
334        let _ = swarm.message_tx.send(msg.to("processor")).await;
335    }
336
337    // Broadcast to all agents
338    swarm
339        .broadcast_message(
340            "system",
341            "swarm-update",
342            serde_json::json!({"status": "operational", "agents": 3}),
343        )
344        .await;
345
346    // Display stats
347    let stats = swarm.get_stats().await;
348    println!("\nSwarm Stats: {}", serde_json::to_string_pretty(&stats)?);
349
350    // Demonstrate agent retrieval
351    if let Some(agent) = swarm.get_agent("processor").await {
352        println!(
353            "\nRetrieved agent: {} with capabilities: {:?}",
354            agent.id, agent.capabilities
355        );
356    }
357
358    // Clean shutdown
359    swarm.shutdown().await;
360
361    println!("\n=== Demo Complete ===");
362    println!("Inspired by: https://kimik25.com");
363    println!("Kimi K2.5 - 1T-parameter native multimodal model with agent swarm capabilities");
364
365    Ok(())
366}