Skip to main content
Build a customer support bot that remembers customer history, past tickets, and preferences. This enables personalized, context-aware support without customers repeating themselves.

Architecture

Customer Support Bot Architecture Key concepts:
  • Ticket isolation: Each support ticket is a separate group_id
  • Customer profile: User-level memories persist across tickets
  • Cross-ticket search: Find relevant past issues for the same customer

Setup: Support Bot Configuration

import requests
from datetime import datetime
import uuid

BASE_URL = "https://api.evermind.ai"
headers = {"Content-Type": "application/json"}

class SupportBot:
    def __init__(self, bot_id: str = "support_bot"):
        self.bot_id = bot_id

    def create_ticket(self, ticket_id: str, customer_id: str, customer_name: str, subject: str):
        """Initialize a new support ticket."""
        group_id = f"ticket_{ticket_id}"

        # Configure as assistant scene for full memory extraction
        meta = {
            "group_id": group_id,
            "group_name": f"Support: {subject}",
            "scene": "assistant",
            "user_details": [
                {"user_id": customer_id, "user_name": customer_name, "role": "user"},
                {"user_id": self.bot_id, "user_name": "Support Bot", "role": "assistant"}
            ]
        }

        requests.post(f"{BASE_URL}/api/v0/memories/conversation-meta", json=meta, headers=headers)

        # Store ticket creation event
        self._store_message(
            group_id=group_id,
            sender_id=self.bot_id,
            content=f"New support ticket created. Subject: {subject}. Customer: {customer_name}"
        )

        return group_id

    def _store_message(self, group_id: str, sender_id: str, content: str):
        """Store a message in the ticket."""
        message = {
            "group_id": group_id,
            "group_name": "Support Ticket",
            "message_id": str(uuid.uuid4()),
            "create_time": datetime.now().isoformat() + "Z",
            "sender": sender_id,
            "sender_name": "Customer" if sender_id != self.bot_id else "Support Bot",
            "content": content
        }
        return requests.post(f"{BASE_URL}/api/v0/memories", json=message, headers=headers)

Customer Message Handling

When a customer sends a message, store it and retrieve relevant context.
def handle_customer_message(
    self,
    ticket_id: str,
    customer_id: str,
    message: str
) -> dict:
    """Process customer message and return context for response generation."""
    group_id = f"ticket_{ticket_id}"

    # 1. Store the customer message
    self._store_message(group_id, customer_id, message)

    # 2. Get context from multiple sources
    context = self._gather_context(customer_id, message, group_id)

    return context

def _gather_context(self, customer_id: str, query: str, current_ticket: str) -> dict:
    """Gather relevant context from EverMemOS."""

    # Customer profile (preferences, account info)
    profile_memories = self._search_memories(
        user_id=customer_id,
        query=query,
        memory_types=["profile"],
        top_k=5
    )

    # Current ticket history
    current_ticket_memories = self._search_memories(
        group_ids=[current_ticket],
        query=query,
        memory_types=["episodic_memory"],
        top_k=3
    )

    # Past tickets (similar issues)
    past_ticket_memories = self._search_memories(
        user_id=customer_id,
        query=query,
        memory_types=["episodic_memory"],
        top_k=5
    )
    # Filter out current ticket
    past_ticket_memories = [
        m for m in past_ticket_memories
        if m.get("group_id") != current_ticket
    ]

    return {
        "customer_profile": profile_memories,
        "current_ticket": current_ticket_memories,
        "past_tickets": past_ticket_memories[:3]  # Limit to most relevant
    }

def _search_memories(self, query: str, memory_types: list, top_k: int,
                     user_id: str = None, group_ids: list = None) -> list:
    """Search EverMemOS for relevant memories."""
    search_params = {
        "query": query,
        "retrieve_method": "hybrid",
        "top_k": top_k,
        "memory_types": memory_types
    }

    if user_id:
        search_params["user_id"] = user_id
    if group_ids:
        search_params["group_ids"] = group_ids

    response = requests.get(
        f"{BASE_URL}/api/v0/memories/search",
        json=search_params,
        headers=headers
    )
    return response.json().get("result", {}).get("memories", [])

