I built a synthetic dataset generator that uses a large teacher model to write chain-of-thought reasoning traces, then validates them with a smaller student model to produce clean training pairs for fine-tuning. It is useful for ML engineers who need domain-specific reasoning data without paying token-based costs that scale with prompt length.
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
Step 1: Configure the Oxlo.ai client
I use the OpenAI SDK as a drop-in replacement pointed at Oxlo.ai. This keeps the code portable and lets me swap between models without touching client initialization.
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 teacher system prompt
The teacher model needs explicit instructions to emit structured chain-of-thought. I keep the system prompt in a constant so I can tune it without touching the rest of the pipeline.
TEACHER_SYSTEM_PROMPT = """You are a synthetic data generator. The user will provide a math or logic problem. Respond only with a JSON object containing:
- "problem": the original problem
- "chain_of_thought": a detailed step-by-step reasoning trace
- "answer": the final concise answer
Do not include markdown fences or commentary outside the JSON."""
Step 3: Generate reasoning traces with the teacher model
I use Kimi K2.6 as the teacher because its chain-of-thought reasoning is strong for structured problems. Because Oxlo.ai charges a flat rate per request, a long system prompt does not inflate the cost.
def generate_trace(problem: str) -> dict:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": TEACHER_SYSTEM_PROMPT},
{"role": "user", "content": problem},
],
)
raw = response.choices[0].message.content
return json.loads(raw)
problem = "A train travels 120 km in 2 hours. How far will it travel in 5 hours at the same speed?"
trace = generate_trace(problem)
print(json.dumps(trace, indent=2))
Step 4: Validate traces with the student model
To simulate transfer learning, I run each trace past a smaller, efficient model. Qwen 3 32B checks whether the reasoning is coherent and the answer is correct. If the student disagrees, we drop the sample.
VALIDATOR_SYSTEM_PROMPT = """You are a grader. Given a problem, a chain-of-thought reasoning trace, and an answer, respond with only VALID or INVALID. A response is VALID only if every reasoning step is correct and the final answer is accurate. Respond with one word only."""
def validate_trace(trace: dict) -> bool:
payload = json.dumps(trace)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": VALIDATOR_SYSTEM_PROMPT},
{"role": "user", "content": payload},
],
)
verdict = response.choices[0].message.content.strip().upper()
return verdict == "VALID"
is_good = validate_trace(trace)
print("Accepted:", is_good)
Step 5: Batch process and export to JSONL
I wrap the pipeline in a loop over a seed list of problems. Each accepted example is written as one JSON line, ready for fine-tuning frameworks like Axolotl or LLaMA-Factory.
problems = [
"A train travels 120 km in 2 hours. How far will it travel in 5 hours at the same speed?",
"If 3 workers can build a wall in 6 days, how long will it take 9 workers?",
"What is the sum of the first 10 positive integers?",
]
dataset = []
for p in problems:
try:
t = generate_trace(p)
if validate_trace(t):
dataset.append(t)
except Exception as e:
print(f"Failed on problem: {p}, error: {e}")
with open("cot_dataset.jsonl", "w") as f:
for item in dataset:
f.write(json.dumps(item) + "\n")
print(f"Wrote {len(dataset)} examples to cot_dataset.jsonl")
Run it
Test the full pipeline end to end with a single problem before burning through a batch.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
TEACHER_SYSTEM_PROMPT = """You are a synthetic data generator. The user will provide a math or logic problem. Respond only with a JSON object containing:
- "problem": the original problem
- "chain_of_thought": a detailed step-by-step reasoning trace
- "answer": the final concise answer
Do not include markdown fences or commentary outside the JSON."""
VALIDATOR_SYSTEM_PROMPT = """You are a grader. Given a problem, a chain-of-thought reasoning trace, and an answer, respond with only VALID or INVALID. A response is VALID only if every reasoning step is correct and the final answer is accurate. Respond with one word only."""
def generate_trace(problem: str) -> dict:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": TEACHER_SYSTEM_PROMPT},
{"role": "user", "content": problem},
],
)
return json.loads(response.choices[0].message.content)
def validate_trace(trace: dict) -> bool:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": VALIDATOR_SYSTEM_PROMPT},
{"role": "user", "content": json.dumps(trace)},
],
)
return response.choices[0].message.content.strip().upper() == "VALID"
problem = "A car rental costs $50 per day plus $0.20 per mile. How much for 3 days and 200 miles?"
trace = generate_trace(problem)
print("Generated trace:")
print(json.dumps(trace, indent=2))
print("Validation:", validate_trace(trace))
Example output:
Generated trace:
{
"problem": "A car rental costs $50 per day plus $0.20 per mile. How much for 3 days and 200 miles?",
"chain_of_thought": "First, calculate the daily cost: 3 days * $50/day = $150. Next, calculate the mileage cost: 200 miles * $0.20/mile = $40. Finally, add them: $150 + $40 = $190.",
"answer": "$190"
}
Validation: VALID
Wrap-up
Swap the teacher model to DeepSeek V3.2 to keep generation costs on the free tier while you prototype. Once your dataset is clean, feed it into a fine-tuning pipeline for Llama 3.3 70B or Qwen 3 32B on Oxlo.ai to see if the smaller model learns the reasoning style through distillation.
Top comments (0)