We are going to build a support ticket NLU pipeline that classifies intent and extracts entities from raw customer messages. This gives you a structured feed from unstructured text, which is the first step in any automated support workflow. I run this on Oxlo.ai because the flat per-request pricing stays predictable even when I stuff long ticket threads into the prompt.
What you'll need
- Python 3.10 or higher
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Step 1: Configure the Oxlo.ai client
Create a file named nlu_agent.py and initialize the client. Oxlo.ai exposes a fully OpenAI-compatible endpoint, so the only difference is the base URL.
from openai import OpenAI
import json
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
Step 2: Define the NLU system prompt
I treat the system prompt as a strict contract. It tells the model to return only valid JSON with two keys, intent and entities. I keep the entity list open so it generalizes to new products without retraining.
SYSTEM_PROMPT = """You are an NLU engine for customer support. Analyze the user's message and output strictly valid JSON with no markdown formatting.
Required JSON schema:
{
"intent": "One of: REFUND_REQUEST, TECHNICAL_SUPPORT, BILLING_QUESTION, ACCOUNT_ACCESS, GENERAL_INQUIRY, ESCALATE",
"entities": {
"product_name": "string or null",
"order_id": "string or null",
"email": "string or null",
"urgency": "One of: LOW, MEDIUM, HIGH, CRITICAL"
},
"reasoning": "Brief 10-word explanation of why this intent was chosen."
}
Rules:
- intent must be exactly one of the listed enums.
- If a value is missing, use null, do not guess.
- urgency is CRITICAL if the user mentions 'down', 'broken', or 'cannot access' and seems blocked.
"""
Step 3: Build the extraction function with JSON mode
I wrap the chat completion in a small function that enforces JSON mode. Oxlo.ai supports this on llama-3.3-70b and other models, so I get parsable output without fragile regex.
def extract_nlu(message: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": message},
],
response_format={"type": "json_object"},
temperature=0.1,
)
raw = response.choices[0].message.content
return json.loads(raw)
Step 4: Add the batch processor
Most support queues do not arrive one at a time. I add a small batch runner that processes a list of messages and prints line-delimited JSON. This is the piece I actually ship into my ingestion worker.
def process_batch(messages: list[str]) -> list[dict]:
results = []
for msg in messages:
try:
parsed = extract_nlu(msg)
results.append(parsed)
except Exception as e:
results.append({"error": str(e), "raw_message": msg})
return results
if __name__ == "__main__":
tickets = [
"My Acme Pro subscription renewed twice this month. Order #ORD-9981. Please refund the duplicate charge.",
"The dashboard is completely down and I cannot pull reports. This is blocking our quarterly review.",
"How do I add a new team member to my workspace?",
]
parsed = process_batch(tickets)
for item in parsed:
print(json.dumps(item, indent=2))
Run it
Export your key and execute the script. I use Oxlo.ai here because long ticket threads do not inflate the cost, each API call is one flat request regardless of how much prior context I include.
export OXLO_API_KEY="sk-oxlo.ai-..."
python nlu_agent.py
Example output:
{
"intent": "BILLING_QUESTION",
"entities": {
"product_name": "Acme Pro",
"order_id": "ORD-9981",
"email": null,
"urgency": "MEDIUM"
},
"reasoning": "User requests refund for duplicate charge."
}
{
"intent": "TECHNICAL_SUPPORT",
"entities": {
"product_name": null,
"order_id": null,
"email": null,
"urgency": "CRITICAL"
},
"reasoning": "Dashboard is down and blocking quarterly review."
}
{
"intent": "GENERAL_INQUIRY",
"entities": {
"product_name": null,
"order_id": null,
"email": null,
"urgency": "LOW"
},
"reasoning": "User asks how to add team member."
}
Wrap-up
Next, wire this NLU function into your actual support queue via a webhook or message broker like RabbitMQ. If you need to process thousands of tickets daily, switch to Oxlo.ai's request-based plans so your bill stays predictable even when user messages grow into long threads. See https://oxlo.ai/pricing for plan details.
Top comments (0)