""" AgentLens Customer Support Example — Realistic support ticket workflow. Demonstrates: - Ticket classification with ROUTING decisions - Specialist routing with TOOL_SELECTION - Escalation decisions with ESCALATION type - Error handling — traces capture exceptions automatically - Multiple real-world decision patterns Usage: pip install vectry-agentlens python customer_support_agent.py """ import agentlens import time import random # Initialize agentlens.init( api_key="your-api-key-here", endpoint="http://localhost:4200", ) # Simulated ticket data TICKETS = [ { "id": "TKT-4021", "subject": "Cannot access billing portal after password reset", "priority": "high", "customer_tier": "enterprise", "body": "After resetting my password, I get a 403 error on the billing page. " "I need to update our payment method before end of month.", }, { "id": "TKT-4022", "subject": "Feature request: dark mode for dashboard", "priority": "low", "customer_tier": "free", "body": "Would love to have a dark mode option. My eyes hurt during late-night sessions.", }, { "id": "TKT-4023", "subject": "API returning 500 errors intermittently", "priority": "critical", "customer_tier": "enterprise", "body": "Our production integration is failing ~20% of requests with 500 errors. " "Started about 2 hours ago. This is blocking our release.", }, ] def simulate_llm(prompt: str, delay: float = 0.15) -> str: """Fake LLM — replace with real calls.""" time.sleep(delay) return f"[Response to: {prompt[:60]}]" def process_ticket(ticket: dict) -> None: """Process a single support ticket through the agent pipeline.""" with agentlens.trace( "customer-support-bot", tags=["support", ticket["priority"], ticket["customer_tier"]], ): # Step 1: Classify the ticket agentlens.log_decision( type="ROUTING", chosen={ "name": "classify_ticket", "confidence": 0.91, "params": { "ticket_id": ticket["id"], "predicted_category": ( "billing" if "billing" in ticket["subject"].lower() else "bug" if "error" in ticket["body"].lower() or "500" in ticket["body"] else "feature_request" ), }, }, alternatives=[ { "name": "ask_customer_for_clarification", "confidence": 0.2, "reason_rejected": "Ticket subject and body are clear enough", }, ], reasoning=f"Ticket '{ticket['subject']}' clearly maps to a known category.", ) classification = simulate_llm(f"Classify: {ticket['subject']}") # Step 2: Route to specialist is_critical = ticket["priority"] in ("critical", "high") is_enterprise = ticket["customer_tier"] == "enterprise" if is_critical and is_enterprise: specialist = "senior_engineer" elif is_critical: specialist = "engineer" elif "billing" in ticket["subject"].lower(): specialist = "billing_team" else: specialist = "general_support" agentlens.log_decision( type="ROUTING", chosen={ "name": specialist, "confidence": 0.87, "params": { "ticket_id": ticket["id"], "priority": ticket["priority"], "sla_minutes": 30 if is_enterprise else 240, }, }, alternatives=[ { "name": "general_support", "confidence": 0.4, "reason_rejected": "Ticket requires specialized handling" if specialist != "general_support" else "This is general support already", }, ], reasoning=f"Priority={ticket['priority']}, Tier={ticket['customer_tier']} -> route to {specialist}.", ) # Step 3: Specialist handles ticket (nested trace) with agentlens.trace(f"specialist-{specialist}", tags=[specialist]): # Tool selection for the specialist agentlens.log_decision( type="TOOL_SELECTION", chosen={ "name": "search_knowledge_base", "confidence": 0.82, "params": {"query": ticket["subject"], "limit": 5}, }, alternatives=[ { "name": "search_past_tickets", "confidence": 0.7, "reason_rejected": "KB is more authoritative for known issues", }, { "name": "check_status_page", "confidence": 0.6, "reason_rejected": "Already checked — no ongoing incidents posted", }, ], reasoning="Knowledge base has resolution guides for common issues.", ) kb_result = simulate_llm(f"Search KB for: {ticket['subject']}") # Step 4: Escalation decision for critical tickets if ticket["priority"] == "critical": agentlens.log_decision( type="ESCALATION", chosen={ "name": "escalate_to_engineering", "confidence": 0.94, "params": { "severity": "P1", "team": "platform-reliability", "ticket_id": ticket["id"], }, }, alternatives=[ { "name": "resolve_at_support_level", "confidence": 0.15, "reason_rejected": "500 errors suggest infrastructure issue beyond support scope", }, ], reasoning="Intermittent 500s on enterprise account = immediate P1 escalation.", ) # Simulate escalation failure for the critical ticket (shows error handling) if random.random() < 0.3: raise RuntimeError( f"Escalation service unavailable for {ticket['id']}" ) # Generate response response = simulate_llm( f"Draft response for {ticket['id']}: {ticket['subject']}", delay=0.3, ) print(f" [{ticket['id']}] Processed -> routed to {specialist}") # Process all tickets print("Processing support tickets...\n") for ticket in TICKETS: try: process_ticket(ticket) except Exception as e: # The trace context manager captures the error automatically print(f" [{ticket['id']}] Error during processing: {e}") # Shutdown agentlens.shutdown() print("\nDone! Check AgentLens dashboard for 'customer-support-bot' traces.") print("Look for the ERROR trace — it shows how failures are captured.")