DEV Community

shashank ms
shashank ms

Posted on

Zero-Shot Learning with LLMs: Applications and Techniques

We are building a zero-shot support ticket triage agent that classifies incoming messages into categories and assigns urgency without any training examples. This is useful for teams that need to route tickets quickly but do not have labeled data to fine-tune a model. Because Oxlo.ai uses flat per-request pricing (https://oxlo.ai/pricing), long tickets with heavy context do not inflate your bill, which makes this workload predictable.

What you'll need

  • Python 3.10 or newer
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • The OpenAI SDK: pip install openai
  • Pydantic for validation: pip install pydantic

Step 1: Wire up the Oxlo.ai client

Test that your key works with a simple call. I use the OpenAI SDK as a drop-in client because Oxlo.ai is fully compatible.

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.environ.get("OXLO_API_KEY", "YOUR_OXLO_API_KEY")
)

response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say 'Oxlo.ai client is ready'"},
    ],
)

print(response.choices[0].message.content)

Step 2: Write the zero-shot system prompt

The prompt is the entire training signal. We define the categories, the JSON schema, and the rules. No examples are provided. This is zero-shot.

SYSTEM_PROMPT = """You are a support ticket triage agent.
Your job is to read a ticket and return a JSON object with exactly these keys:
- category: one of ["Billing", "Bug", "Feature Request", "Account Access", "Other"]
- urgency: one of ["Low", "Medium", "High", "Critical"]
- reasoning: a short sentence explaining your decision

Rules:
1. Base your decision only on the text provided.
2. If the user cannot log in or access their account, use "Account Access".
3. If the user reports broken functionality, use "Bug".
4. If the user asks for a new capability, use "Feature Request".
5. If the user mentions payment, invoice, or charges, use "Billing".
6. Respond with valid JSON only. Do not wrap it in markdown fences."""

print("Prompt loaded. Length:", len(SYSTEM_PROMPT))

Step 3: Build the triage function

Now we wrap the call. We pass the raw ticket as the user message and ask for JSON. I use llama-3.3-70b here because it follows instructions well for classification tasks.

import json

def triage_ticket(subject: str, body: str) -> dict:
    user_message = f"Subject: {subject}\n\nBody:\n{body}"
    
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        temperature=0.1,
        max_tokens=256,
    )
    
    raw = response.choices[0].message.content.strip()
    if raw.startswith("

```"):
        raw = raw.split("\n", 1)[1].rsplit("```

", 1)[0].strip()
    return json.loads(raw)

test = triage_ticket(
    subject="Invoice PDF not downloading",
    body="I need last month's invoice for accounting. The download button spins and never loads. This is blocking our quarter-end close."
)
print(json.dumps(test, indent=2))

Step 4: Validate with Pydantic

Zero-shot outputs can drift, especially if the ticket is ambiguous. We add a Pydantic model to validate and retry if the JSON is malformed.

from pydantic import BaseModel, Field, ValidationError
from typing import Literal
import time

class TriageResult(BaseModel):
    category: Literal["Billing", "Bug", "Feature Request", "Account Access", "Other"]
    urgency: Literal["Low", "Medium", "High", "Critical"]
    reasoning: str = Field(min_length=5)

def triage_ticket_safe(subject: str, body: str, retries: int = 2) -> TriageResult:
    user_message = f"Subject: {subject}\n\nBody:\n{body}"
    last_error = None
    
    for attempt in range(retries + 1):
        try:
            response = client.chat.completions.create(
                model="llama-3.3-70b",
                messages=[
                    {"role": "system", "content": SYSTEM_PROMPT},
                    {"role": "user", "content": user_message},
                ],
                temperature=0.1,
                max_tokens=256,
            )
            
            raw = response.choices[0].message.content.strip()
            if raw.startswith("

```"):
                raw = raw.split("\n", 1)[1].rsplit("```

", 1)[0].strip()
            
            data = json.loads(raw)
            return TriageResult(**data)
        except (json.JSONDecodeError, ValidationError) as e:
            last_error = e
            time.sleep(0.5)
    
    raise RuntimeError(f"Triage failed after {retries} retries: {last_error}")

result = triage_ticket_safe("Invoice PDF not downloading", "The download button spins and never loads.")
print(result.model_dump_json(indent=2))

Step 5: Process a batch of tickets

In production you will handle many tickets. Here is a simple loop that runs them through the agent and prints a summary. Because Oxlo.ai uses per-request pricing, you know the cost of this batch before you run it.

tickets = [
    {"subject": "Refund request", "body": "I was charged twice on March 1st. I need a refund for the duplicate charge."},
    {"subject": "Dark mode", "body": "It would be great if the dashboard had a dark mode for late night shifts."},
    {"subject": "502 error on /api/v1/export", "body": "Our automated export job has been getting 502s since 09:00 UTC."},
    {"subject": "Reset password email not arriving", "body": "I tried resetting my password three times but I never get the email."},
]

for t in tickets:
    try:
        r = triage_ticket_safe(t["subject"], t["body"])
        print(f"{r.category:20} | {r.urgency:8} | {t['subject']}")
    except RuntimeError:
        print(f"{'FAILED':20} | {'N/A':8} | {t['subject']}")

Run it

Save the complete script as triage.py and run it.

export OXLO_API_KEY="sk-oxlo.ai-..."
python triage.py

Example output:

Billing              | High     | Refund request
Feature Request      | Low      | Dark mode
Bug                  | High     | 502 error on /api/v1/export
Account Access       | High     | Reset password email not arriving

Wrap-up

You now have a working zero-shot triage agent. A concrete next step is to wire this into an email webhook or Slack bot so it runs on every inbound ticket. If you need stronger multilingual zero-shot reasoning, swap llama-3.3-70b for qwen-3-32b or kimi-k2.6 on Oxlo.ai without changing any other code.

Top comments (0)