Data augmentation with LLMs is the fastest way to bootstrap a training set when you only have a handful of examples. In this tutorial I will walk through building a Python agent that takes ten seed customer support tickets and generates two hundred diverse variations for training a classifier. We will run everything through Oxlo.ai so the flat per-request pricing keeps costs predictable even when we pack long prompts full of examples.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
- A few seed examples, which I will define inline
Step 1: Configure the Oxlo.ai client
I use the OpenAI SDK as a drop-in replacement pointed at Oxlo.ai. I picked Llama 3.3 70B because it handles long context and instruction following reliably.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
Step 2: Define the seed data and system prompt
The system prompt is the most important part. It locks the model into a strict JSON schema and tells it to preserve intent while varying tone, length, and vocabulary.
import json
SYSTEM_PROMPT = """You are a data augmentation agent. Your job is to read a list of seed customer support tickets and generate new synthetic tickets that are semantically equivalent but textually distinct.
Rules:
- Preserve the original intent and category.
- Vary tone (frustrated, neutral, urgent, vague).
- Vary length between 10 and 60 words.
- Output strictly valid JSON with no markdown formatting.
- The JSON must contain a key "variations" which is a list of objects.
- Each object must have "text" and "category" keys.
Generate exactly 20 variations per request."""
SEED_TICKETS = [
{"text": "My order has not arrived and it is already two days late.", "category": "shipping"},
{"text": "The app crashes every time I try to upload a photo.", "category": "bug"},
{"text": "How do I change my subscription from monthly to annual?", "category": "billing"},
{"text": "I was charged twice for my last purchase.", "category": "billing"},
{"text": "The login page keeps showing an error after I enter my password.", "category": "bug"},
]
Step 3: Generate augmented batches
I send the seeds in the user message and request JSON mode so parsing is trivial. Because Oxlo.ai charges per request, I pack the full seed list into one prompt to keep costs flat.
def generate_variations(seed_list, model="llama-3.3-70b"):
user_content = (
"Seed tickets:\n" + json.dumps(seed_list, indent=2) +
"\n\nGenerate 20 new variations as JSON."
)
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_content},
],
response_format={"type": "json_object"},
temperature=0.9,
)
raw = response.choices[0].message.content
return json.loads(raw).get("variations", [])
batch = generate_variations(SEED_TICKETS)
print(f"Generated {len(batch)} examples")
for v in batch[:3]:
print(v)
Step 4: Scale with diversity controls
One batch is not enough. I loop ten times with a rotating style hint in the user message to push the model into different linguistic regions. This avoids duplicates without any extra infrastructure.
import random
STYLES = [
"write like a non-native English speaker",
"use highly technical jargon",
"be extremely brief and blunt",
"be polite but include a feature request",
"be confused and ask multiple questions",
"sound angry and demand a refund",
"write a very long run-on sentence",
"sound like a corporate lawyer",
"be friendly but mention a competitor",
"write as if using voice-to-text with odd phrasing",
]
all_variations = []
for style in STYLES:
user_content = (
f"Style constraint: {style}\n\n" +
"Seed tickets:\n" + json.dumps(SEED_TICKETS, indent=2) +
"\n\nGenerate 20 new variations as JSON."
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_content},
],
response_format={"type": "json_object"},
temperature=0.95,
)
data = json.loads(response.choices[0].message.content)
all_variations.extend(data.get("variations", []))
print(f"Total variations collected: {len(all_variations)}")
Step 5: Deduplicate and export
I deduplicate with a simple lowercase string similarity check, then write the final set to a JSONL file ready for Hugging Face or scikit-learn.
def normalize(text):
return " ".join(text.lower().split())
seen = set()
unique = []
for v in all_variations:
key = normalize(v["text"])
if key not in seen and len(v["text"]) > 5:
seen.add(key)
unique.append(v)
with open("augmented_tickets.jsonl", "w") as f:
for item in unique:
f.write(json.dumps(item) + "\n")
print(f"Wrote {len(unique)} unique examples to augmented_tickets.jsonl")
Run it
Running the full script against Oxlo.ai takes about a minute. Here is what the first few lines of output look like.
Total variations collected: 200
Wrote 187 unique examples to augmented_tickets.jsonl
And a sample of the generated records:
{"text": "Package still missing, it should have been here Tuesday.", "category": "shipping"}
{"text": "Application terminates unexpectedly during image upload process.", "category": "bug"}
{"text": "Can I switch billing cycle from monthly to yearly plan?", "category": "billing"}
{"text": "Duplicate charge appeared on my credit card statement.", "category": "billing"}
{"text": "Unable to authenticate, receiving persistent error post-password entry.", "category": "bug"}
Next steps
Feed the JSONL file into a scikit-learn pipeline to train a ticket classifier, or upload it to Oxlo.ai as a dataset for fine-tuning an embedding model. If you want to generate even larger volumes, switch to DeepSeek V3.2 on the free tier to prototype, then move to Llama 3.3 70B for production quality.
Top comments (0)