> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evermind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Support Bot

> Build a support bot with ticket history and customer context

Build a customer support bot that remembers customer history, past interactions,
and preferences, so customers don't repeat themselves and agents inherit full
context on handoff.

## Architecture

* **Per-customer memory:** every message is attributed to the customer via
  `sender_id`; search by `user_id` spans all of that customer's history.
* **Session per ticket:** each ticket uses its own `session_id`, so a ticket
  extracts as a coherent unit while still rolling up under the customer.
* **Cross-ticket recall:** search a customer's memory to surface similar past
  issues, regardless of which ticket they happened in.

## Setup

```bash theme={null}
pip install everos-cloud
export EVEROS_API_KEY="your_api_key"
```

```python theme={null}
from everos_cloud import EverOS

client = EverOS()  # reads EVEROS_API_KEY
```

## Store ticket messages

The customer's messages carry their `customer_id` as `sender_id`; the bot's
replies use a bot sender id with `role="assistant"`. A ticket maps to a session.

```python theme={null}
BOT_SENDER = "support_bot"

def store_message(customer_id: str, ticket_id: str, role: str, content: str):
    client.add(
        session_id=f"ticket_{ticket_id}",
        messages=[
            {
                "sender_id": customer_id if role == "user" else BOT_SENDER,
                "role": role,
                "content": content,
            }
        ],
    )
```

## Gather context for a reply

Search the customer's memory for relevant past episodes. Episodes are narrative
summaries of prior interactions; `include_profile=True` also returns the
consolidated customer profile once it has built up.

```python theme={null}
def gather_context(customer_id: str, query: str) -> str:
    results = client.search(
        query, user_id=customer_id, method="hybrid",
        top_k=5, include_profile=True,
    )
    parts = [f"[Profile] {p.profile_data}" for p in (results.profiles or [])]
    parts += [f"[Past issue] {e.episode}" for e in (results.episodes or [])]
    return "\n".join(parts) if parts else "No prior history for this customer."
```

## Complete support bot

```python theme={null}
from everos_cloud import EverOS

client = EverOS()
BOT_SENDER = "support_bot"


class CustomerSupportBot:
    def _store(self, customer_id: str, ticket_id: str, role: str, content: str):
        client.add(
            session_id=f"ticket_{ticket_id}",
            messages=[{
                "sender_id": customer_id if role == "user" else BOT_SENDER,
                "role": role,
                "content": content,
            }],
        )

    def _context(self, customer_id: str, query: str) -> str:
        results = client.search(
            query, user_id=customer_id, method="hybrid",
            top_k=5, include_profile=True,
        )
        parts = [f"[Profile] {p.profile_data}" for p in (results.profiles or [])]
        parts += [f"[Past issue] {e.episode}" for e in (results.episodes or [])]
        return "\n".join(parts) if parts else "No prior history for this customer."

    def _generate(self, message: str, context: str) -> str:
        prompt = f"""You are a helpful support agent. Use the customer's history
to avoid asking them to repeat themselves. Reference past issues when relevant.

CUSTOMER CONTEXT:
{context}

CUSTOMER MESSAGE:
{message}"""
        # Replace with your LLM call.
        return "[LLM support reply grounded in the context above]"

    def handle_message(self, customer_id: str, ticket_id: str, message: str) -> str:
        # Recall context BEFORE storing, so we don't retrieve the current message
        context = self._context(customer_id, message)
        reply = self._generate(message, context)
        self._store(customer_id, ticket_id, "user", message)
        self._store(customer_id, ticket_id, "assistant", reply)
        return reply

    def close_ticket(self, ticket_id: str):
        # Force extraction of the ticket when it's resolved
        client.flush(f"ticket_{ticket_id}")

    def handoff_to_agent(self, customer_id: str, ticket_id: str, agent: str) -> dict:
        results = client.search(
            "issue summary and resolution attempts",
            user_id=customer_id, method="hybrid", top_k=10,
        )
        return {
            "ticket_id": ticket_id,
            "history": [e.summary for e in (results.episodes or [])],
            "handoff_note": f"Escalated to {agent}. Full context above.",
        }


bot = CustomerSupportBot()

print(bot.handle_message("customer_john", "T-2024-001",
    "I've been trying to reset my password but the email never arrives."))
print(bot.handle_message("customer_john", "T-2024-001",
    "I checked spam too. My email is john@example.com."))

bot.close_ticket("T-2024-001")
print(bot.handoff_to_agent("customer_john", "T-2024-001", "Sarah"))
```

## Cross-ticket intelligence

Because search is scoped to the customer (not a single ticket), you can surface
recurring issues across their entire history.

```python theme={null}
def find_recurring_issues(customer_id: str) -> list:
    results = client.search(
        "recurring issue, problem, error, unable to",
        user_id=customer_id, method="hybrid", top_k=20,
    )
    return results.episodes or []

history = find_recurring_issues("customer_john")
print(f"Found {len(history)} relevant past interactions")
```

## Best practices

<AccordionGroup>
  <Accordion title="One session per ticket">
    Use a unique `session_id` per ticket so each extracts as a coherent unit,
    while `user_id` search still unifies the customer across tickets.

    ```python theme={null}
    session_id = f"ticket_{ticket_id}"   # good: isolates each ticket
    ```
  </Accordion>

  <Accordion title="Recall before you store">
    Search for context *before* storing the incoming message, so retrieval
    reflects prior history rather than echoing the message you just received.
  </Accordion>

  <Accordion title="Privacy">
    Filter sensitive content out of episodes before putting them in an LLM prompt.

    ```python theme={null}
    safe = [e for e in episodes
            if not any(t in (e.episode or "").lower()
                       for t in ("ssn", "credit card", "password"))]
    ```
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="AI Tutor" icon="graduation-cap" href="/cookbook/ai-tutor">
    Apply the same recall-generate-persist loop to learning.
  </Card>

  <Card title="Agentic retrieval" icon="wand-magic-sparkles" href="/cloud/agentic-retrieval">
    LLM-guided search for complex, multi-part support questions.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.