cloudsync-fte / src /agent /prototype.py
Asma-yaseen's picture
feat: complete CloudSync Pro AI Customer Support FTE
07c1369
Raw
History Blame Contribute Delete
4.75 kB
"""
Incubation prototype — src/agent/prototype.py
Stage 1: Claude Code-driven exploration. NOT production code.
"""
from __future__ import annotations
import json
import os
from datetime import datetime
from pathlib import Path
CHANNEL_STYLES = {
"email": {"max_len": 2000, "greeting": True, "signature": True},
"whatsapp": {"max_len": 300, "greeting": False, "signature": False},
"web_form": {"max_len": 1000, "greeting": False, "signature": False},
}
ESCALATION_KEYWORDS = [
"pricing", "price", "cost", "refund", "lawyer", "sue", "legal",
"attorney", "lawsuit", "cancel subscription", "money back",
]
HUMAN_REQUEST_KEYWORDS = [
"human", "agent", "real person", "speak to someone", "representative",
"put me through", "connect me", "talk to someone", "speak with someone",
]
# In-memory state — replaced by PostgreSQL in production
_conversations: dict[str, dict] = {}
def _load_product_docs() -> str:
docs_path = Path(__file__).parent.parent.parent / "context" / "product-docs.md"
if docs_path.exists():
return docs_path.read_text()
return ""
PRODUCT_DOCS = _load_product_docs()
def search_docs(query: str) -> str:
"""Simple keyword-based search (replaced by pgvector in production)."""
query_lower = query.lower()
results = []
for section in PRODUCT_DOCS.split("##"):
if any(word in section.lower() for word in query_lower.split()):
results.append(section.strip()[:500])
if len(results) >= 3:
break
return "\n\n---\n\n".join(results) if results else ""
def detect_escalation(message: str) -> tuple[bool, str]:
"""Return (should_escalate, reason)."""
msg_lower = message.lower()
for kw in ESCALATION_KEYWORDS:
if kw in msg_lower:
if any(k in kw for k in ("pric", "cost", "cancel")):
return True, "pricing_inquiry"
if "refund" in kw or "money back" in kw:
return True, "refund_request"
return True, "legal_language"
for kw in HUMAN_REQUEST_KEYWORDS:
if kw in msg_lower:
return True, "explicit_human_request"
return False, ""
def detect_sentiment(message: str) -> float:
"""Naive sentiment — replaced by LLM in production."""
negative_words = ["ridiculous", "broken", "useless", "terrible", "worst",
"hate", "scam", "awful", "garbage", "stupid"]
score = 0.7 # default neutral-positive
for word in negative_words:
if word in message.lower():
score -= 0.2
return max(0.0, min(1.0, score))
def format_response(response: str, channel: str, customer_name: str = "Customer") -> str:
style = CHANNEL_STYLES.get(channel, CHANNEL_STYLES["web_form"])
if style["greeting"]:
response = f"Hi {customer_name},\n\n{response}"
if style["signature"]:
response += "\n\nBest regards,\nCloudSync Pro Support Team"
if len(response) > style["max_len"]:
response = response[:style["max_len"] - 3] + "..."
return response
def process_message(customer_id: str, message: str, channel: str,
customer_name: str = "Customer") -> dict:
"""Core interaction loop — returns response dict with escalation info."""
# Memory
if customer_id not in _conversations:
_conversations[customer_id] = {
"messages": [], "sentiment_scores": [], "topics": [],
"original_channel": channel, "status": "active",
}
conv = _conversations[customer_id]
conv["messages"].append({"role": "customer", "content": message, "channel": channel})
sentiment = detect_sentiment(message)
conv["sentiment_scores"].append(sentiment)
should_escalate, reason = detect_escalation(message)
if not should_escalate and sentiment < 0.3:
should_escalate, reason = True, "negative_sentiment"
if should_escalate:
conv["status"] = "escalated"
reply = f"I've escalated your case to our specialist team. Reference: TICKET-{len(_conversations):04d}"
return {
"response": format_response(reply, channel, customer_name),
"escalated": True,
"escalation_reason": reason,
"sentiment": sentiment,
}
docs = search_docs(message)
if docs:
reply = f"Here's what I found:\n\n{docs[:500]}\n\nDoes that help?"
else:
reply = "I couldn't find specific information on that. Could you provide more detail?"
conv["messages"].append({"role": "agent", "content": reply})
return {
"response": format_response(reply, channel, customer_name),
"escalated": False,
"escalation_reason": None,
"sentiment": sentiment,
}