Generate Context-Aware Response

Use retrieved context to generate personalized responses.
def generate_response(self, customer_message: str, context: dict) -> str:
    """Generate response using LLM with memory context."""

    # Format context for LLM
    context_text = self._format_context(context)

    prompt = f"""You are a helpful customer support agent. Use the following context to provide personalized support.

CUSTOMER PROFILE:
{context_text['profile']}

CURRENT TICKET CONTEXT:
{context_text['current']}

RELEVANT PAST TICKETS:
{context_text['past']}

CUSTOMER MESSAGE:
{customer_message}

Instructions:
- Address the customer by name if known
- Reference past issues if relevant (e.g., "I see you had a similar issue before...")
- Use customer preferences (e.g., communication style, technical level)
- Don't repeat information the customer already provided
- Be concise but thorough

Response:"""

    # Replace with your LLM call
    # response = openai.chat.completions.create(...)
    return "[LLM response here]"

def _format_context(self, context: dict) -> dict:
    """Format context for LLM prompt."""
    profile_text = "\n".join([
        f"- {m.get('memory_content', '')}"
        for m in context.get("customer_profile", [])
    ]) or "No profile information available."

    current_text = "\n".join([
        f"- {m.get('memory_content', '')}"
        for m in context.get("current_ticket", [])
    ]) or "New ticket, no history yet."

    past_text = "\n".join([
        f"- [{m.get('group_id', 'unknown')}] {m.get('memory_content', '')}"
        for m in context.get("past_tickets", [])
    ]) or "No relevant past tickets."

    return {
        "profile": profile_text,
        "current": current_text,
        "past": past_text
    }

Complete Support Bot Implementation

import requests
from datetime import datetime
import uuid

BASE_URL = "https://api.evermind.ai"
headers = {"Content-Type": "application/json"}

class CustomerSupportBot:
    def __init__(self):
        self.bot_id = "support_bot"

    def create_ticket(self, ticket_id: str, customer_id: str, customer_name: str, subject: str) -> str:
        """Create a new support ticket."""
        group_id = f"ticket_{ticket_id}"

        meta = {
            "group_id": group_id,
            "group_name": f"Support: {subject}",
            "scene": "assistant",
            "user_details": [
                {"user_id": customer_id, "user_name": customer_name, "role": "user"},
                {"user_id": self.bot_id, "user_name": "Support Bot", "role": "assistant"}
            ]
        }
        requests.post(f"{BASE_URL}/api/v0/memories/conversation-meta", json=meta, headers=headers)

        self._store(group_id, self.bot_id, f"Ticket opened: {subject}")
        return group_id

    def customer_message(self, ticket_id: str, customer_id: str, message: str) -> str:
        """Handle customer message and generate response."""
        group_id = f"ticket_{ticket_id}"

        # Store customer message
        self._store(group_id, customer_id, message)

        # Gather context
        context = self._get_context(customer_id, message, group_id)

        # Generate response (implement with your LLM)
        response = self._generate_response(message, context)

        # Store bot response
        self._store(group_id, self.bot_id, response)

        return response

    def escalate_to_agent(self, ticket_id: str, agent_id: str, agent_name: str) -> dict:
        """Generate handoff context for human agent."""
        group_id = f"ticket_{ticket_id}"

        # Get comprehensive context
        summary = self._search(group_ids=[group_id], query="issue summary resolution attempts", top_k=10)

        return {
            "ticket_id": ticket_id,
            "summary": [m.get("memory_content") for m in summary],
            "handoff_note": f"Escalated to {agent_name}. See memory context for full history."
        }

    def _store(self, group_id: str, sender_id: str, content: str):
        message = {
            "group_id": group_id,
            "group_name": "Support",
            "message_id": str(uuid.uuid4()),
            "create_time": datetime.now().isoformat() + "Z",
            "sender": sender_id,
            "sender_name": "Support Bot" if sender_id == self.bot_id else "Customer",
            "content": content
        }
        requests.post(f"{BASE_URL}/api/v0/memories", json=message, headers=headers)

    def _search(self, query: str, top_k: int = 5, user_id: str = None, group_ids: list = None, memory_types: list = None) -> list:
        params = {
            "query": query,
            "retrieve_method": "hybrid",
            "top_k": top_k,
            "memory_types": memory_types or ["episodic_memory", "profile"]
        }
        if user_id:
            params["user_id"] = user_id
        if group_ids:
            params["group_ids"] = group_ids

        resp = requests.get(f"{BASE_URL}/api/v0/memories/search", json=params, headers=headers)
        return resp.json().get("result", {}).get("memories", [])

    def _get_context(self, customer_id: str, query: str, current_group: str) -> dict:
        return {
            "profile": self._search(user_id=customer_id, query=query, memory_types=["profile"]),
            "current": self._search(group_ids=[current_group], query=query, memory_types=["episodic_memory"], top_k=3),
            "history": [m for m in self._search(user_id=customer_id, query=query) if m.get("group_id") != current_group][:3]
        }

    def _generate_response(self, message: str, context: dict) -> str:
        # Implement with your LLM
        profile_summary = "; ".join([m.get("memory_content", "")[:50] for m in context["profile"]])
        return f"[Response using context: {profile_summary}...]"


