Customer Support AI Agent
Customer Support Agent Architecture
What is a Customer Support Agent?
Customer support agents automate ticket handling by classifying issues, retrieving relevant knowledge, analyzing sentiment, and generating personalized responses. They help support teams handle more tickets while maintaining quality.
Why this matters: A support team handling 1,000 tickets/day can reduce response time by 60% with AI assistance. The agent handles routine questions, freeing humans for complex issues.
Common Misconception
"AI will replace human support agents."
AI agents handle routine queries efficiently but cannot replace human empathy for complex issues. They work best as assistants that draft responses and surface relevant information, with humans making final decisions.
Real-World Analogy
Think of it as a knowledgeable colleague who can instantly find answers in the knowledge base, draft professional responses, and flag urgent issues â but who always checks with you before sending sensitive replies.
Project Overview
We will build a customer support agent that:
- Classifies tickets by category and priority
- Retrieves relevant answers from a knowledge base using RAG
- Analyzes customer sentiment and adjusts tone
- Routes complex issues to appropriate teams
- Generates personalized response drafts
- Tracks ticket metrics and SLA compliance
Expected outcome: An agent that handles 80%+ of routine support queries automatically.
Tools and Setup
| Tool | Version | Purpose |
|---|---|---|
| Python | 3.11+ | Core language |
| chromadb | 0.4+ | Vector database for RAG |
| openai | 1.0+ | LLM backbone |
| pydantic | 2.0+ | Data models |
| tiktoken | 0.5+ | Token counting |
Step 1: Environment Setup
python -m venv venv
source venv/bin/activate
pip install chromadb openai pydantic tiktoken
Step 2: RAG Knowledge Base
# knowledge/base.py
import chromadb
from typing import List, Dict
from openai import OpenAI
class KnowledgeBase:
def __init__(self, collection_name: str = "support_docs"):
self.client = chromadb.Client()
self.collection = self.client.create_collection(
name=collection_name,
metadata={"hnsw:space": "cosine"}
)
self.embedder = OpenAI()
def add_documents(self, documents: List[Dict]):
for doc in documents:
embedding = self._get_embedding(doc["content"])
self.collection.add(
documents=[doc["content"]],
embeddings=[embedding],
metadatas=[{"title": doc["title"], "category": doc.get("category", "general")}],
ids=[doc["id"]],
)
def search(self, query: str, n_results: int = 3) -> List[Dict]:
query_embedding = self._get_embedding(query)
results = self.collection.query(
query_embeddings=[query_embedding],
n_results=n_results,
)
return [
{"content": doc, "title": meta["title"], "category": meta["category"]}
for doc, meta in zip(results["documents"][0], results["metadatas"][0])
]
def _get_embedding(self, text: str) -> List[float]:
response = self.embedder.embeddings.create(
model="text-embedding-3-small", input=text
)
return response.data[0].embedding
Step 3: Ticket Classifier and Sentiment Analyzer
# analysis/classifier.py
import json
from typing import Dict
from openai import OpenAI
class TicketClassifier:
def __init__(self, model: str = "gpt-4o"):
self.client = OpenAI()
self.model = model
self.categories = [
"billing", "technical_issue", "account_access",
"feature_request", "bug_report", "general_inquiry",
"refund_request", "cancelation", "complaint",
]
def classify(self, ticket: Dict) -> Dict:
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": f"""Classify this support ticket. Return JSON:
{{"category": "one of: {', '.join(self.categories)}",
"priority": "low|medium|high|urgent",
"sentiment": "positive|neutral|negative|angry",
"complexity": "simple|moderate|complex",
"requires_human": true/false,
"key_topics": ["topic1", "topic2"]}}"""},
{"role": "user", "content": f"Subject: {ticket.get('subject', '')}\nBody: {ticket.get('body', '')}"},
],
temperature=0.0,
)
try:
return json.loads(response.choices[0].message.content)
except (json.JSONDecodeError, IndexError):
return {"category": "general_inquiry", "priority": "medium", "requires_human": True}
# analysis/sentiment.py
import json
from typing import Dict
from openai import OpenAI
class SentimentAnalyzer:
def __init__(self, model: str = "gpt-4o"):
self.client = OpenAI()
self.model = model
def analyze(self, text: str) -> Dict:
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": """Analyze customer sentiment. Return JSON:
{{"sentiment_score": -1.0 to 1.0,
"emotions": ["frustrated", "confused", "angry", "satisfied"],
"escalation_risk": "low|medium|high",
"recommended_tone": "empathetic|professional|friendly|urgent"}}"""},
{"role": "user", "content": text},
],
temperature=0.0,
)
try:
return json.loads(response.choices[0].message.content)
except (json.JSONDecodeError, IndexError):
return {"sentiment_score": 0.0, "escalation_risk": "medium", "recommended_tone": "professional"}
def adjust_response_tone(self, response: str, customer_sentiment: Dict) -> str:
tone = customer_sentiment.get("recommended_tone", "professional")
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": f"Rewrite this response with a {tone} tone while keeping the same information."},
{"role": "user", "content": response},
],
temperature=0.3,
)
return response.choices[0].message.content
Step 4: Escalation Engine and Response Generator
# routing/escalation.py
from typing import Dict, List
import logging
logger = logging.getLogger(__name__)
class EscalationEngine:
def __init__(self):
self.escalation_rules = {
"urgent": {"max_wait_minutes": 15, "route_to": "senior_support"},
"high": {"max_wait_minutes": 60, "route_to": "technical_team"},
"medium": {"max_wait_minutes": 240, "route_to": "general_support"},
"low": {"max_wait_minutes": 1440, "route_to": "queue"},
}
self.escalation_keywords = [
"legal", "lawsuit", "attorney", "regulatory", "data breach",
"security incident", "financial loss", "service outage",
]
def should_escalate(self, ticket: Dict, classification: Dict) -> bool:
if classification.get("requires_human", False):
return True
if classification.get("priority") in ["urgent", "high"]:
return True
body_lower = ticket.get("body", "").lower()
if any(kw in body_lower for kw in self.escalation_keywords):
return True
sentiment = classification.get("sentiment", "")
if sentiment in ["angry"] and classification.get("complexity") == "complex":
return True
return False
def get_escalation_target(self, classification: Dict) -> str:
priority = classification.get("priority", "medium")
return self.escalation_rules.get(priority, self.escalation_rules["medium"])["route_to"]
def get_wait_time(self, classification: Dict) -> int:
priority = classification.get("priority", "medium")
return self.escalation_rules.get(priority, self.escalation_rules["medium"])["max_wait_minutes"]
# response/generator.py
from typing import Dict, List
from openai import OpenAI
class ResponseGenerator:
def __init__(self, model: str = "gpt-4o"):
self.client = OpenAI()
self.model = model
def generate_response(self, ticket: Dict, knowledge_results: List[Dict], sentiment: Dict) -> str:
context = "\n\n".join([f"Source: {r['title']}\n{r['content']}" for r in knowledge_results])
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": f"""You are a customer support agent. Generate a helpful response.
Tone: {sentiment.get('recommended_tone', 'professional')}
Customer sentiment: {sentiment.get('sentiment_score', 0)}
Use this knowledge:
{context}
Rules:
- Be specific and actionable
- Reference relevant knowledge base articles
- If unsure, offer to connect with a human agent
- Never make promises you cannot keep"""},
{"role": "user", "content": f"Customer message:\nSubject: {ticket.get('subject', '')}\nBody: {ticket.get('body', '')}"},
],
temperature=0.4,
)
return response.choices[0].message.content
def generate_followup(self, ticket: Dict, response: str) -> str:
followup = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "Generate a follow-up question to ensure the customer's issue is resolved."},
{"role": "user", "content": f"Ticket: {ticket.get('subject', '')}\nResponse sent: {response[:500]}"},
],
temperature=0.3,
)
return followup.choices[0].message.content
Step 5: Complete Agent
# agent.py
from knowledge.base import KnowledgeBase
from analysis.classifier import TicketClassifier
from analysis.sentiment import SentimentAnalyzer
from routing.escalation import EscalationEngine
from response.generator import ResponseGenerator
from typing import Dict, List
class CustomerSupportAgent:
def __init__(self, model: str = "gpt-4o"):
self.knowledge_base = KnowledgeBase()
self.classifier = TicketClassifier(model)
self.sentiment_analyzer = SentimentAnalyzer(model)
self.escalation_engine = EscalationEngine()
self.response_generator = ResponseGenerator(model)
def handle_ticket(self, ticket: Dict) -> Dict:
classification = self.classifier.classify(ticket)
sentiment = self.sentiment_analyzer.analyze(ticket.get("body", ""))
if self.escalation_engine.should_escalate(ticket, classification):
return {
"ticket_id": ticket.get("id"),
"status": "escalated",
"route_to": self.escalation_engine.get_escalation_target(classification),
"wait_time_minutes": self.escalation_engine.get_wait_time(classification),
"classification": classification,
"sentiment": sentiment,
}
knowledge_results = self.knowledge_base.search(
f"{ticket.get('subject', '')} {ticket.get('body', '')}"
)
draft_response = self.response_generator.generate_response(
ticket, knowledge_results, sentiment
)
final_response = self.sentiment_analyzer.adjust_response_tone(
draft_response, sentiment
)
followup = self.response_generator.generate_followup(ticket, final_response)
return {
"ticket_id": ticket.get("id"),
"status": "resolved",
"classification": classification,
"sentiment": sentiment,
"response": final_response,
"followup_question": followup,
"knowledge_sources": [r["title"] for r in knowledge_results],
}
def bulk_process(self, tickets: List[Dict]) -> Dict:
results = [self.handle_ticket(t) for t in tickets]
return {
"total": len(results),
"resolved": sum(1 for r in results if r["status"] == "resolved"),
"escalated": sum(1 for r in results if r["status"] == "escalated"),
"avg_sentiment": sum(r["sentiment"].get("sentiment_score", 0) for r in results) / len(results) if results else 0,
"results": results,
}
Mathematical Foundation
Sentiment Score: Range -1.0 (very negative) to 1.0 (very positive).
Resolution Rate: resolved / total tickets à 100
Escalation Rate: escalated / total tickets à 100
SLA Compliance: tickets resolved within SLA / total tickets à 100
Cost Savings: (human_cost_per_ticket - ai_cost_per_ticket) Ã tickets_handled
Performance Considerations
| Metric | Value | Notes |
|---|---|---|
| Resolution Rate | 85% | Routine queries |
| Response Time | < 2s | Including RAG search |
| CSAT Score | 4.2/5 | Customer satisfaction |
| Cost per Ticket | 5-15 human cost |
Security Notes
- Never log customer PII in tickets
- Use encryption for ticket storage
- Implement role-based access control
- Regularly audit knowledge base for sensitive data
- Ensure API keys are stored securely
- Monitor for prompt injection attempts
Interview Questions
1. How do you handle ticket prioritization?
Multi-signal approach: urgency keywords (outage, breach, legal), customer sentiment score, customer tier (enterprise vs free), and SLA deadlines. Combine with LLM classification for nuanced priority detection.
2. How does RAG improve response quality?
RAG grounds responses in actual documentation. Vector search finds relevant articles, providing context that reduces hallucination. Always cite sources so agents can verify accuracy.
3. How do you handle angry customers?
Detect anger through sentiment analysis. Use empathetic tone. Acknowledge frustration before providing solutions. Offer escalation to human agents for severe cases. Never match customer anger.
4. How do you measure support quality?
Track CSAT scores, first-contact resolution rate, average handle time, escalation rate, and customer effort score. Monitor accuracy of AI-generated responses through human review.
5. How do you handle knowledge base maintenance?
Schedule regular reviews of outdated articles. Track which articles are accessed and which lead to successful resolutions. Use analytics to identify gaps. Implement version control for documentation.
6. How do you prevent AI from giving wrong answers?
Confidence scoring â if confidence is low, escalate to humans. Always cite sources. Never make promises about timelines or outcomes. Implement human review for critical categories.
7. How would you handle multi-channel support?
Abstract channels behind a unified interface. Different channels may need different response formats (chat vs email). Maintain conversation context across channels. Track channel-specific metrics.
8. What are limitations of AI in customer support?
Cannot handle truly novel issues, may miss emotional nuances, struggles with complex technical debugging, cannot make judgment calls about policy exceptions, and requires human oversight.
Common Pitfalls and Solutions
| Pitfall | Solution |
|---|---|
| Over-automation | Set clear escalation criteria for human handoff |
| Stale knowledge | Schedule regular content reviews and updates |
| Tone mismatch | Adjust response tone based on sentiment analysis |
| Wrong answers | Implement confidence scoring and human review |
| Privacy breaches | Encrypt PII and limit access controls |
| Prompt injection | Validate inputs and use structured outputs |
| SLA violations | Monitor response times and escalate proactively |
Summary with Key Takeaways
- Ticket classification enables automatic routing and prioritization
- RAG knowledge base provides grounded, accurate responses
- Sentiment analysis adjusts tone for customer satisfaction
- Escalation logic ensures complex issues reach humans quickly
- Response generation maintains consistency across channels
- Always track metrics and customer satisfaction
- Human oversight remains essential for quality assurance
KnowledgeCheck
-
What is the primary purpose of ticket classification?
- a) Generate responses
- b) Route tickets to appropriate teams and set priorities
- c) Store ticket history
- d) Count total tickets
-
What does RAG stand for?
- a) Random Answer Generation
- b) Retrieval-Augmented Generation
- c) Real-time Answer Graph
- d) Recursive Answer Gateway
-
Why is sentiment analysis important?
- a) It counts words
- b) It helps adjust response tone for customer satisfaction
- c) It generates new tickets
- d) It stores customer data
-
When should a ticket be escalated to a human?
- a) Always
- b) Only when the customer asks
- c) For complex, urgent, or high-risk issues
- d) Never
-
What is the benefit of using a knowledge base?
- a) Makes responses longer
- b) Provides grounded, accurate information reducing hallucination
- c) Replaces all documentation
- d) Increases response time
-
Why should AI never make promises about timelines?
- a) It doesn't understand time
- b) It cannot guarantee outcomes and may create legal liability
- c) Timelines are not important
- d) Customers prefer vague answers
Answers: 1-b, 2-b, 3-b, 4-c, 5-b, 6-b