Building a serverless customer support triage agent that classifies incoming tickets, drafts first-pass replies, and routes them to the correct team. We will deploy it as an AWS Lambda function backed by Oxlo.ai, where flat per-request pricing keeps costs predictable even when customers paste long logs or full chat histories. Details are at https://oxlo.ai/pricing.
What you'll need
- Python 3.10 or newer
- An AWS account with Lambda and API Gateway access
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Step 1: Scaffold the Lambda handler and dependencies
Create a new directory and a requirements file that pins the OpenAI SDK. We will keep everything in a single handler file so deployment stays simple.
mkdir serverless-triage && cd serverless-triage
cat > requirements.txt << 'EOF'
openai>=1.0.0
EOF
cat > handler.py << 'EOF'
import json
import os
from openai import OpenAI
OXLO_API_KEY = os.environ["OXLO_API_KEY"]
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=OXLO_API_KEY,
)
def lambda_handler(event, context):
# Parse the incoming support ticket
body = json.loads(event.get("body", "{}"))
ticket = body.get("ticket", "")
return {
"statusCode": 200,
"body": json.dumps({"received": ticket})
}
EOF
Step 2: Define the system prompt
The agent needs to return structured JSON every time. I use a system prompt that forces a strict schema so downstream routing logic never has to guess.
SYSTEM_PROMPT = """You are a support triage agent. Analyze the ticket below and return ONLY a JSON object with no markdown formatting.
Required keys:
- urgency: one of "critical", "high", "normal", "low"
- team: one of "billing", "technical", "account", "general"
- reply: a polite first-pass response to the customer
- reason: one sentence explaining the classification
Rules:
- If the ticket contains payment errors or refund requests, team is "billing".
- If the ticket contains API errors, integration questions, or code snippets, team is "technical".
- Keep the reply under 100 words.
- Do not include
```json or ```
markers."""
Step 3: Wire the Oxlo.ai inference call
I use Llama 3.3 70B because it follows structured instructions reliably and handles long ticket threads without issue. Because Oxlo.ai charges per request instead of per token, pasting a 4,000-word log transcript costs the same as a single sentence. Update handler.py with the inference logic.
import json
import os
from openai import OpenAI
OXLO_API_KEY = os.environ["OXLO_API_KEY"]
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=OXLO_API_KEY,
)
SYSTEM_PROMPT = """You are a support triage agent. Analyze the ticket below and return ONLY a JSON object with no markdown formatting.
Required keys:
- urgency: one of "critical", "high", "normal", "low"
- team: one of "billing", "technical", "account", "general"
- reply: a polite first-pass response to the customer
- reason: one sentence explaining the classification
Rules:
- If the ticket contains payment errors or refund requests, team is "billing".
- If the ticket contains API errors, integration questions, or code snippets, team is "technical".
- Keep the reply under 100 words.
- Do not include
```json or ```
markers."""
def lambda_handler(event, context):
body = json.loads(event.get("body", "{}"))
ticket = body.get("ticket", "")
if not ticket:
return {"statusCode": 400, "body": json.dumps({"error": "missing ticket"})}
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": ticket},
],
)
raw = response.choices[0].message.content
result = json.loads(raw)
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(result)
}
Step 4: Add validation and error handling
Lambda retries can get expensive if the function crashes on malformed LLM output. I add a small guard that catches JSON decode errors and falls back to a safe default.
import json
import os
import traceback
from openai import OpenAI
OXLO_API_KEY = os.environ["OXLO_API_KEY"]
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=OXLO_API_KEY,
)
SYSTEM_PROMPT = """You are a support triage agent. Analyze the ticket below and return ONLY a JSON object with no markdown formatting.
Required keys:
- urgency: one of "critical", "high", "normal", "low"
- team: one of "billing", "technical", "account", "general"
- reply: a polite first-pass response to the customer
- reason: one sentence explaining the classification
Rules:
- If the ticket contains payment errors or refund requests, team is "billing".
- If the ticket contains API errors, integration questions, or code snippets, team is "technical".
- Keep the reply under 100 words.
- Do not include
```json or ```
markers."""
def lambda_handler(event, context):
body = json.loads(event.get("body", "{}"))
ticket = body.get("ticket", "")
if not ticket:
return {"statusCode": 400, "body": json.dumps({"error": "missing ticket"})}
try:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": ticket},
],
)
raw = response.choices[0].message.content.strip()
result = json.loads(raw)
# Validate required keys
for key in ("urgency", "team", "reply", "reason"):
if key not in result:
raise ValueError(f"missing key: {key}")
except Exception:
traceback.print_exc()
result = {
"urgency": "normal",
"team": "general",
"reply": "Thanks for reaching out. A support agent will review your ticket shortly.",
"reason": "Fallback due to parsing error."
}
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(result)
}
Step 5: Deploy to AWS Lambda
Package the OpenAI SDK alongside the handler, create the function, and set the Oxlo.ai key as an environment variable. I keep the timeout at 30 seconds because Oxlo.ai serves popular models with no cold starts, so the first invocation after idle time still returns quickly.
pip install openai -t python/
zip -r function.zip handler.py python/
aws lambda create-function \
--function-name support-triage-agent \
--runtime python3.11 \
--handler handler.lambda_handler \
--role arn:aws:iam::YOUR_ACCOUNT:role/lambda-execution-role \
--zip-file fileb://function.zip \
--environment Variables="{OXLO_API_KEY=YOUR_OXLO_API_KEY}" \
--timeout 30 \
--memory-size 256
Run it
Invoke the function with a test ticket containing a long error trace. Notice that the cost stays flat even though the input is several thousand tokens.
aws lambda invoke \
--function-name support-triage-agent \
--payload '{"body": "{\"ticket\": \"I was charged twice for my Pro subscription this month. My account email is dev@example.com and I need a refund immediately.\"}"}' \
response.json && cat response.json
Expected output:
{"urgency": "high", "team": "billing", "reply": "We are sorry for the double charge. Our billing team is reviewing your account and will issue a refund within 24 hours.", "reason": "The ticket explicitly mentions a duplicate charge and requests a refund."}
Next steps
Wire the Lambda to an API Gateway endpoint so your helpdesk software can POST tickets directly. If you start handling multilingual tickets, swap the model to qwen-3-32b in the client call without changing any other code.
Top comments (0)