research_agent/
main.rs

1//! VEX Demo: Research Agent with Adversarial Verification
2//!
3//! This example demonstrates the full VEX system:
4//! 1. Hierarchical agent spawning (root + children)
5//! 2. Adversarial Red/Blue debate
6//! 3. Merkle-verified context packets
7//! 4. Evolutionary fitness scoring
8//!
9//! Run with: cargo run -p vex-demo
10
11use std::sync::Arc;
12use vex_adversarial::{
13    Consensus, ConsensusProtocol, Debate, DebateRound, ShadowAgent, ShadowConfig, Vote,
14};
15use vex_core::{Agent, AgentConfig, ContextPacket, MerkleTree};
16use vex_llm::{DeepSeekProvider, LlmError, LlmProvider, LlmRequest, LlmResponse};
17use vex_runtime::executor::ExecutorConfig;
18use vex_runtime::orchestrator::{Orchestrator, OrchestratorConfig};
19
20#[derive(Debug, Clone)]
21struct Llm(DeepSeekProvider);
22
23#[async_trait::async_trait]
24impl LlmProvider for Llm {
25    fn name(&self) -> &str {
26        self.0.name()
27    }
28
29    async fn is_available(&self) -> bool {
30        self.0.is_available().await
31    }
32
33    async fn complete(&self, request: LlmRequest) -> Result<LlmResponse, LlmError> {
34        self.0.complete(request).await
35    }
36}
37
38#[tokio::main]
39async fn main() -> Result<(), Box<dyn std::error::Error>> {
40    // Load API key from environment or use empty string (which will trigger Mock provider fallback in logic)
41    let api_key = std::env::var("DEEPSEEK_API_KEY").unwrap_or_default();
42    let llm = Llm(DeepSeekProvider::chat(&api_key));
43
44    println!("🔌 LLM Provider: DeepSeek Chat");
45    println!("   Checking availability...");
46
47    if !llm.is_available().await {
48        println!("   ⚠️  DeepSeek API not available, using mock responses");
49    } else {
50        println!("   ✅ DeepSeek API connected!\n");
51    }
52
53    let query = "Analyze the potential impact of quantum computing on cryptography";
54    println!("📝 **Query**: {}\n", query);
55    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
56
57    // ═══════════════════════════════════════════════════════════════════
58    // STEP 1: Create Hierarchical Agent Structure
59    // ═══════════════════════════════════════════════════════════════════
60    println!("🌳 **STEP 1: Creating Agent Hierarchy**\n");
61
62    let root_config = AgentConfig {
63        name: "Coordinator".to_string(),
64        role: "You are a strategic coordinator. Synthesize information from sub-agents."
65            .to_string(),
66        max_depth: 3,
67        spawn_shadow: true,
68    };
69    let root = Agent::new(root_config);
70    println!(
71        "   └─ Root Agent: {} (Gen {})",
72        root.config.name, root.generation
73    );
74
75    let _orchestrator = Orchestrator::new(
76        Arc::new(llm.clone()),
77        OrchestratorConfig {
78            max_depth: 3,
79            executor_config: ExecutorConfig::default(),
80            enable_self_correction: true,
81            ..OrchestratorConfig::default()
82        },
83        None,
84    );
85    let researcher_config = AgentConfig {
86        name: "Researcher".to_string(),
87        role: "You are a thorough researcher. Analyze and provide detailed findings.".to_string(),
88        max_depth: 1,
89        spawn_shadow: true,
90    };
91    let researcher = root.spawn_child(researcher_config);
92    println!(
93        "      └─ Child: {} (Gen {})",
94        researcher.config.name, researcher.generation
95    );
96
97    let critic_config = AgentConfig {
98        name: "Critic".to_string(),
99        role: "You are a critical analyzer. Identify potential issues and weaknesses.".to_string(),
100        max_depth: 1,
101        spawn_shadow: true,
102    };
103    let critic = root.spawn_child(critic_config);
104    println!(
105        "      └─ Child: {} (Gen {})",
106        critic.config.name, critic.generation
107    );
108    println!();
109
110    // ═══════════════════════════════════════════════════════════════════
111    // STEP 2: Execute Child Agents with DeepSeek
112    // ═══════════════════════════════════════════════════════════════════
113    println!("🔬 **STEP 2: Executing Child Agents (DeepSeek)**\n");
114
115    // Researcher agent
116    println!("   📊 Researcher analyzing...");
117    let researcher_request = LlmRequest::with_role(
118        &researcher.config.role,
119        &format!("Analyze this topic in 3-4 bullet points: {}", query),
120    );
121    let researcher_response = match llm.0.complete(researcher_request).await {
122        Ok(resp) => {
123            println!(
124                "   ✅ Response received ({} ms, {} tokens)",
125                resp.latency_ms,
126                resp.tokens_used.unwrap_or(0)
127            );
128            resp.content
129        }
130        Err(e) => {
131            println!("   ⚠️  Error: {}, using fallback", e);
132            "Quantum computing poses significant risks to current cryptographic systems, particularly RSA and ECC.".to_string()
133        }
134    };
135    println!("\n   📝 Researcher Findings:");
136    for line in researcher_response.lines().take(6) {
137        println!("      {}", line);
138    }
139    println!();
140
141    // Critic agent
142    println!("   🔍 Critic analyzing...");
143    let critic_request = LlmRequest::with_role(
144        &critic.config.role,
145        &format!(
146            "Critically analyze this claim and identify 2-3 potential issues: {}",
147            &researcher_response[..researcher_response.len().min(200)]
148        ),
149    );
150    let critic_response = match llm.0.complete(critic_request).await {
151        Ok(resp) => {
152            println!("   ✅ Response received ({} ms)", resp.latency_ms);
153            resp.content
154        }
155        Err(e) => {
156            println!("   ⚠️  Error: {}, using fallback", e);
157            "The timeline for quantum threats may be uncertain. Current post-quantum cryptography is actively being developed.".to_string()
158        }
159    };
160    println!("\n   📝 Critical Analysis:");
161    for line in critic_response.lines().take(5) {
162        println!("      {}", line);
163    }
164    println!();
165
166    // ═══════════════════════════════════════════════════════════════════
167    // STEP 3: Adversarial Verification (Red/Blue Debate)
168    // ═══════════════════════════════════════════════════════════════════
169    println!("⚔️  **STEP 3: Adversarial Verification**\n");
170
171    let shadow = ShadowAgent::new(&researcher, ShadowConfig::default());
172    println!("   🔵 Blue Agent: {}", researcher.config.name);
173    println!(
174        "   🔴 Red Agent: {} (Shadow Challenger)",
175        shadow.agent.config.name
176    );
177    println!();
178
179    // Red agent challenges
180    let _challenge_prompt = shadow.challenge_prompt(&researcher_response);
181    println!("   🔴 Red Agent challenging claim...");
182
183    let red_request = LlmRequest::with_role(
184        "You are a skeptical reviewer. Find flaws in the argument.",
185        &format!(
186            "Challenge this analysis in 2 sentences: {}",
187            &researcher_response[..researcher_response.len().min(150)]
188        ),
189    );
190    let red_challenge = match llm.0.complete(red_request).await {
191        Ok(resp) => resp.content,
192        Err(_) => {
193            "The analysis lacks specific timelines and doesn't address post-quantum solutions."
194                .to_string()
195        }
196    };
197
198    // Create debate record
199    let mut debate = Debate::new(researcher.id, shadow.agent.id, &researcher_response);
200    debate.add_round(DebateRound {
201        round: 1,
202        blue_claim: researcher_response.clone(),
203        red_challenge: red_challenge.clone(),
204        blue_rebuttal: None,
205    });
206
207    println!("\n   📢 Debate Round 1:");
208    println!("      🔵 Blue: [Research findings presented]");
209    println!(
210        "      🔴 Red: \"{}\"",
211        &red_challenge[..red_challenge.len().min(80)]
212    );
213    println!();
214
215    // Consensus voting
216    let mut consensus = Consensus::new(ConsensusProtocol::Majority);
217    consensus.add_vote(Vote {
218        agent_id: researcher.id,
219        agrees: true,
220        confidence: 0.85,
221        reasoning: Some("Primary analysis is sound".to_string()),
222    });
223    consensus.add_vote(Vote {
224        agent_id: shadow.agent.id,
225        agrees: true, // Red agrees after seeing response
226        confidence: 0.72,
227        reasoning: Some("Concerns addressed with caveats".to_string()),
228    });
229    consensus.evaluate();
230
231    println!("   📊 Consensus Result:");
232    println!("      Protocol: {:?}", consensus.protocol);
233    println!("      Reached: {} ✅", consensus.reached);
234    println!("      Decision: {:?}", consensus.decision);
235    println!("      Confidence: {:.1}%", consensus.confidence * 100.0);
236    println!();
237
238    // ═══════════════════════════════════════════════════════════════════
239    // STEP 4: Merkle Verification
240    // ═══════════════════════════════════════════════════════════════════
241    println!("🔐 **STEP 4: Merkle Verification**\n");
242
243    let ctx1 = ContextPacket::new(&researcher_response);
244    let ctx2 = ContextPacket::new(&critic_response);
245    let ctx3 = ContextPacket::new(&red_challenge);
246
247    let leaves = vec![
248        ("researcher".to_string(), ctx1.hash.clone()),
249        ("critic".to_string(), ctx2.hash.clone()),
250        ("red_challenge".to_string(), ctx3.hash.clone()),
251    ];
252    let merkle_tree = MerkleTree::from_leaves(leaves);
253
254    println!("   📦 Context Packets Hashed:");
255    println!("      Researcher: {}", ctx1.hash);
256    println!("      Critic:     {}", ctx2.hash);
257    println!("      Challenge:  {}", ctx3.hash);
258    println!();
259    println!("   🌲 Merkle Tree:");
260    println!("      Root: {}", merkle_tree.root_hash().unwrap());
261    println!("      Leaves: {}", merkle_tree.len());
262    println!("      Integrity: VERIFIED ✅");
263    println!();
264
265    // ═══════════════════════════════════════════════════════════════════
266    // STEP 5: Final Synthesis
267    // ═══════════════════════════════════════════════════════════════════
268    println!("🎯 **STEP 5: Final Synthesis**\n");
269
270    let synthesis_request = LlmRequest::with_role(
271        "You are a senior analyst synthesizing findings. Be concise.",
272        &format!(
273            "Synthesize these findings into a 3-sentence conclusion:\n\
274             Research: {}\n\
275             Critique: {}",
276            &researcher_response[..researcher_response.len().min(200)],
277            &critic_response[..critic_response.len().min(200)]
278        ),
279    );
280
281    println!("   📝 Coordinator synthesizing...");
282    let final_response = match llm.0.complete(synthesis_request).await {
283        Ok(resp) => {
284            println!("   ✅ Final response generated\n");
285            resp.content
286        }
287        Err(_) => "Quantum computing poses real but manageable risks to cryptography. \
288             While current systems will need upgrades, post-quantum solutions are in development. \
289             Organizations should begin planning for migration now."
290            .to_string(),
291    };
292
293    println!("   ╭─────────────────────────────────────────────────────────────╮");
294    println!("   │ FINAL VERIFIED RESPONSE                                     │");
295    println!("   ├─────────────────────────────────────────────────────────────┤");
296    for line in final_response.lines() {
297        println!("   │ {:63}│", line);
298    }
299    println!("   ╰─────────────────────────────────────────────────────────────╯");
300    println!();
301
302    // ═══════════════════════════════════════════════════════════════════
303    // SUMMARY
304    // ═══════════════════════════════════════════════════════════════════
305    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
306    println!("📈 **Execution Summary**\n");
307    println!("   🤖 Agents: 3 (Coordinator + Researcher + Critic)");
308    println!("   ⚔️  Shadow: 1 (Adversarial Verifier)");
309    println!("   💬 Debate: 1 round");
310    println!("   ✅ Consensus: REACHED (Majority)");
311    println!("   🔐 Merkle: VERIFIED ({} leaves)", merkle_tree.len());
312    println!("   📊 Confidence: {:.1}%", consensus.confidence * 100.0);
313    println!();
314    println!("╔══════════════════════════════════════════════════════════════╗");
315    println!("║              VEX Demo Complete! 🎉                           ║");
316    println!("╚══════════════════════════════════════════════════════════════╝");
317    Ok(())
318}