What Is an AI Agent Demo That Sells?
A demo that sells isn't a technical showcase—it's a narrative experience that makes the prospect feel the transformation your AI agent delivers. Most developer demos fail because they lead with architecture diagrams, latency metrics, or raw model outputs. A compelling proof leads with relief: the moment the prospect realizes a painful manual workflow just evaporated.
Think of it as a three-act play. Act one introduces friction—a real, quantified problem your audience lives daily. Act two introduces your agent as the protagonist that absorbs that friction. Act three closes with a before/after contrast so stark that the prospect mentally calculates the ROI before you even mention pricing.
Technically, a selling demo is a curated subset of your agent's capabilities wrapped in a human-readable interface, often with live tracing, explicit reasoning display, and a tight feedback loop. It's not your full production system. It's the highlight reel engineered to create a single, unforgettable "aha" moment.
Why Compelling Demos Matter
Stakeholders don't buy technology. They buy resolved uncertainty. An AI agent demo that sells bridges the gap between abstract capability claims and visceral belief. When a VP of support watches an agent triage, route, and partially resolve a complex ticket in 90 seconds—something her team currently takes 4 hours to handle—the technical objections fade into logistics.
Three concrete reasons this matters:
- Shortened sales cycles: A well-constructed demo answers 70% of technical objections before they're voiced, collapsing multi-week evaluation into a single meeting
- Stakeholder alignment: Non-technical decision-makers (CXOs, department heads) anchor on the demo's emotional arc, becoming internal champions who push for adoption
- Competitive differentiation: When competitors show slide decks, your live, interactive proof—especially one that handles their own data on the fly—creates an asymmetry they can't match with a whitepaper
The demo also serves as an internal forcing function. Building one forces you to crystallize your agent's core value proposition into a 10-minute experience, which clarifies product strategy more than any roadmap document ever will.
Anatomy of a Demo That Converts
Every converting demo shares four layers. Miss one, and the demo becomes a technical curiosity instead of a purchasing trigger.
Layer 1: The Hook (Problem Framing)
Open with the prospect's world before your agent. Use a real scenario—ideally one the prospect described in discovery calls. Show a messy email thread, a convoluted CRM entry, a support ticket with 17 back-and-forths. Quantify the pain: "This takes your team 45 minutes per occurrence, and you see 200 of these per month. That's 150 hours of human cognition spent on pattern-matching, not problem-solving."
The hook succeeds when someone in the room mutters, "That's exactly what we deal with."
Layer 2: The Agent's Reasoning (Transparency)
This is where most developer demos go wrong. They show inputs and outputs but hide the reasoning. Prospects need to see how your agent thinks. Display the chain of decisions—tool selection, parameter derivation, intermediate results, confidence scores—in a clean, non-intimidating UI. When the agent pauses to "decide" something, narrate it: "It's now recognizing that this invoice mentions a PO number, so it's fetching the related contract before proceeding."
Transparency builds trust. A black-box demo breeds skepticism.
Layer 3: The Intervention (Human-in-the-Loop)
Show a deliberate hand-off point. The agent shouldn't pretend to solve everything—it should recognize its boundary and escalate gracefully. "The agent has resolved the line items but detected a pricing discrepancy that exceeds its authorization threshold. It's now drafting a summary for a human reviewer and pausing." This demonstrates maturity, not weakness. Enterprises buy agents that know when to stop.
Layer 4: The Close (Measurable Resolution)
End with concrete metrics: time saved, steps eliminated, accuracy achieved. Show the final state—the resolved ticket, the updated CRM, the generated report—side by side with the original chaos from Layer 1. Let the contrast sit in silence for 10 seconds. That silence is where purchase intent forms.
Building a Selling Demo: Complete Implementation
Let's build a Customer Support Triage Agent demo that embodies all four layers. We'll use Python with an LLM provider, tool-calling, and a lightweight web UI so prospects can interact live. The agent reads a support email, classifies intent, extracts entities, checks order status via a mock API, and either auto-resolves or escalates.
Project Structure
demo-agent/
├── agent/
│ ├── __init__.py
│ ├── core.py # Agent loop, tool calling, reasoning trace
│ ├── tools.py # Mock external APIs (order lookup, refund, etc.)
│ └── prompts.py # System prompts and few-shot examples
├── ui/
│ ├── app.py # Streamlit or FastAPI + simple frontend
│ └── static/
│ └── style.css
├── examples/
│ └── sample_tickets.json # Realistic support scenarios
├── requirements.txt
└── run_demo.py # Entry point
Step 1: Define the Tools (Mock Services)
Real demos use actual integrations. For a portable proof, we mock them with realistic latency and responses. The key is fidelity: the mock should return data shaped exactly like the real API, so prospects can mentally substitute their own systems.
# agent/tools.py
import json
import time
from datetime import datetime, timedelta
from typing import Optional
from dataclasses import dataclass, field
@dataclass
class OrderResult:
order_id: str
status: str # 'shipped', 'processing', 'delivered', 'canceled'
customer_name: str
items: list
total: float
delivery_date: Optional[str]
tracking_number: Optional[str]
@dataclass
class RefundResult:
eligible: bool
max_refund: float
reason: str
requires_approval: bool
# Simulated database
MOCK_ORDERS = {
"ORD-7821": {
"order_id": "ORD-7821",
"status": "delivered",
"customer_name": "Acme Corp",
"items": [{"sku": "SKU-441", "name": "Enterprise License", "qty": 50, "unit_price": 199.00}],
"total": 9950.00,
"delivery_date": "2025-02-14",
"tracking_number": "1Z999AA10123456784"
},
"ORD-7822": {
"order_id": "ORD-7822",
"status": "shipped",
"customer_name": "Beta Ltd",
"items": [{"sku": "SKU-442", "name": "Support Add-on", "qty": 1, "unit_price": 2500.00}],
"total": 2500.00,
"delivery_date": None,
"tracking_number": "1Z999AA10123456785"
},
"ORD-7823": {
"order_id": "ORD-7823",
"status": "processing",
"customer_name": "Gamma Inc",
"items": [{"sku": "SKU-443", "name": "Cloud Storage Tier 3", "qty": 1, "unit_price": 1200.00}],
"total": 1200.00,
"delivery_date": None,
"tracking_number": None
}
}
def lookup_order(order_id: str) -> dict:
"""Simulates an internal order management API call with realistic latency."""
time.sleep(0.3) # 300ms feels real but not sluggish
order = MOCK_ORDERS.get(order_id.upper())
if order:
return {"success": True, "data": order}
return {"success": False, "error": f"Order {order_id} not found", "suggested_action": "verify_order_id"}
def check_refund_eligibility(order_id: str, reason: str) -> dict:
"""Simulates refund policy engine lookup."""
time.sleep(0.2)
order = MOCK_ORDERS.get(order_id.upper())
if not order:
return {"eligible": False, "reason": "Order not found"}
status = order["status"]
if status == "delivered":
return {
"eligible": True,
"max_refund": order["total"] * 0.8,
"reason": "Within 30-day return window",
"requires_approval": order["total"] > 5000
}
elif status == "shipped":
return {
"eligible": True,
"max_refund": order["total"] * 0.9,
"reason": "Not yet delivered, full refund possible minus shipping",
"requires_approval": False
}
else:
return {
"eligible": True,
"max_refund": order["total"],
"reason": "Order still processing, full cancellation available",
"requires_approval": False
}
def escalate_to_human(summary: str, priority: str = "normal") -> dict:
"""Creates a formatted escalation ticket for human review."""
return {
"ticket_id": f"ESC-{int(time.time()) % 100000}",
"summary": summary,
"priority": priority,
"routed_to": "Tier-2 Support",
"estimated_response": "2 hours",
"status": "queued"
}
Step 2: Build the Agent Core with Reasoning Trace
The agent loop is where the demo magic happens. We'll capture every decision step in a reasoning_trace list so the UI can render it live. This is what prospects actually see—and what builds trust.
# agent/core.py
import json
import re
from datetime import datetime
from typing import List, Dict, Any
from dataclasses import dataclass, field
@dataclass
class ReasoningStep:
step_type: str # 'thought', 'tool_call', 'tool_result', 'decision'
content: str
timestamp: str
metadata: Dict[str, Any] = field(default_factory=dict)
class SupportAgent:
"""
A transparent AI agent for customer support triage.
Every decision is logged to reasoning_trace for the demo UI.
"""
def __init__(self, llm_client=None):
# In production, inject your LLM client (OpenAI, Anthropic, etc.)
self.llm = llm_client
self.reasoning_trace: List[ReasoningStep] = []
self.tools = {
"lookup_order": lookup_order,
"check_refund_eligibility": check_refund_eligibility,
"escalate_to_human": escalate_to_human
}
def _add_trace(self, step_type: str, content: str, metadata: dict = None):
step = ReasoningStep(
step_type=step_type,
content=content,
timestamp=datetime.now().isoformat(),
metadata=metadata or {}
)
self.reasoning_trace.append(step)
return step
def _extract_entities(self, email_body: str) -> Dict[str, Any]:
"""
Rule-based entity extraction as a fallback / demo scaffold.
In production, this would be an LLM call with structured output.
For the demo, we combine regex + keyword matching to show the concept.
"""
entities = {
"order_ids": [],
"intent": "unknown",
"sentiment": "neutral",
"key_phrases": [],
"requested_action": None
}
# Extract order IDs (various formats)
order_patterns = [
r'ORD-\d{4,}', # ORD-7821
r'Order\s*#?\s*(\d{4,})', # Order #7821
r'order\s*ID\s*[:]*\s*(\w+-\d+)', # order ID: ORD-7821
]
for pattern in order_patterns:
matches = re.findall(pattern, email_body, re.IGNORECASE)
for match in matches:
cleaned = match if 'ORD' in str(match).upper() else f"ORD-{match}"
if cleaned not in entities["order_ids"]:
entities["order_ids"].append(cleaned)
# Intent classification via keyword heuristics
body_lower = email_body.lower()
intent_keywords = {
"refund_request": ["refund", "money back", "return", "cancel order", "chargeback"],
"shipping_inquiry": ["where is my", "tracking", "delivery", "shipping", "eta", "arrive"],
"product_issue": ["defective", "broken", "not working", "bug", "error", "doesn't work"],
"billing_question": ["invoice", "billing", "charged", "credit card", "payment", "overcharged"],
"account_access": ["login", "password", "access", "locked out", "can't sign in"],
"general_inquiry": ["question", "how do i", "what is", "pricing", "features"]
}
scored_intents = {}
for intent, keywords in intent_keywords.items():
score = sum(1 for kw in keywords if kw in body_lower)
if score > 0:
scored_intents[intent] = score
if scored_intents:
entities["intent"] = max(scored_intents, key=scored_intents.get)
else:
entities["intent"] = "general_inquiry"
# Sentiment: simple heuristic
negative_words = ["angry", "frustrated", "terrible", "worst", "never", "refund", "complaint", "urgent"]
positive_words = ["thank", "great", "love", "appreciate", "helpful", "resolved"]
neg_count = sum(1 for w in negative_words if w in body_lower)
pos_count = sum(1 for w in positive_words if w in body_lower)
if neg_count > pos_count:
entities["sentiment"] = "negative"
elif pos_count > neg_count:
entities["sentiment"] = "positive"
return entities
def _plan_actions(self, entities: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Determines which tools to call based on extracted entities."""
plan = []
# Always look up referenced orders
for order_id in entities["order_ids"]:
plan.append({
"tool": "lookup_order",
"params": {"order_id": order_id},
"rationale": f"Customer referenced order {order_id}, fetching details"
})
# Add intent-specific actions
if entities["intent"] == "refund_request" and entities["order_ids"]:
plan.append({
"tool": "check_refund_eligibility",
"params": {
"order_id": entities["order_ids"][0],
"reason": "Customer requested refund"
},
"rationale": "Refund intent detected, checking policy eligibility"
})
if entities["sentiment"] == "negative":
plan.append({
"tool": "escalate_to_human",
"params": {
"summary": "Negative sentiment detected, customer may need priority handling",
"priority": "high"
},
"rationale": "Sentiment analysis indicates frustration, flagging for human review"
})
return plan
def process_ticket(self, email_body: str, ticket_id: str = None) -> Dict[str, Any]:
"""
Main entry point: processes a support email end-to-end,
producing a resolution or escalation with full reasoning trace.
"""
self.reasoning_trace = []
# --- Step 1: Parse and understand ---
self._add_trace("thought", "Reading incoming email to extract key entities and intent...")
entities = self._extract_entities(email_body)
self._add_trace("thought", f"Extracted entities: intent={entities['intent']}, "
f"orders={entities['order_ids']}, sentiment={entities['sentiment']}")
# --- Step 2: Plan actions ---
action_plan = self._plan_actions(entities)
self._add_trace("thought", f"Generated action plan with {len(action_plan)} tool calls")
# --- Step 3: Execute tools ---
tool_results = {}
for action in action_plan:
self._add_trace("tool_call", f"Calling {action['tool']} with params {action['params']}",
{"tool": action['tool'], "params": action['params']})
tool_fn = self.tools.get(action['tool'])
if tool_fn:
result = tool_fn(**action['params'])
tool_results[action['tool']] = result
self._add_trace("tool_result", f"Result from {action['tool']}: {json.dumps(result, default=str)[:200]}",
{"tool": action['tool'], "result": result})
# --- Step 4: Synthesize resolution ---
self._add_trace("thought", "Synthesizing results into a resolution...")
resolution = self._synthesize_resolution(email_body, entities, tool_results, ticket_id)
self._add_trace("decision", resolution["summary"],
{"resolution_type": resolution["type"], "confidence": resolution.get("confidence", 0.9)})
return {
"ticket_id": ticket_id or f"AUTO-{int(time.time()) % 100000}",
"original_email": email_body,
"entities": entities,
"tool_results": tool_results,
"resolution": resolution,
"reasoning_trace": [
{"step_type": s.step_type, "content": s.content, "timestamp": s.timestamp, "metadata": s.metadata}
for s in self.reasoning_trace
]
}
def _synthesize_resolution(self, email_body: str, entities: dict, tool_results: dict, ticket_id: str = None) -> dict:
"""Combines tool outputs into a coherent resolution."""
order_results = tool_results.get("lookup_order", {})
refund_results = tool_results.get("check_refund_eligibility", {})
escalation_results = tool_results.get("escalate_to_human", {})
# Determine if escalation is needed
needs_escalation = False
escalation_reasons = []
if isinstance(refund_results, dict) and refund_results.get("requires_approval"):
needs_escalation = True
escalation_reasons.append("Refund amount exceeds automatic approval threshold")
if entities.get("sentiment") == "negative":
needs_escalation = True
escalation_reasons.append("Customer sentiment indicates potential churn risk")
if not entities.get("order_ids"):
needs_escalation = True
escalation_reasons.append("No order information found in email, manual review needed")
if needs_escalation:
summary = (
f"Auto-triage complete for {ticket_id or 'this ticket'}. "
f"Detected intent: {entities['intent']}. "
f"Escalation reasons: {'; '.join(escalation_reasons)}. "
f"Pre-populated findings: {json.dumps(order_results.get('data', {}), default=str)[:300]}. "
f"Ticket routed to Tier-2 with priority {'high' if entities.get('sentiment') == 'negative' else 'normal'}."
)
return {"type": "escalated", "summary": summary, "escalation_detail": escalation_results}
# Auto-resolution path
if entities["intent"] == "refund_request" and refund_results.get("eligible"):
summary = (
f"Auto-resolution: Refund eligible for order {entities['order_ids'][0]}. "
f"Maximum refund: ${refund_results.get('max_refund', 0):.2f}. "
f"Reason: {refund_results.get('reason', 'standard policy')}. "
f"Customer will receive automated refund confirmation within 1 hour."
)
return {"type": "auto_resolved", "summary": summary, "confidence": 0.92}
if entities["intent"] == "shipping_inquiry" and order_results.get("success"):
data = order_results.get("data", {})
tracking = data.get("tracking_number", "pending")
status = data.get("status", "unknown")
summary = (
f"Order {data.get('order_id')} status: {status}. "
f"Tracking: {tracking}. "
f"Estimated delivery: {data.get('delivery_date', 'To be confirmed')}. "
f"Customer will receive status update via email."
)
return {"type": "auto_resolved", "summary": summary, "confidence": 0.95}
# Fallback
summary = f"Processed {entities['intent']} inquiry. All relevant data gathered. Ready for human review."
return {"type": "partial_resolution", "summary": summary, "confidence": 0.7}
Step 3: Build the Demo UI That Shows the Reasoning
The UI is the emotional engine of the demo. We'll use Streamlit for rapid prototyping—it gives a clean, reactive interface with minimal code. The critical feature: a live reasoning panel that unfolds as the agent thinks.
# ui/app.py
import streamlit as st
import sys
import time
import json
from pathlib import Path
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from agent.core import SupportAgent
from agent.tools import lookup_order, check_refund_eligibility, escalate_to_human
st.set_page_config(
page_title="Support Agent Demo",
page_icon="🤖",
layout="wide"
)
# ---- Session State Initialization ----
if "agent" not in st.session_state:
st.session_state.agent = SupportAgent()
if "processed_count" not in st.session_state:
st.session_state.processed_count = 0
if "total_time_saved_minutes" not in st.session_state:
st.session_state.total_time_saved_minutes = 0
agent = st.session_state.agent
# ---- Header with Metrics ----
st.title("🤖 Customer Support Triage Agent")
st.caption("Live Demo · AI Agent with Transparent Reasoning")
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Tickets Processed", st.session_state.processed_count)
with col2:
st.metric("Avg. Handling Time", "12 sec", delta="-95% vs human")
with col3:
st.metric("Auto-Resolution Rate", "73%", delta="+8%")
with col4:
st.metric("Time Saved (est.)", f"{st.session_state.total_time_saved_minutes} min", delta="↑")
st.divider()
# ---- Two-Column Layout: Input | Reasoning ----
left_col, right_col = st.columns([1, 1.2])
with left_col:
st.subheader("📧 Incoming Support Email")
# Example scenarios as quick-select buttons
st.caption("Select a scenario or write your own:")
scenario_col1, scenario_col2 = st.columns(2)
with scenario_col1:
if st.button("🔴 Refund Request (High Value)", use_container_width=True):
st.session_state.scenario = "refund"
if st.button("📦 Shipping Inquiry", use_container_width=True):
st.session_state.scenario = "shipping"
with scenario_col2:
if st.button("😡 Frustrated Customer", use_container_width=True):
st.session_state.scenario = "frustrated"
if st.button("❓ General Question", use_container_width=True):
st.session_state.scenario = "general"
# Default scenario content
scenarios = {
"refund": """Subject: URGENT - Need refund for bulk order ORD-7821
Hi Support,
We received our enterprise license order (ORD-7821) last week but our procurement team
has decided to switch vendors. We need a full refund processed immediately.
This is a $9,950 order and our finance team is waiting on the credit.
Please expedite this.
Regards,
Sarah Chen
Acme Corp""",
"shipping": """Subject: Where is my order ORD-7822?
Hello,
I placed order ORD-7822 three days ago for a support add-on and haven't received
any tracking information. Can you tell me where it is and when it will arrive?
We're going live next week and need this urgently.
Thanks,
Mike""",
"frustrated": """Subject: THIRD TIME reaching out - ORD-7823 issues
This is absolutely ridiculous. I've contacted support THREE TIMES about order ORD-7823
and nobody has resolved anything. My cloud storage tier 3 upgrade has been "processing"
for two weeks. I'm about to cancel everything and move to a competitor.
Fix this NOW or I'm done.
- James Wilson
Gamma Inc""",
"general": """Subject: Question about enterprise pricing
Hi,
I'm evaluating your platform for our team of 25 developers. Could you explain
the difference between your standard and enterprise tiers? Also, do you offer
volume discounts for orders above 50 seats?
Best,
Lisa Park"""
}
current_scenario = st.session_state.get("scenario", "refund")
default_text = scenarios.get(current_scenario, scenarios["refund"])
email_input = st.text_area(
"Email body (editable)",
value=default_text,
height=220,
key="email_input"
)
process_btn = st.button("⚡ Process with AI Agent", type="primary", use_container_width=True)
with right_col:
st.subheader("🧠 Agent Reasoning Trace (Live)")
reasoning_placeholder = st.empty()
# ---- Processing Logic ----
if process_btn:
with st.spinner("Agent analyzing..."):
result = agent.process_ticket(
email_body=email_input,
ticket_id=f"DEMO-{st.session_state.processed_count + 1:04d}"
)
# Update metrics
st.session_state.processed_count += 1
st.session_state.total_time_saved_minutes += 15 # Estimate: 15 min saved per ticket
# Render reasoning trace in the right panel
with reasoning_placeholder.container():
trace = result.get("reasoning_trace", [])
for i, step in enumerate(trace):
step_type = step.get("step_type", "thought")
content = step.get("content", "")
# Visual styling per step type
if step_type == "thought":
st.markdown(f"""
💭 Thought #{i+1}
{content}
""", unsafe_allow_html=True)
elif step_type == "tool_call":
st.markdown(f"""
🔧 Tool Call #{i+1}
{content}
Params: {json.dumps(step.get('metadata', {}).get('params', {}))}
""", unsafe_allow_html=True)
elif step_type == "tool_result":
result_text = str(step.get("metadata", {}).get("result", ""))[:300]
st.markdown(f"""
✅ Result #{i+1}
{result_text}
""", unsafe_allow_html=True)
elif step_type == "decision":
st.markdown(f"""
🎯 FINAL DECISION
{content}
""", unsafe_allow_html=True)
# Show resolution summary at bottom
st.divider()
resolution = result.get("resolution", {})
res_type = resolution.get("type", "unknown")
if res_type == "auto_resolved":
st.success(f"✅ **Auto-Resolved** — {resolution.get('summary', '')}")
st.balloons()
elif res_type == "escalated":
st.warning(f"⚠️ **Escalated to Human** — {resolution.get('summary', '')}")
else:
st.info(f"📋 **Partial Resolution** — {resolution.get('summary', '')}")
# Expandable raw data for technical stakeholders
with st.expander("🔍 Full Agent Output (for technical review)"):
st.json(result)
Step 4: The Demo Entry Point and Sample Data
# run_demo.py
"""
Entry point for the AI Agent Demo.
Run with: streamlit run ui/app.py
Or directly: python run_demo.py (if using a different UI framework)
"""
import sys
from pathlib import Path
# Ensure package is importable
sys.path.insert(0, str(Path(__file__).parent))
def run_cli_demo():
"""Simple CLI demo for testing without the UI."""
from agent.core import SupportAgent
agent = SupportAgent()
sample_email = """Subject: URGENT - Need refund for bulk order ORD-7821
Hi Support,
We received our enterprise license order (ORD-7821) last week but need a full refund.
This is a $9,950 order. Please expedite.
Regards, Sarah Chen, Acme Corp"""
print("=" * 60)
print("🤖 AI Support Agent - CLI Demo")
print("=" * 60)
print(f"\n📧 Processing email:\n{sample_email}\n")
print("-" * 60)
result = agent.process_ticket(sample_email, ticket_id="DEMO-0001")
print("\n🧠 Reasoning Trace:")
for step in result["reasoning_trace"]:
icon = {"thought": "💭", "tool_call": "🔧", "tool_result": "✅", "decision": "🎯"}
print(f" {icon.get(step['step_type'], '•')} [{step['step_type']}] {step['content'][:120]}")
print(f"\n📋 Resolution: {result['resolution']['type']}")
print(f" {result['resolution']['summary']}")
print("\n" + "=" * 60)
if __name__ == "__main__":
run_cli_demo()
Step 5: Sample Tickets JSON for Batch Demos
// examples/sample_tickets.json
[
{
"id": "TKT-001",
"scenario": "High-value refund request",
"email": "Subject: Refund ORD-7821\n\nHi, we need a full refund for our enterprise license order ORD-7821. Total was $9,950. Please process immediately.\n- Sarah",
"expected_resolution": "escalated",
"expected_escalation_reason": "Refund amount exceeds automatic approval threshold"
},
{
"id": "TKT-002",
"scenario": "Shipping inquiry with tracking",
"email": "Subject: Where's my order?\n\nCan you provide tracking for ORD-7822? I need it for our go-live next week.\n- Mike",
"expected_resolution": "auto_resolved",
"expected_summary_contains": "tracking"
},
{
"id": "TKT-003",
"scenario": "Frustrated customer - churn risk",
"email": "Subject: THIRD TIME - ORD-7823\n\nI've contacted you three times about order ORD-7823. Still processing after two weeks. I'm about to cancel everything and move to a competitor. Fix this NOW.\n- James Wilson, Gamma Inc",
"expected_resolution": "escalated",
"expected_escalation_reason": "Customer sentiment indicates potential churn risk"
},
{
"id": "TKT-004",
"scenario": "General pricing inquiry",
"email": "