Table of Contents
đź“– Article Overview While large language models (LLMs) running in the cloud handle general reasoning, running specialized agents on local edge devices requires optimizing Small Language Models (SLMs, e.g. Phi-3 or Llama-3-8B). To train an SLM to perform specific tool calls, developers rely on fine-tuning. However, fine-tuning accuracy is heavily dependent on data quality: "Garbage in, garbage out." If your synthetic dataset contains duplicate tasks or malformed JSON tool schemas, the model's accuracy will drop. In this article, we design a synthetic dataset curation pipeline and implement a trajectory filtering compiler in Python.
The Threat of Malformed Synthetic Data
In typical model fine-tuning preparation:
- Redundant Trajectories: Synthetic datasets generated by larger models (e.g [1]. GPT-4) contain hundreds of identical prompt patterns, causing model overfitting.
- Syntax Inconsistencies: Instruction sets often contain invalid JSON payloads or broken brackets that train the model to output corrupted tool parameters.
- The Solution: Automated Dataset Curation. We construct pipeline filters to validate JSON parsing parameters, de-duplicate instruction variations, and export outputs into standardized ChatML structures.
1. Validating Structured JSON Payloads
To protect training datasets:
- Strict Schema Verification: Parse every code example or tool call payload within the training set, throwing out entries that fail validation checks.
- Discard Incomplete Snippets: Trajectories cut off by generation limits (token limits) must be identified and removed.
2. Formatting into ChatML Schemas
The curation engine standardizes dataset formats:
- ChatML Structure: Wrap entries inside
<|im_start|>and<|im_end|>boundaries forsystem,user, andassistantroles. - Standardize System Prompts: Apply a consistent system prompt across all examples to align behavior.
Code Demo: Synthetic Dataset Curation Parser
Below is a Python implementation of an instruction dataset curator. It filters syntax errors, removes duplicate examples, and exports clean ChatML JSONL configurations.
import json
from typing import List, Dict, Any, Tuple
class DatasetCurator:
def __init__(self):
# Memory storage for unique prompt hashes to prevent duplication
self.seen_prompts = set()
def curate_trajectory(self, raw_entry: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
# 1. Verify required keys exist
if "prompt" not in raw_entry or "response" not in raw_entry:
return False, {"error": "Missing key variables."}
prompt = raw_entry["prompt"].strip()
response = raw_entry["response"].strip()
# 2. De-duplicate prompts
prompt_hash = hash(prompt)
if prompt_hash in self.seen_prompts:
return False, {"error": "Duplicate prompt pattern."}
# 3. If the response contains tool data, validate JSON syntax
if "tool_call" in raw_entry:
try:
json.loads(raw_entry["tool_call"])
except json.JSONDecodeError:
return False, {"error": "Invalid JSON tool call format."}
# 4. Success: Save hash and compile to ChatML format
self.seen_prompts.add(prompt_hash)
chatml_structure = {
"messages": [
{"role": "system", "content": "You are a helpful local automation agent."},
{"role": "user", "content": prompt},
{"role": "assistant", "content": response}
]
}
return True, chatml_structure
if __name__ == "__main__":
curator = DatasetCurator()
# Raw synthetic datasets containing duplicates, syntax issues, and valid logs
raw_dataset = [
{"prompt": "Get database stats", "response": "Fetching schema metrics", "tool_call": '{"action": "get_stats"}'},
# Duplicate prompt
{"prompt": "Get database stats", "response": "Fetching schema metrics", "tool_call": '{"action": "get_stats"}'},
# Invalid JSON tool call
{"prompt": "Run migration plan", "response": "Migrating database tables", "tool_call": '{"action": "migrate", }'}, # Trailing comma
# Clean example
{"prompt": "Clean sandbox directory", "response": "Removing temporary logs", "tool_call": '{"action": "clean_tmp"}'}
]
print("🌲 Curating Synthetic Instruction Dataset...")
print("---------------------------------------------")
cleaned_dataset: List[Dict[str, Any]] = []
for idx, entry in enumerate(raw_dataset):
success, result = curator.curate_trajectory(entry)
if success:
cleaned_dataset.append(result)
print(f"âś… [Row {idx + 1}] Cleaned and formatted successfully.")
else:
print(f"❌ [Row {idx + 1}] Discarded: {result['error']}")
print("\n--- Exported ChatML JSONL Sample ---")
for item in cleaned_dataset:
print(json.dumps(item))
Dataset Curation Takeaways
- Validate JSON Syntaxes: Enforce strict JSON parsing on all synthetic tool outputs before using them for training.
- Apply Prompt Hash Checks: Identify and delete duplicate questions to prevent model overfitting.
- Standardize Format Wrappers: Wrap training data inside ChatML tags to train models to output clear system, user, and assistant blocks. [2]
Antithesis & Production Trade-Offs: When NOT to Use This Architecture
Even a high-throughput Optimization, Fine-Tuning architecture introduces non-trivial operational complexity. Before adopting this pattern in production, evaluate three concrete engineering boundaries where a simpler alternative strictly dominates:
- Low-Throughput / Single-Node Workloads ($< 50 ext{ req/s}$):
If your service operates well below saturation on a single vertical instance and does not face strict multi-tenant tail-latency SLAs (
p99 < 50 ms), the bookkeeping overhead of two-tier state partitioning, canonical digest hashing, and sliding-window circuit breakers adds cognitive and operational cost without measurable user-facing gains. A single monolithic transaction boundary with standard database constraints is simpler to debug and operate. - Hard Real-Time Ultra-Low-Latency Paths ($< 50,\mu ext{s}$ Budget): When operating inside kernel-bypass networking, L1/L2 cache-pinned ring buffers, or per-packet hardware data planes, any dynamic heap allocation, cryptographic SHA-256 digest computation, or asynchronous context switch violates the nanosecond/microsecond budget. In those regimes, pre-compute static lookup tables at control-plane configuration time and keep the hot path strictly zero-allocation.
- Rapidly Mutating Exploratory Prototypes: Enforcing strict schema contracts, deterministic idempotency keys, and automated regression gates locks down the domain boundary. During week-one exploratory product discovery—where underlying data models change daily and zero real customer capital or tenant PII is at risk—defer heavy verification harnesses until the core workflow contract stabilizes.
References & Further Reading
- Hu, E. J., et al. (2021). LoRA: Low-Rank Adaptation of Large Language Models. ICLR. https://arxiv.org/abs/2106.09685 ↩︎
- Dettmers, T., et al. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. NeurIPS. https://arxiv.org/abs/2305.14314 ↩︎
- Lin, J., et al. (2024). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. MLSys. https://arxiv.org/abs/2306.00978 ↩︎

Discussion & Comments