I built a multilingual support router that classifies incoming tickets by language and routes them to either a general-purpose or a multilingual specialist model. This helps teams with mixed-language queues reduce costs on long tickets by relying on flat per-request inference instead of token-based routing layers. The whole pipeline runs against Oxlo.ai's OpenAI-compatible endpoint.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK installed with
pip install openai
Step 1: Initialize the Oxlo.ai client
I start by importing the OpenAI SDK and pointing it at Oxlo.ai. Because the platform is fully OpenAI SDK compatible, this is a drop-in replacement and no extra adapters are needed.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
Step 2: Define the agent's system prompt
This prompt is shared by both model paths. It instructs the agent to match the user's language and tone, and to keep responses concise. I keep it single-shot so it works unchanged regardless of which model picks it up.
SYSTEM_PROMPT = """You are a senior SaaS support agent.
Read the ticket below and reply in the exact language the user wrote in.
If the user wrote in English, give a direct, technical answer.
If the user wrote in another language, preserve local technical terminology and polite formality.
Keep your answer under three sentences."""
Step 3: Build the triage function with Qwen 3 32B
I use Qwen 3 32B for the triage step because it handles multilingual reasoning and agent workflows well. The model returns strict JSON so the rest of the script can route deterministically, and I enable JSON mode to avoid parsing drift.
TRIAGE_PROMPT = """You are a routing layer. Read the support ticket and return only a JSON object with this exact schema:
{"language": "ISO-639-1 code", "topic": "one-word category", "urgency": "low|medium|high"}
Do not add markdown or explanation."""
def triage_ticket(text: str) -> dict:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": TRIAGE_PROMPT},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
)
raw = response.choices[0].message.content
return json.loads(raw)
Step 4: Route to the right model and generate the answer
If the ticket is English, I send it to Llama 3.3 70B for fast, general-purpose inference. For any other language, I keep it on Qwen 3 32B. Because Oxlo.ai charges per request rather than per token, splitting the workload across two calls does not scale costs with ticket length. You can see the pricing details at https://oxlo.ai/pricing.
def generate_response(ticket_text: str, triage: dict) -> tuple[str, str]:
model = "llama-3.3-70b" if triage["language"] == "en" else "qwen-3-32b"
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Language: {triage['language']}\nTopic: {triage['topic']}\nUrgency: {triage['urgency']}\nTicket: {ticket_text}"},
],
)
return model, response.choices[0].message.content
Run it
I test the pipeline on three tickets: English, Spanish, and Japanese. The script prints which model handled each one so you can see the routing in action.
if __name__ == "__main__":
tickets = [
"My API key started returning 401 errors immediately after rotation.",
"El dashboard no carga después de la actualización de ayer, necesito ayuda urgente.",
"昨日からレポートのエクスポートが失敗します。至急確認してください。",
]
for t in tickets:
triage = triage_ticket(t)
model, answer = generate_response(t, triage)
print(f"Ticket: {t[:45]}...")
print(f"Triage: {triage}")
print(f"Routed to: {model}")
print(f"Response: {answer}\n")
Example output:
Ticket: My API key started returning 401 errors immedia...
Triage: {'language': 'en', 'topic': 'authentication', 'urgency': 'high'}
Routed to: llama-3.3-70b
Response: Check that your new key is active in the Oxlo.ai portal and that you are using the correct base URL, https://api.oxlo.ai/v1. Clear any cached keys and retry.
Ticket: El dashboard no carga después de la actualizaci...
Triage: {'language': 'es', 'topic': 'dashboard', 'urgency': 'high'}
Routed to: qwen-3-32b
Response: Entiendo la urgencia. Por favor, intenta limpiar la caché del navegador y verifica el estado del servicio en nuestra página de estado. Si el problema persiste, envíanos los logs de la consola.
Ticket: 昨日からレポートのエクスポートが失敗します。至急確認してください。...
Triage: {'language': 'ja', 'topic': 'export', 'urgency': 'high'}
Routed to: qwen-3-32b
Response: ご連絡ありがとうございます。エクスポート設定とファイル形式を確認いたします。しばらくお待ちいただくか、サポートチャットでトランザクションIDをお送りください。
Wrap-up
You now have a working router that treats multilingual tickets as first-class workloads instead of forcing them through a general-purpose pipeline. For a production version, cache the triage step with a lightweight embedding check using Oxlo.ai's BGE-Large endpoint, or swap in DeepSeek V3.2 for English code tickets and Kimi K2.6 for vision-heavy agentic workflows.
Top comments (0)