Spaces:
Runtime error
Runtime error
File size: 6,172 Bytes
8cadb90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """
agent.py β Groq API version
Uses Groq's free API with llama-3.3-70b-versatile model.
Groq is extremely fast (200+ tokens/sec) and free to use.
Setup:
1. Go to console.groq.com β sign up β API Keys β Create Key
2. Set: export GROQ_API_KEY=gsk_your_key_here (Mac/Linux)
set GROQ_API_KEY=gsk_your_key_here (Windows)
3. pip install groq
"""
import os
import json
import re
from groq import Groq
client = Groq(api_key=os.environ.get("GROQ_API_KEY", ""))
MODEL = "llama-3.3-70b-versatile" # Free, fast, high quality
def _call(system: str, user: str, max_tokens: int = 800) -> str:
"""Call Groq API and return text response."""
response = client.chat.completions.create(
model=MODEL,
max_tokens=max_tokens,
temperature=0.2,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user},
],
)
return response.choices[0].message.content.strip()
# ββ CLASSIFICATION ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
CLASSIFY_SYSTEM = """You are a support ticket classifier for a multi-domain helpdesk.
Classify the ticket into exactly these fields and respond ONLY with valid JSON.
Fields:
- domain: one of ["hackerrank", "claude", "visa", "unknown"]
* hackerrank β coding assessments, candidates, recruiters, HackerRank platform, proctoring, plagiarism
* claude β Claude AI assistant, claude.ai, Anthropic API, AI conversations, Claude subscription
* visa β Visa card, ATM, transactions, payments, card dispute, merchant
* unknown β unclear or does not match any domain
- request_type: one of ["faq", "billing", "bug_report", "account_access",
"assessment", "feature_request", "fraud", "complaint",
"how_to", "permissions", "other"]
- product_area: short phrase (e.g. "password reset", "card dispute", "API usage", "candidate results")
- confidence: "high" | "medium" | "low"
Respond ONLY with a JSON object. No markdown fences. No explanation. No extra text."""
def classify_ticket(ticket_text: str) -> dict:
"""Classify a support ticket. Returns dict."""
raw = _call(CLASSIFY_SYSTEM, f"Classify this ticket:\n\n{ticket_text}", max_tokens=200)
try:
clean = raw.strip().strip("```json").strip("```").strip()
return json.loads(clean)
except json.JSONDecodeError:
# Try to extract JSON object with regex
match = re.search(r'\{.*?\}', clean, re.DOTALL)
if match:
try:
return json.loads(match.group(0))
except Exception:
pass
return _rule_based_classify(ticket_text)
def _rule_based_classify(text: str) -> dict:
"""Fast keyword fallback if LLM JSON parse fails."""
t = text.lower()
if any(w in t for w in ["hackerrank", "assessment", "candidate", "recruiter", "plagiar", "proctoring"]):
domain = "hackerrank"
elif any(w in t for w in ["claude", "anthropic", "claude.ai", "ai assistant"]):
domain = "claude"
elif any(w in t for w in ["visa", "card", "atm", "transaction", "merchant", "chargeback"]):
domain = "visa"
else:
domain = "unknown"
if any(w in t for w in ["fraud", "unauthorized", "stolen", "scam"]):
rtype = "fraud"
elif any(w in t for w in ["charge", "refund", "billing", "invoice"]):
rtype = "billing"
elif any(w in t for w in ["password", "login", "locked", "hacked", "access"]):
rtype = "account_access"
elif any(w in t for w in ["bug", "error", "broken", "crash", "not working"]):
rtype = "bug_report"
else:
rtype = "faq"
return {"domain": domain, "request_type": rtype,
"product_area": f"{domain} support", "confidence": "medium"}
# ββ RESPONSE GENERATION βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
RESPONSE_SYSTEM = """You are a professional, friendly support agent.
STRICT RULE: Answer ONLY using the provided support documentation.
Do NOT invent any policies, prices, steps, or features not in the docs.
If docs don't cover something, say so honestly and suggest contacting support directly.
Keep responses concise: 3-5 sentences for simple FAQs, up to 8 for complex issues."""
def generate_response(ticket_text: str, docs: list[dict]) -> str:
"""Generate a grounded support response using retrieved docs."""
if not docs:
return (
"Thank you for reaching out. I wasn't able to find relevant documentation "
"for your specific query in our support corpus. Please contact our support "
"team directly for personalized assistance."
)
context = "\n\n---\n\n".join(
f"[Source {i}: {d['title']}]\n{d.get('snippet', d['text'][:800])}"
for i, d in enumerate(docs[:4], 1)
)
user_prompt = f"""Support Documentation:
{context}
---
Customer Issue:
{ticket_text}
Write a helpful, accurate response using only the documentation above."""
return _call(RESPONSE_SYSTEM, user_prompt, max_tokens=500)
# ββ ESCALATION MESSAGE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ESCALATE_SYSTEM = """You are a support agent writing an escalation acknowledgment.
Write 2-3 sentences: acknowledge the customer's concern, say a specialist will review it,
and set a reasonable expectation. Be warm and professional.
Do NOT attempt to resolve the issue. Do NOT reveal internal routing decisions."""
def generate_escalation_message(ticket_text: str, escalation_reason: str) -> str:
"""Generate a customer-facing escalation message."""
return _call(
ESCALATE_SYSTEM,
f"Customer issue: {ticket_text}\n\n(Internal note - do not reveal: {escalation_reason})",
max_tokens=180,
)
|