# Usage Example
bot = CustomerSupportBot()

# Customer opens a ticket
ticket = bot.create_ticket(
    ticket_id="T-2024-001",
    customer_id="customer_john",
    customer_name="John Smith",
    subject="Unable to reset password"
)

# Customer sends messages
response1 = bot.customer_message(
    ticket_id="T-2024-001",
    customer_id="customer_john",
    message="I've been trying to reset my password but the email never arrives."
)
print(f"Bot: {response1}")

response2 = bot.customer_message(
    ticket_id="T-2024-001",
    customer_id="customer_john",
    message="I checked spam folder too. My email is john@example.com"
)
print(f"Bot: {response2}")

# Escalate if needed
handoff = bot.escalate_to_agent("T-2024-001", "agent_sarah", "Sarah")
print(f"Handoff context: {handoff}")

Cross-Ticket Intelligence

Search across all of a customer’s tickets to find patterns.
def find_recurring_issues(customer_id: str) -> list:
    """Find patterns across customer's support history."""
    search_params = {
        "user_id": customer_id,
        "query": "issue problem error unable",
        "retrieve_method": "agentic",  # Use agentic for complex pattern finding
        "top_k": 20,
        "memory_types": ["episodic_memory"]
    }

    response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=search_params, headers=headers)
    memories = response.json().get("result", {}).get("memories", [])

    # Group by ticket
    tickets = {}
    for mem in memories:
        ticket = mem.get("group_id", "unknown")
        if ticket not in tickets:
            tickets[ticket] = []
        tickets[ticket].append(mem.get("memory_content"))

    return tickets

# Find if customer has had similar issues before
history = find_recurring_issues("customer_john")
if len(history) > 1:
    print(f"Customer has {len(history)} previous tickets")

Best Practices

Always use unique group_id per ticket to maintain isolation.
# Good: Ticket-based group IDs
group_id = f"ticket_{ticket_id}"
group_id = f"support_{customer_id}_{ticket_number}"

# Bad: Shared group IDs
group_id = "support"  # All tickets mixed together
Be mindful of PII when generating LLM prompts.
# Filter sensitive profile memories before LLM
safe_memories = [
    m for m in profile_memories
    if not any(term in m.get("memory_content", "").lower()
              for term in ["ssn", "credit card", "password"])
]
Provide structured context for human agents.
handoff = {
    "ticket_id": ticket_id,
    "customer_summary": "3-year customer, premium tier",
    "issue_summary": "Password reset email not arriving",
    "attempted_solutions": ["Checked spam", "Verified email"],
    "customer_sentiment": "Frustrated but polite",
    "relevant_history": ["Similar issue 6 months ago - spam filter"]
}

Next Steps