A practical crewai customer support triage example shows how to split incoming tickets into classification, urgency scoring, and routing without a single oversized prompt. This walkthrough builds that crew with CrewAI, connects it to a resilient model gateway, and gives you a verification path you can run in CI.
Step 1: Install dependencies and scaffold the project
The crewai customer support triage example below assumes Python 3.11+ and a virtual environment. Install the framework and a dotenv loader:
pip install crewai langchain-openai python-dotenv
Create a .env file to hold your gateway credentials. Keep model names out of source control.
OPENAI_API_KEY=sk-your-key
OPENAI_API_BASE=https://api.n4n.ai/v1
TRIAGE_MODEL=gpt-4o-mini
Load these in your entry script:
import os
from dotenv import load_dotenv
load_dotenv()
MODEL = os.getenv("TRIAGE_MODEL", "gpt-4o-mini")
BASE_URL = os.getenv("OPENAI_API_BASE")
API_KEY = os.getenv("OPENAI_API_KEY")
Step 2: Define the incoming ticket shape
Triage only works if you constrain the input. Use a plain dict or Pydantic model. A minimal ticket:
{
"ticket_id": "SUP-1042",
"subject": "Cannot export CSV from dashboard",
"body": "The export button spins forever after the latest update. I have a quarterly report due.",
"customer_tier": "enterprise"
}
In code, pass this as the inputs dict to the crew. Don’t embed the raw email thread as one string; separate subject and body so agents can reference fields explicitly.
Step 3: Build the triage agents
In this crewai customer support triage example, we define three narrow agents. Each has a single responsibility, which keeps prompts short and eval-able.
from crewai import Agent
classifier = Agent(
role="Support Classifier",
goal="Map the ticket to a product area: billing, dashboard, api, or account",
backstory="You have tagged 50k support tickets. You output only the area name.",
allow_delegation=False,
verbose=False,
)
urgency = Agent(
role="Urgency Scorer",
goal="Score urgency 1-5 using customer tier and business impact language",
backstory="You prioritize enterprise blockers over cosmetic issues.",
allow_delegation=False,
verbose=False,
)
router = Agent(
role="Queue Router",
goal="Pick the owning team and suggest a first-response template",
backstory="You know the on-call map and standard macros.",
allow_delegation=False,
verbose=False,
)
Narrow roles beat a “general support bot” because you can unit-test each output independently.
Step 4: Create tasks with explicit outputs
Tasks bind agents to inputs and force structured output. Use output_json with a small schema so the next step can parse without regex.
from crewai import Task
from pydantic import BaseModel
class ClassOut(BaseModel):
area: str
class UrgOut(BaseModel):
score: int
reason: str
class RouteOut(BaseModel):
team: str
macro: str
t_class = Task(
description="Classify ticket {ticket_id}: {subject} | {body}",
agent=classifier,
expected_output="JSON with area",
output_json=ClassOut,
)
t_urg = Task(
description="Score urgency for tier {customer_tier}: {subject} | {body}",
agent=urgency,
expected_output="JSON with score and reason",
output_json=UrgOut,
)
t_route = Task(
description="Given area {area} and urgency {score}, pick team and macro",
agent=router,
expected_output="JSON with team and macro",
output_json=RouteOut,
)
The {area} and {score} placeholders are filled from prior task outputs when you use a sequential process.
Step 5: Point the crew at a resilient LLM backend
CrewAI uses LangChain under the hood. Instantiate ChatOpenAI with your gateway base URL. Pointing the crew at n4n.ai’s OpenAI-compatible endpoint gives you access to 240+ models and automatic fallback when a provider is rate-limited, so a single model name doesn’t become a single point of failure.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model=MODEL,
base_url=BASE_URL,
api_key=API_KEY,
temperature=0,
)
Attach the LLM to each agent by passing llm=llm in the Agent constructor, or set it globally via crewai.llm if your version supports it. Use temperature=0 for triage—determinism matters more than creativity.
Step 6: Assemble and run the crew
Running the crewai customer support triage example end to end takes under a second per ticket on a small model. Wire the agents and tasks:
from crewai import Crew, Process
crew = Crew(
agents=[classifier, urgency, router],
tasks=[t_class, t_urg, t_route],
process=Process.sequential,
verbose=True,
)
ticket = {
"ticket_id": "SUP-1042",
"subject": "Cannot export CSV from dashboard",
"body": "The export button spins forever after the latest update. I have a quarterly report due.",
"customer_tier": "enterprise",
}
result = crew.kickoff(inputs=ticket)
print(result)
The final task returns a RouteOut JSON. Intermediate outputs are accessible via crew.tasks[i].output.json_dict if you need to log them.
Step 7: Verify the triage output
Success means the router output is valid and the classification matches a held-out label. Write a smoke test:
def test_triage_runs():
out = crew.kickoff(inputs=ticket)
data = out.json_dict
assert data["team"] in {"dashboard_oncall", "billing_oncall", "api_oncall", "account_team"}
assert isinstance(data["macro"], str) and len(data["macro"]) > 0
Run it in CI against a fixed ticket to catch prompt drift. For manual verification, print crew.tasks[0].output.json_dict and confirm area == "dashboard" on the sample above. If the classifier flips to api, your prompt or model version changed.
Token usage should be small: three short completions, not one long chain. If you see thousands of input tokens per ticket, you’re leaking conversation history.
Step 8: Production considerations
Cache repeated triage patterns. Gateways like n4n.ai honor client routing directives and forward provider cache-control hints, which matters when you see the same “export broken” spike across hundreds of tickets. Set cache_control on the classifier prompt and let the gateway return cached completions.
Add a fallback rule outside the crew: if score >= 4 and customer_tier == "enterprise", page a human regardless of model output. The crew accelerates routing; it doesn’t replace your escalation policy.
Meter cost per route. Per-token usage metering lets you attribute spend to the router agent versus the urgency scorer, so you can swap the classifier to a cheaper model without guessing. Keep the crew’s task boundaries fixed even as you change models underneath—that’s the whole point of the decomposition.