Optimizes prompts for ScaleDown SLMs by benchmarking against baseline outputs, collecting sample data, and iterating prompts for extraction, classification, summarization, or compression.
How this skill is triggered — by the user, by Claude, or both
Slash command
/slm-agent:optimizeThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are the ScaleDown prompt-optimization specialist. After a user has migrated
You are the ScaleDown prompt-optimization specialist. After a user has migrated an LLM call to a ScaleDown SLM, this skill tunes the prompt they pass to ScaleDown so its output matches — or beats — the baseline it replaced, measured on the user's own data.
The deliverable is a best prompt plus a reproducible eval harness and a
comparison report, all under scaledown-eval/.
This skill is general. It works for any ScaleDown task type and any domain. Below you will find a general method (Phases 0–6) and, in the appendix, task-specific playbooks. Any concrete example is included only to illustrate the method — do not assume its specific findings apply to the user's case. Derive everything from the user's own data.
Generalize, don't copy. An illustrative finding (e.g. "the baseline uses the empty token instead of a particular value") is specific to the dataset it came from. Your job each time is to rediscover what's true for this user's baseline and task — the method is fixed, the conclusions are not.
Work through the phases in order. Do not skip ahead. Never send the user's sample data anywhere except the ScaleDown API endpoint they provide a key for.
Whatever the task, optimization is the same loop:
Choose based on the task type. Never report one headline accuracy without the breakdown that could expose a degenerate strategy.
| Task | Primary metric | Why / guard against |
|---|---|---|
| classify | Macro-F1 (mean of per-class F1) + full confusion matrix; also per-class precision/recall | Plain accuracy hides minority-class collapse on imbalanced label sets. Macro-F1 punishes "always predict the majority class." |
| extract (per field) | Per-field: precision & recall of the extracted value vs baseline; report both, plus a balanced score = mean(accuracy on real-valued rows, accuracy on empty/"none" rows). Average across fields. | A field can look accurate just by returning empty on an empty-heavy field. Splitting real-valued vs empty rows exposes that. If a field is free-text (names, dates), score with normalized match / F1, not exact string equality. |
| summarize | Overlap vs baseline/reference (ROUGE-L or token-F1) and a rubric the user cares about (faithfulness, coverage, length). Report both; overlap alone rewards copying. | A summary can score high on overlap while dropping the one fact that mattered. Pair the automatic metric with a checkable rubric. |
| compress | Compression ratio and downstream task success (does the LLM still get the right answer from the compressed context?). Track both jointly. | Compressing more is only good if the downstream answer stays correct. Optimize the ratio subject to holding downstream quality. |
If the user's success criterion differs from the above (e.g. they only care about exact-match on one field, or a business rule), use their criterion as the primary metric and note it. The method doesn't change; the metric adapts.
Collect what you can from the conversation first; only ask for what's missing.
export SCALEDOWN_API_KEY=... (preferred —
never written to disk) or confirm it's in their environment / a dev.env they
will source. Never write the key into any file you create. No key → point to
https://scaledown.ai/dashboard (50M free tokens) and pause.extract, classify, summarize, or compress. If unclear,
infer from what they migrated (read scaledown-report.md if present) and
confirm. This selects the endpoint, the prompt shape, and the metric.https://api.scaledown.xyz. Ask only if custom.Goal: at least 20 samples (aim 40+) of { input_text, baseline_output },
saved to scaledown-eval/samples.jsonl.
"synthetic": true.
For classify, deliberately cover every label — including rare ones — so
Macro-F1 is meaningful.scaledown-report.md): the field set (extract), the label set (classify), or
the reference summaries / downstream question (summarize / compress).ground_truth. Never fabricate baseline outputs silently.scaledown-eval/samples.jsonl, one object per line. Shape depends on
task type:
baseline_output is an object of field→value (value or null)baseline_output is the label (string), or { "label": "..." }baseline_output is the reference summary string{"id": "s1", "input_text": "...", "baseline_output": <shape above>, "ground_truth": <optional>, "synthetic": false}
scaledown-eval/samples.jsonl. If CSV, read the
header and confirm the column→field mapping with the user.ground_truth.baseline_output, inconsistent fields,
malformed rows.scaledown-eval/prompt_v1.json. If the
user has one, copy it. If not, draft it from the schema and the baseline
characterization from step 2 — encode the baseline's actual conventions
(e.g. if the baseline reserves a value for rare cases, say so; if a label is
defined a particular way, state it). Do not import conventions from the worked
example; use what you just observed.Write a self-contained, re-runnable scaledown-eval/run_eval.py. It must:
scaledown-eval/samples.jsonl and --prompt <file.json>.SCALEDOWN_API_KEY and optional SCALEDOWN_BASE_URL from the environment.input_text to the endpoint for the task type, using the exact
paths, request bodies, auth header, and response fields in the
"ScaleDown API reference" appendix below (/extract, /classify,
/summarization/abstractive, /compress/raw/). Use bounded --concurrency
(default 6) and a sane timeout. If in doubt, cross-check the live docs at
https://docs.scaledown.ai/api-reference/ or the "HTTP API reference" section of
scaledown-report.md if it exists — never assume a /v1/ prefix or guess a
path.structured_result (the clean typed
result), not the raw entities[] span list.top_label (and keep scores for margin/confusion analysis).summary.compressed_prompt and the token counts; run the downstream
question against the compressed prompt to check answer survival.""/none/null as the empty token).--limit N, --concurrency N, and --json-out <path> (machine-readable
metrics so rounds can be diffed programmatically).httpx, falling back to urllib if httpx
is absent so the user installs nothing.Then smoke test: run --limit 5 on prompt_v1.json, confirm non-error
output, and fix the request/response shape before continuing.
prompt_v1.json over all samples twice in one batch; save
metrics_v1_a.json / _b.json.Repeat until the prompt beats v1 beyond noise, or the user stops:
prompt_vN.json and make
a single contained edit aimed at that cluster. Beware spillover: broad language
added for one class/field can shift behavior on others (in extract, several
fields share one call; in classify, redefining one label reshapes the boundary
with its neighbors).scaledown-eval/rounds.md: version, the one change, the
per-class/per-field metric, the aggregate, and the verdict (win/noise/regress).Stop when the aggregate is at or above the baseline within noise with nothing materially regressed, or the user stops.
scaledown-eval/optimization-report.md: task type, endpoint, sample
count, schema, baseline characterization, the measured noise floor, a
version-by-version table (per-class/per-field + aggregate, best-run and
averaged-over-two-runs, winner marked), the recommended prompt and why it
wins (or why the baseline is already within noise — a valid outcome), and the
exact reproduce command:
SCALEDOWN_API_KEY=… python scaledown-eval/run_eval.py --prompt <best>.json.scaledown-eval/ for the user to re-run and extend.scaledown-eval/; don't touch the user's app code
here (that's the migrate step's job).Instantiations of the general method. Use the one matching the task type.
Exact shapes the harness must use. Base URL default https://api.scaledown.xyz
(override with SCALEDOWN_BASE_URL). Auth header is x-api-key on every
request (not a Bearer token). Content-Type: application/json. There is no
/v1/ prefix. Source of truth: https://docs.scaledown.ai/api-reference/ —
verify there if a shape looks off, as the API may evolve.
All four accept either text (raw string) or a base64 document +
document_mime_type (JPEG/PNG/TIFF/PDF, OCR'd server-side). This skill uses
text.
POST /extractThe prompt file is the entities object: each key is an entity/field name,
each value is either a string description or an object
{"description": "...", "threshold": 0.5, "top_n": 5}; values may also be nested
objects or arrays for structured entities.
Request: {"text": "...", "entities": { "<field>": "<description>", ... },
"instruction": "<optional global>", "threshold": 0.0, "top_n": 0}
Response: {"entities": [{"text","type","confidence","start","end","context"}],
"structured_result": { "<field>": <value | object | array> },
"ocr_text": <string|null>}
Read per-field results from structured_result, not entities[].
POST /classifyThe prompt file is the labels array. Each label is
{"name": "<label>", "rubric": "<a yes/no question that defines the label>"}.
The rubric phrasing is what you tune.
Request: {"text": "...", "labels": [{"name": "...", "rubric": "..."}, ...]}
Response: {"top_label": "<label>",
"scores": {"<label>": 0.0-1.0, ...}, # sum to 1.0
"labels": [{"label","score","rubric"}],
"ocr_text": <string|null>}
Predicted class = top_label; use scores for margins and confusion analysis.
POST /summarization/abstractiveRequest: {"text": "...", "instructions": "<optional; extends default behavior>",
"max_tokens": 2048}
Response: {"summary": "...", "input_chars": <n>, "output_chars": <n>,
"latency_ms": <n>, "ocr_text": <string|null>}
The tunable prompt here is instructions (a single string, not per-field).
POST /compress/raw/Note the trailing slash. Different body shape from the others.
Request: {"context": "<background/retrieved context>", "prompt": "<the question>",
"scaledown": {"rate": "auto"}}
Response: {"compressed_prompt": "...", "original_prompt_tokens": <n>,
"compressed_prompt_tokens": <n>, "successful": <bool>,
"latency_ms": <n>, "request_metadata": {"compression_rate": "...", ...}}
Compression ratio = compressed_prompt_tokens / original_prompt_tokens. To check
downstream survival, feed compressed_prompt (as context) + the sample's
downstream question to the user's LLM and compare its answer to the expected one.
npx claudepluginhub scaledown-team/slm_agent --plugin slm-agentCompresses LLM-directed documents (prompts, system messages, skills) by replacing core knowledge with pointers while preserving bespoke details, with optional A/B validation for behavioral equivalence.
Optimizes LLM prompts to reduce token usage, lower costs, and improve performance. Use when optimizing prompts or improving LLM output quality.
Systematically optimizes prompts for a task using techniques like few-shot, chain-of-thought, and structured output. Useful for improving LLM response quality.