LLM-Assisted Dataset Curation
SkillDatabases & dataUse locally-hosted LLMs (vLLM/SGLang) for dataset filtering, quality scoring, rewriting, labeling, and synthetic data generation. Covers LLM-as-judge scoring, structured output filtering, batch inference pipelines, and 2025-2026 techniques (DataRater, perplexity filtering, curriculum scoring, LLM-based dedup).
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the LLM-Assisted Dataset Curation skill
What this skill tells your AI
The instructions your AI receives, as published by mkurman/zorai in skills/llm-assisted-curation/SKILL.md and read by ahel’s review.
Overview
Modern dataset curation uses LLMs as quality filters, rewriters, labelers, and synthetic data generators. This skill covers hosting models locally with vLLM/SGLang and using them for dataset work — not for interactive chat, but for batch, structured, reproducible data operations.
When to Use
Use this skill when:
- Scoring or filtering dataset examples with an LLM quality judge.
- Rewriting noisy text (queries, answers, reasoning traces) in bulk.
- Generating synthetic examples to balance classes or fill gaps.
- Extracting structured labels from unstructured text.
- Running curriculum scoring (difficulty, complexity, educational value).
- Implementing DataRater-style learned quality scoring (2025).
Do not use for:
- Interactive chat or single-example inspection — use a UI.
- Exact-match deduplication — use hashing.
- Embedding-based dedup — use
embedding-analysisskill.
Prerequisites
Requires a running vLLM or SGLang server. See vllm and sglang skills for server setup.
# vLLM (high throughput)
vllm serve Qwen/Qwen2.5-7B-Instruct --port 8000 --max-model-len 8192
# SGLang (structured output)
python -m sglang.launch_server --model-path Qwen/Qwen2.5-7B-Instruct --port 30000
Core Patterns
1. LLM-as-Judge Quality Scoring
Score each example on clarity, correctness, and usefulness.
from openai import OpenAI
import json
from datasets import load_dataset
client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
QUALITY_PROMPT = """Score the following example on these dimensions (1-5 each):
- clarity: Is the text well-written and understandable?
- correctness: Are the facts accurate?
- usefulness: Would this help someone learn or solve a problem?
Respond with ONLY valid JSON: {"clarity": N, "correctness": N, "usefulness": N}
Example:
{sample}
"""
def score_example(sample: dict) -> dict:
prompt = QUALITY_PROMPT.format(sample=json.dumps(sample))
response = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": prompt}],
temperature=0.0, # deterministic
max_tokens=128,
)
try:
scores = json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
scores = {"clarity": 0, "correctness": 0, "usefulness": 0}
return {**sample, **scores}
# Batch scoring with datasets
dataset = load_dataset("my-dataset", split="train")
scored = dataset.map(score_example)
# Filter low-quality examples
filtered = scored.filter(lambda x: x["clarity"] >= 3 and x["correctness"] >= 3)
2. Structured Output Filtering (SGLang)
Use SGLang's constrained decoding for guaranteed JSON schema output.
import sglang as sgl
@sgl.function
def classify_quality(s, text: str):
s += sgl.system("You classify dataset examples. Output ONLY valid JSON.")
s += sgl.user(f"Classify this example:\n\n{text}")
s += sgl.gen("result", max_tokens=256, temperature=0.0, schema=json.dumps({
"type": "object",
"properties": {
"quality": {"type": "string", "enum": ["high", "medium", "low", "noise"]},
"language": {"type": "string", "enum": ["en", "code", "other"]},
"topic": {"type": "string"},
"issues": {"type": "array", "items": {"type": "string"}},
},
"required": ["quality", "language", "topic", "issues"],
}))
state = classify_quality.run(text=example["text"])
result = state["result"] # guaranteed valid JSON
3. Batch Rewriting/Refinement
Clean noisy data by rewriting through an LLM.
REWRITE_PROMPT = """Rewrite the following text to be clear, grammatical, and well-structured.
Preserve all factual information. Fix typos, grammar, and awkward phrasing.
Original: {text}
Rewritten:"""
def rewrite_text(sample: dict, client, model: str) -> dict:
prompt = REWRITE_PROMPT.format(text=sample["text"])
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=1024,
)
sample["text_rewritten"] = response.choices[0].message.content
return sample
# Process with concurrency
from concurrent.futures import ThreadPoolExecutor, as_completed
def batch_rewrite(dataset, client, model, max_workers=8):
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(rewrite_text, example, client, model): i
for i, example in enumerate(dataset)
}
results = [None] * len(dataset)
for future in as_completed(futures):
idx = futures[future]
results[idx] = future.result()
return results
4. Synthetic Data Generation
Generate additional examples to fill class imbalances or cover edge cases.
SYNTHETIC_PROMPT = """Given this REAL example, generate {n} NEW examples that are:
- Semantically different (new variations, not paraphrases)
- Same difficulty level
- Same format and style
- Realistic and useful
REAL example:
{seed}
Generate {n} new examples as a JSON array of objects with the same keys.
Output ONLY the JSON array."""
def generate_synthetic(seed_examples, client, model, n_per_seed=5):
synthetic = []
for seed in seed_examples:
prompt = SYNTHETIC_PROMPT.format(n=n_per_seed, seed=json.dumps(seed))
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.8, # higher for diversity
max_tokens=2048,
)
try:
generated = json.loads(response.choices[0].message.content)
synthetic.extend(generated)
except json.JSONDecodeError:
continue
return synthetic
5. Curriculum Difficulty Scoring
Score examples by difficulty to enable curriculum learning.
DIFFICULTY_PROMPT = """Rate the difficulty of this example on a scale of 1-5:
1 = Trivial, basic knowledge
2 = Easy, common knowledge
3 = Moderate, requires some reasoning
4 = Hard, requires deep understanding
5 = Expert, requires specialized knowledge
Example: {sample}
Difficulty (number only):"""
def score_difficulty(sample, client, model):
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": DIFFICULTY_PROMPT.format(sample=sample["text"])}],
temperature=0.0,
max_tokens=4,
)
try:
return int(response.choices[0].message.content.strip())
except ValueError:
return 3 # default moderate
# Build curriculum: sort by difficulty
scored = dataset.map(lambda x: {"difficulty": score_difficulty(x, client, model)})
curriculum = scored.sort("difficulty")
6. LLM-Based Label Extraction
Extract structured labels from unstructured text.
LABELING_PROMPT = """Extract the following labels from this text.
Respond with ONLY valid JSON.
Text: {text}
Labels to extract:
- sentiment: "positive", "negative", or "neutral"
- has_code: true if contains code snippets, false otherwise
- domain: one of ["science", "technology", "business", "arts", "other"]
- entities: list of named entities mentioned
"""
def extract_labels(sample, client, model):
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": LABELING_PROMPT.format(text=sample["text"])}],
temperature=0.0,
max_tokens=256,
)
try:
labels = json.loads(response.choices[0].message.content)
return {**sample, **labels}
except json.JSONDecodeError:
return {**sample, "sentiment": None, "has_code": None, "domain": None, "entities": []}
Optimization Patterns
Openai Batch API (vLLM)
# vLLM supports batch API for cost efficiency on large jobs
# Upload a JSONL file of requests
requests = []
for example in dataset:
requests.append({
"custom_id": str(example["id"]),
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [{"role": "user", "content": QUALITY_PROMPT.format(sample=example["text"])}],
"temperature": 0.0,
"max_tokens": 128,
}
})
import tempfile, json
with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f:
for req in requests:
f.write(json.dumps(req) + "\n")
batch_file = f.name
batch = client.files.create(file=open(batch_file, "rb"), purpose="batch")
job = client.batches.create(input_file_id=batch.id, endpoint="/v1/chat/completions", completion_window="24h")
2025-2026 Literature Integration
This skill integrates techniques from:
| Paper | Venue | Technique | How Applied |
||--------|--------|-------|
| DataRater (Calian et al.) | NeurIPS 2025 | Meta-learned quality scoring | embedding_quality_score() in embedding-analysis; LLM judge as proxy |
| Why Less is More (Dohmatob et al.) | 2025 | Theory of data curation thresholds | Informs filtering aggressiveness |
| GRAPE Score | 2025 | Perplexity-based filtering | grape_score() in embedding-analysis |
| NeMo Curator SemDedup | 2024-2025 | Clustering-based semantic dedup | semantic_dedup() in embedding-analysis |
| LSHBloom (Khan et al.) | 2025 | Internet-scale text dedup | lsh_semantic_dedup() for >100M scale |
| Blu-WERP (Rupesh et al.) | 2025 | Scalable preprocessing pipeline | Streaming + batched map pattern |
| TBDFiltering (Busa-Fekete et al.) | 2025 | Tree-based data filtering | LLM scoring as tree node condition |
| Ensembled Multimodal Curation (Xu et al.) | 2025 | Multi-signal quality fusion | Combine LLM scores + embedding scores + perplexity |
Quality Gate
An LLM-assisted curation run is complete when:
- The LLM server (vLLM/SGLang) is healthy and reachable.
- Scoring prompts are versioned and produce structured, parseable output.
- Filtered examples are saved with their scores for auditability.
- Synthetic data is flagged with a
synthetic: truefield. - Batch results are reproducible (temperature=0 for scoring, fixed seed for generation).
- A before/after dataset card documents what was filtered and why.
Signals
- GitHub stars
- 324
- Forks
- 26
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
llm-assisted-curation- Source
- github.com/mkurman/zorai