DEV Community

shashank ms
shashank ms

Posted on

Using LLMs for Data Augmentation: Best Practices and Techniques

We are going to build a synthetic data generator that expands a small set of labeled customer support tickets into a full training dataset for intent classification. This is useful when you need to bootstrap a classifier but only have a handful of trusted examples per label.

What you'll need

Oxlo.ai uses flat per-request pricing, so generating long synthetic examples costs the same regardless of prompt length. See https://oxlo.ai/pricing for details.

1. Configure the client and seed data

I start by pointing the OpenAI SDK at Oxlo.ai. I also define a small seed dataset with three intents: Refund Request, Account Access, and Technical Bug. These are the only real examples the generator will see.

import os
from openai import OpenAI

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

SEEDS = {
    "Refund Request": [
        "I was charged twice for my subscription last month. Can I get a refund?",
        "My order arrived damaged. I need my money back.",
    ],
    "Account Access": [
        "I forgot my password and the reset email is not arriving.",
        "My account got locked after too many failed login attempts.",
    ],
    "Technical Bug": [
        "The export button crashes the app every time I click it.",
        "Images are not loading on the dashboard after the latest update.",
    ],
}

2. Design the augmentation prompt

The system prompt does the heavy lifting. It tells the model to act as a data augmentation assistant, to vary sentence structure and vocabulary, and to return only a JSON object with a generated_examples array.

SYSTEM_PROMPT = """You are a data augmentation assistant. Your job is to generate synthetic training examples for a text classifier.

Rules:
- Read the provided label and seed examples.
- Generate new examples that belong to the same intent but use different wording, length, and style.
- Do not copy phrases directly from the seeds.
- Vary user tone: polite, frustrated, urgent, casual.
- Return ONLY a JSON object in this format: {"generated_examples": ["example 1", "example 2", ...]}.
- Generate exactly the requested number of examples.
"""

3. Generate examples for one intent

I write a helper that builds the user message from the label and seeds, then calls Llama 3.3 70B through Oxlo.ai. I use JSON mode so parsing is trivial.

import json

def generate_examples(intent: str, seeds: list[str], n: int) -> list[str]:
    user_msg = (
        f"Intent: {intent}\n"
        f"Seed examples:\n" + "\n".join(f"- {s}" for s in seeds) + "\n\n"
        f"Generate {n} new synthetic examples."
    )

    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_msg},
        ],
        response_format={"type": "json_object"},
        temperature=0.9,
    )

    raw = response.choices[0].message.content
    data = json.loads(raw)
    return data.get("generated_examples", [])

4. Scale to all labels

Now I loop over every intent, request twenty synthetic examples for each, and collect them into a list of records. Because Oxlo.ai has no cold starts on popular models, the loop runs without delays between requests.

import time

def augment_dataset(seeds: dict[str, list[str]], per_label: int = 20):
    records = []
    for intent, examples in seeds.items():
        print(f"Generating for {intent} ...")
        batch = generate_examples(intent, examples, per_label)
        for text in batch:
            records.append({"text": text.strip(), "label": intent})
        time.sleep(0.5)  # polite rate limit padding
    return records

augmented = augment_dataset(SEEDS, per_label=20)
print(f"Generated {len(augmented)} total examples.")

5. Validate and deduplicate

Synthetic data can drift. I run a quick validation pass to remove exact duplicates and any output that is suspiciously short or that exactly matches a seed.

def clean_records(records: list[dict], seeds: dict[str, list[str]]):
    seed_set = {s.lower().strip() for vals in seeds.values() for s in vals}
    seen = set()
    cleaned = []

    for r in records:
        text = r["text"]
        key = text.lower().strip()
        if len(text) < 10 or key in seed_set or key in seen:
            continue
        seen.add(key)
        cleaned.append(r)

    return cleaned

final_data = clean_records(augmented, SEEDS)
print(f"After cleaning: {len(final_data)} examples.")

6. Export the dataset

I write the final set to a JSONL file that any training framework can ingest. Each line is a standalone JSON object with text and label fields.

import json

with open("augmented_intents.jsonl", "w", encoding="utf-8") as f:
    for r in final_data:
        f.write(json.dumps(r, ensure_ascii=False) + "\n")

# Preview
for r in final_data[:4]:
    print(f"[{r['label']}] {r['text']}")

Run it

Putting the pieces together, the script generates, cleans, and exports the data in one pass. Here is what the output looks like when I run it against Oxlo.ai.

if __name__ == "__main__":
    augmented = augment_dataset(SEEDS, per_label=20)
    final_data = clean_records(augmented, SEEDS)

    with open("augmented_intents.jsonl", "w", encoding="utf-8") as f:
        for r in final_data:
            f.write(json.dumps(r, ensure_ascii=False) + "\n")

    print("\nSample output:")
    for r in final_data[:6]:
        print(f"{r['label']}: {r['text']}")

Example output:

Generating for Refund Request ...
Generating for Account Access ...
Generating for Technical Bug ...
After cleaning: 58 examples.

Sample output:
Refund Request: I need my subscription fee returned. I was billed twice in January and this is unacceptable.
Refund Request: The product I received is completely broken. Please process a full reimbursement immediately.
Account Access: I cannot log in and the password reset link is going to spam.
Account Access: My profile is locked. I tried signing in too many times and now I am blocked.
Technical Bug: Clicking the export button causes the application to freeze and close.
Technical Bug: Since the last release, thumbnails on the main dashboard fail to appear.

Next steps

Train a small classifier such as logistic regression or a lightweight transformer on the augmented JSONL file, and compare its F1 score against a model trained only on the original six seeds. If you need higher quality or multilingual coverage, swap the model ID to qwen-3-32b or kimi-k2.6 in the Oxlo.ai client and rerun the pipeline.

Top comments (0)