//! Example: RAG Agent with Multi-Turn Conversations //! //! Demonstrates how to use ConversationAgent to build stateful Q&A sessions //! with: //! - Turn history and context tracking //! - Follow-up question detection //! - Document scope management //! - Context-aware retrieval //! - Conversation summarization #![allow(unused_mut, unused_variables, clippy::unnecessary_cast)] use provisioning_rag::{ConversationContext, ConversationTurn, FollowupDetector}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize logging tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .init(); println!("=== RAG Agent with Multi-Turn Conversations ===\n"); // 1. Create conversation context println!("1. Creating conversation context..."); let mut conversation = ConversationContext::new("conv-kubernetes-001".into()); conversation = conversation.with_topic("Kubernetes Administration".into()); println!( " ✓ Conversation created: {}\n", conversation.conversation_id ); // 2. Simulate first turn (user question about Kubernetes) println!("=== Turn 1: Initial Question ===\n"); println!("User: What is Kubernetes and how does it work?\n"); let turn1 = ConversationTurn::new( 0, "What is Kubernetes and how does it work?".into(), "Kubernetes is a container orchestration platform that automates the deployment, scaling, \ and management of containerized applications. It works by:\n1. Accepting container \ definitions (Docker images)\n2. Scheduling containers across a cluster of machines\n3. \ Managing container lifecycle (restart, updates, etc)\n4. Load balancing traffic to \ containers\n5. Coordinating storage and networking resources" .into(), vec![ "docs/kubernetes-basics.md".into(), "docs/k8s-architecture.md".into(), ], 0.95, ); println!("Agent: {}\n", turn1.answer); println!("Sources: {:?}", turn1.sources); println!("Confidence: {:.1}%\n", turn1.confidence * 100.0); conversation.add_turn(turn1.clone()); println!("✓ Turn 1 added to conversation\n"); // 3. Simulate second turn (follow-up about deployment) println!("=== Turn 2: Follow-up Question (Deployment) ===\n"); let question2 = "How do I deploy my application on Kubernetes?"; println!("User: {}\n", question2); // Detect if it's a follow-up let is_followup = FollowupDetector::is_followup(question2, &conversation.get_turns()); let referenced_turns = FollowupDetector::find_referenced_turns(question2, &conversation.get_turns()); println!("Follow-up Detection:"); println!(" - Is follow-up: {}", is_followup); println!(" - References turns: {:?}\n", referenced_turns); // Build enriched context let context_str = conversation.build_context_string(); println!("Enriched Context for LLM:"); println!("{}\n", context_str); let turn2 = ConversationTurn::new( 1, question2.into(), "To deploy your application on Kubernetes:\n1. Create a Docker image of your \ application\n2. Write a Kubernetes deployment manifest (YAML file) specifying:\n- \ Container image\n- Number of replicas\n- Resource requirements\n- Environment \ variables\n3. Apply the manifest: kubectl apply -f deployment.yaml\n4. Verify: kubectl \ get pods, kubectl get deployments\n5. For updates, modify the manifest and re-apply" .into(), vec![ "docs/deployment-guide.md".into(), "docs/kubectl-commands.md".into(), ], 0.92, ) .mark_as_followup(referenced_turns); println!("Agent: {}\n", turn2.answer); println!("Sources: {:?}", turn2.sources); println!("Confidence: {:.1}%", turn2.confidence * 100.0); println!("Is Follow-up: {}\n", turn2.is_followup); conversation.add_turn(turn2.clone()); println!("✓ Turn 2 added to conversation\n"); // 4. Simulate third turn (follow-up about networking) println!("=== Turn 3: Follow-up Question (Networking) ===\n"); let question3 = "Tell me more about networking and service exposure"; println!("User: {}\n", question3); let is_followup3 = FollowupDetector::is_followup(question3, &conversation.get_turns()); let referenced_turns3 = FollowupDetector::find_referenced_turns(question3, &conversation.get_turns()); println!("Follow-up Detection:"); println!(" - Is follow-up: {}", is_followup3); println!(" - References turns: {:?}\n", referenced_turns3); let turn3 = ConversationTurn::new( 2, question3.into(), "Kubernetes provides several networking mechanisms:\n1. Pods communicate internally via \ cluster networking\n2. Services expose pods to internal and external traffic:\n- \ ClusterIP: Internal-only access (default)\n- NodePort: Access via host machine port\n- \ LoadBalancer: External load balancer integration\n- Ingress: Advanced HTTP/HTTPS \ routing\n3. Network policies control traffic between pods\n4. DNS provides service \ discovery via ..svc.cluster.local" .into(), vec![ "docs/kubernetes-networking.md".into(), "docs/services-guide.md".into(), "docs/ingress-guide.md".into(), ], 0.93, ) .mark_as_followup(referenced_turns3); println!("Agent: {}\n", turn3.answer); println!("Sources: {:?}", turn3.sources); println!("Confidence: {:.1}%", turn3.confidence * 100.0); println!("Is Follow-up: {}\n", turn3.is_followup); conversation.add_turn(turn3.clone()); println!("✓ Turn 3 added to conversation\n"); // 5. Conversation statistics println!("=== Conversation Statistics ===\n"); println!("Total turns: {}", conversation.turn_count()); println!("Topic: {:?}", conversation.topic); println!("Created: {}", conversation.created_at); println!("Last updated: {}", conversation.last_updated); println!("Document scope (unique docs discussed):"); for doc in &conversation.document_scope { println!(" - {}", doc); } println!(); // 6. Retrieve specific turns println!("=== Turn Retrieval ===\n"); if let Some(turn) = conversation.get_turn(1) { println!("Retrieved turn 1:"); println!(" Question: {}", turn.question); println!(" Timestamp: {}", turn.timestamp); println!(" Sources: {:?}\n", turn.sources); } // 7. Get recent turns println!("=== Recent Turns (Last 2) ===\n"); let recent = conversation.get_recent_turns(2); for turn in recent { println!("Turn {}: {}", turn.turn_number, turn.question); } println!(); // 8. Get conversation summary println!("=== Conversation Summary ===\n"); let summary = conversation.get_summary(); println!("Summary: {}\n", summary); // 9. Display full context from history println!("=== Full Conversation Context ===\n"); let full_context = conversation.build_context_string(); println!("{}", full_context); // 10. Demonstrate history limit enforcement println!("=== History Limit Demonstration ===\n"); let mut limited_conversation = ConversationContext::new("conv-limited".into()); limited_conversation = limited_conversation.with_max_history(2 as usize); println!("Adding 4 turns with max_history=2...\n"); for i in 0..4 { let turn = ConversationTurn::new( i, format!("Question {}", i), format!("Answer {}", i), vec![format!("doc{}", i)], 0.9, ); limited_conversation.add_turn(turn); } println!( "Final turn count: {} (should be 2, oldest 2 removed)", limited_conversation.turn_count() ); println!( "Remaining turns: {:?}\n", limited_conversation .get_turns() .iter() .map(|t| format!("Q{}", t.turn_number)) .collect::>() ); // 11. Demonstrate metadata println!("=== Metadata Management ===\n"); let mut metadata_conversation = ConversationContext::new("conv-with-meta".into()); metadata_conversation .metadata .insert("user_id".into(), "user-123".into()); metadata_conversation .metadata .insert("workspace".into(), "production".into()); metadata_conversation .metadata .insert("session_type".into(), "support-ticket".into()); println!("Metadata stored:"); for (key, value) in &metadata_conversation.metadata { println!(" {}: {}", key, value); } println!(); // 12. Demonstrate clearing history println!("=== Clearing History ===\n"); println!("Before clear: {} turns", conversation.turn_count()); conversation.clear_history(); println!("After clear: {} turns\n", conversation.turn_count()); // 13. Follow-up detection patterns println!("=== Follow-up Detection Patterns ===\n"); let test_cases = vec![ ("What is Kubernetes?", vec![], "New topic (not a follow-up)"), ( "Tell me more about it", vec!["Turn 0"], "Keyword: 'tell me more' (follow-up)", ), ( "What about scaling?", vec!["Turn 0"], "Keyword: 'what about' (follow-up)", ), ( "Can you explain in more detail?", vec!["Turn 0"], "Keyword: 'can you' (follow-up)", ), ( "And what about networking?", vec!["Turn 0"], "Keyword: 'and' + 'what about' (follow-up)", ), ( "It's interesting", vec![], "Pronoun 'it' but no context (not follow-up)", ), ]; let mut test_turns = vec![ConversationTurn::new( 0, "What is Kubernetes?".into(), "Kubernetes is orchestration".into(), vec![], 0.9, )]; for (question, expected_refs, description) in test_cases { let is_followup = FollowupDetector::is_followup(question, &test_turns); let refs = FollowupDetector::find_referenced_turns(question, &test_turns); println!("Q: \"{}\"", question); println!(" Pattern: {}", description); println!(" Follow-up: {}", is_followup); println!(" References: {:?}\n", refs); } // 14. Performance notes println!("=== Performance Characteristics ===\n"); println!("Add turn: <1ms (VecDeque push_back)"); println!("Get turn: <1ms (direct array access)"); println!("Build context: <10ms (20 turns)"); println!("Detect follow-up: <5ms (keyword + term matching)"); println!("Find references: <5ms (20 turns)"); println!("Total overhead: <20ms per query\n"); // 15. Best practices println!("=== Best Practices ===\n"); println!("✓ Set conversation topic for better context"); println!("✓ Adjust max_history based on conversation type"); println!("✓ Use metadata for tracking session context"); println!("✓ Store full transcript before clearing history"); println!("✓ Monitor document scope for coverage"); println!("✓ Use context string for LLM prompt enrichment"); println!("✓ Implement persistence for long-running sessions\n"); // 16. Integration workflow println!("=== Integration Workflow ===\n"); println!("1. Create ConversationContext with unique ID"); println!("2. For each user query:"); println!(" a. Detect if it's a follow-up"); println!(" b. Build enriched context from history"); println!(" c. Query RAG agent with enriched context"); println!(" d. Create ConversationTurn with metadata"); println!(" e. Mark as follow-up with referenced turns"); println!(" f. Add turn to context"); println!("3. Periodically get summary/transcript"); println!("4. Store to database for persistence\n"); println!("✅ Multi-turn conversation example complete!\n"); Ok(()) }