Gradient-free instructional prompt search (GrIPS): a complete guide
You wrote a decent prompt. It works, mostly. But you suspect a better wording is sitting one edit away, and you have no gradient to follow and no spare LLM to act as an optimizer. GrIPS treats that hunt as a search problem: it chops your instruction into phrases, mechanically deletes, swaps, paraphrases, and re-adds them, and keeps whatever scores best on a small labeled set.
The flip is that no part of this needs to understand your prompt. No backprop, no model weights, no meta-prompting — just crude phrase surgery plus a scoring loop. Prasad et al. (EACL 2023) showed it lifts accuracy by 2–10 points across a dozen models, and with beam search it beat direct finetuning on GPT-2 XL (56.50% vs 55.88%).
See it work
Here's a sentiment task. The human-written instruction reads cleanly. The GrIPS-optimized one looks half-broken — yet it scores higher on the same model.
Original instruction (GPT-2 XL):
"Classify the sentiment of the following tweet as either 'positive' or
'negative'. Consider the overall tone and word choice. Output only the label."
→ balanced accuracy: 49.54%
GrIPS-optimized instruction:
"Classify the sentiment of the following tweet. Consider the overall tone
and word choice. Output only the label."
(the words "positive" and "negative" — the actual labels — got deleted)
→ balanced accuracy: 56.50% with beam search
That deletion of the label names is a real documented edit (Task 195, GPT-2 XL). A human would call the result worse. The model disagrees. That gap is the whole point of GrIPS: LLM behavior keys off surface features of a prompt that don't line up with human notions of clarity.
The mental model
Think of optimizing a prompt the way a locksmith files a blank key. There's no diagram of the lock's insides. You shave a little metal off, try the key, and keep the cut only if the lock turns a bit more. No theory of why — just shave, test, keep, repeat.
GrIPS is hill-climbing over the space of instructions: each edit is a step, the score set is the altimeter, and you only ever walk uphill.
The "state" is your current instruction. Its "neighbors" are every instruction one edit away. The objective is a score on a small evaluation set. You step to a better neighbor when you find one, and stop when you can't.
How it works
The loop runs in five moves:
-
Phrase segmentation. A CRF-based constituency parser turns the instruction into a tree, and disjoint phrase-level chunks (NP, VP, PP, S, and friends) become the atomic edit units. Phrase level is deliberate — word-level edits are too fine to move the needle, sentence-level edits are too destructive.
-
Candidate generation. Each iteration produces
m × lcandidates (mcandidates,lcomposed edits each). Every edit samples one of four operations uniformly:- Delete — remove all copies of a chosen phrase, and stash it for later re-addition.
- Swap — pick two phrases and exchange every occurrence of each (a bidirectional replacement).
- Paraphrase — replace a phrase with a PEGASUS-generated rewording. This is the only operation that introduces genuinely new text.
- Add — pull a previously deleted phrase from the pool and reinsert it at a random phrase boundary.
-
Scoring. Every candidate plus the current base is run on the score set and scored as
BalancedAccuracy + α × H, whereHis the entropy of the model's class predictions andα = 10. Balanced accuracy handles class imbalance; the entropy term is what stops the model from gaming the metric by predicting one label for everything (which alone can still hit 50%+ on binary tasks). -
Selection. Greedy search keeps the single best candidate if it beats the base. Beam search (B=k) keeps the top-B and expands all of them next round — broader, but pricier.
-
Termination. Stop at
niterations (default 10) or afterPconsecutive non-improving rounds (patience, default 2).
GrIPS never touches the model's internals. It changes the input text, watches the output, and treats the LLM as a pure black box — so the "intelligence" lives entirely in the search and scoring, not in the model's reasoning.
The canonical greedy loop is small enough to read in one sitting:
def grips_greedy(instruction, eval_set, score, max_iter=10, patience=2,
m=5, alpha=10):
best, best_score = instruction, score(instruction, eval_set, alpha)
phrases, deleted_pool, stale = parse(instruction), [], 0
for _ in range(max_iter):
candidates = [apply_random_edit(best, phrases, deleted_pool)
for _ in range(m)]
top, top_score = max(((c, score(c, eval_set, alpha)) for c in candidates),
key=lambda x: x[1])
if top_score > best_score:
best, best_score, phrases, stale = top, top_score, parse(top), 0
else:
stale += 1
if stale >= patience:
break
return best
Why it works
The paper ablates each factor; here's the rough ranking by impact.
| Factor | Weight | Evidence |
|---|---|---|
| Initial instruction quality | ~30% | Task-specific beats task-agnostic by 3–5 pts on InstructGPT |
| Score set size and quality | ~25% | Performance degrades sharply below 50 examples |
| Search strategy | ~20% | Beam beats greedy by ~2.8 pts on GPT-2 XL (5× the evals) |
| Entropy term in scoring | ~15% | Dropping it costs 1.48 pts (label collapse returns) |
| Edit-operation diversity | ~10% | Deletion is the most impactful; removing it costs 2.56 pts |
The causal story behind the gains: deletion strips redundant or confusing phrases (and simplifies the instruction-following burden), paraphrasing nudges wording toward the model's training distribution, and swapping moves key information to positions the model attends to more strongly. None of it requires the edits to stay coherent.
Where it shines
GrIPS was built and benchmarked on classification, where balanced accuracy plus entropy is a natural fit:
- Binary and multi-class text classification — sentiment, toxicity, answerability, appropriateness.
- Content moderation and policy-conformance checks.
- Factual verification and correctness checking.
- Topic categorization, routing, and intent detection.
It extends cleanly to any task with a clear metric — extraction (exact match / token F1), binary answerability QA, even summarization-prompt tuning scored by ROUGE. It is a poor fit for open-ended generation or anything judged purely subjectively, because the scoring function needs ground truth.
The headline numbers. GrIPS was evaluated on eight binary classification tasks from Natural Instructions v1: Task 019 (temporal reasoning), 021 (grammatical/logical correctness), 022 (inappropriate content), 050 (question answerability), 069 (story completion), 137 (toxicity comparison), 139 (topicality comparison), and 195 (tweet sentiment). Average instruction-only gains by model:
| Model | Parameters | Improvement |
|---|---|---|
| GPT-2 XL | 1.5B | +9.36 pts |
| GPT-J | 6B | +7.42 pts |
| GPT-NeoX | 20B | +7.10 pts |
| OPT 1.3B | 1.3B | +6.92 pts |
| OPT 2.7B | 2.7B | +6.41 pts |
| OPT 6.7B | 6.7B | +5.78 pts |
| OPT 30B | 30B | +5.35 pts |
| BLOOM 1B | 1B | +6.37 pts |
| BLOOM 3B | 3B | +5.96 pts |
| FLAN-T5 | 3B | +3.08 pts |
| InstructGPT Babbage | ~1.3B | +4.29 pts |
| InstructGPT Curie | ~6.7B | +2.36 pts |
The pattern is clean: smaller, less instruction-tuned models gain the most. GPT-2 XL (no instruction tuning) gained 9.36 points; InstructGPT Curie (RLHF-tuned) gained only 2.36. Models that already follow instructions well have less to fix.
It beat the gradient-based methods. On GPT-2 XL, GrIPS with beam search topped every parameter-efficient tuning method tested — including full finetuning.
| Method | Type | Accuracy |
|---|---|---|
| No optimization | Baseline | 49.54% |
| Prefix-tuning | Gradient-based | 53.29% |
| GrIPS (greedy) | Gradient-free | 53.68% |
| Adapter tuning | Gradient-based | 55.08% |
| Direct finetuning | Gradient-based | 55.88% |
| GrIPS (beam B=5) | Gradient-free | 56.50% |
It beat manual rewriting too. Human rewrites actually degraded GPT-2 XL (49.54% → 47.70%), while GrIPS improved it.
| Model | Manual rewrite | GrIPS (greedy) | Advantage |
|---|---|---|---|
| GPT-2 XL | 47.70% | 53.68% | +5.98 pts |
| InstructGPT Babbage | 55.50% | 57.79% | +2.29 pts |
| InstructGPT Curie | 57.87% | 59.37% | +1.50 pts |
For the InstructGPT models, optimizing the instruction via GrIPS also beat optimizing few-shot example selection under an equal compute budget (Babbage 57.79% vs 56.25%, Curie 59.37% vs 57.75%) — though for GPT-2 XL, example search won (56.00% vs 53.68%).
The real-world signal. A method that does nothing smarter than deleting and shuffling phrases beat adapter tuning, prefix-tuning, and full finetuning on GPT-2 XL — at roughly $20–$175 per run instead of GPU-hours of training. The lesson stuck: surface-level prompt search is a cheap, serious baseline before you reach for anything heavier.
When to use it (and when not)
Reach for GrIPS when:
- You have a working classification prompt you suspect could be better.
- You can't access model weights — API-only deployment.
- You don't want to depend on a second LLM to optimize.
- You have 20–100 labeled examples and a limited compute budget.
- You want a simple, interpretable, loggable optimization process.
Skip it when:
- The task is open-ended generation with no measurable quality.
- The fix requires new instruction content — GrIPS only edits what's already there.
- You need real-time adaptation (optimization is an offline, multi-round job).
- The model is a very large, well-tuned instruction-follower with low sensitivity.
- The starting instruction is fundamentally wrong or missing critical information.
Check fit before committing: run the first iteration without accepting any edit and look at the standard deviation of candidate scores. High variance predicts large gains. The paper found this sensitivity correlates strongly with realized improvement — Pearson's r = 0.94 (p<0.001) on GPT-2 XL, 0.75 on Babbage, 0.51 on Curie.
Cost. A full run across the eight tasks costs roughly $20–$175 per seed depending on the target model, and the authors' entire experiment suite came to about $2,400 — orders of magnitude under finetuning. Budget on evaluations: greedy needs m × |score_set| calls per iteration (e.g. 5 × 100 = 500), totaling ~2,000–5,000; beam search runs ~10,000–25,000. The parser and PEGASUS run locally on a single GPU at near-zero marginal cost.
Model fit. Best gains on base models (GPT-2 XL, OPT 1.3B–6.7B, BLOOM 1–3B). Good gains on GPT-J, GPT-NeoX, Babbage. Modest on Curie and FLAN-T5. The model just needs to follow natural-language instructions, be sensitive to wording, produce classifiable outputs, and fit instruction + input in context (~200 tokens). Embedding models, pure completion models that ignore framing, sub-128-token context windows, and tightly rate-limited APIs are out.
When to escalate:
| Condition | Go to | Why |
|---|---|---|
| Need maximum performance | ProTeGi/APO | Directed "textual gradient" edits, up to ~31% gains |
| Have a capable optimizer LLM | APE or OPRO | LLM-generated candidates explore more intelligently |
| Complex multi-stage pipelines | DSPy with MIPRO | Framework-level pipeline optimization |
| Prompting has plateaued | Fine-tuning | Weight updates capture what prompts can't |
| Want population-scale search | EvoPrompt | Evolutionary algorithms with larger populations |
Variant selection:
| Variant | Best for | Trade-off |
|---|---|---|
| Greedy (B=1) | Quick results, tight budget | May miss better solutions |
| Beam (B=5) | Maximum quality | ~5× cost |
| Instruction-only | Zero-shot optimization | Fewer variables |
| Instruction + examples | Few-shot (examples fixed) | GrIPS edits only the instruction |
| Composed edits (l>1) | Long/complex instructions | More aggressive per iteration |
Components and configuration
GrIPS needs five pieces plus one internal structure:
- Initial instruction — the starting point. For instruction-tuned models, task-specific beats task-agnostic by a wide margin (Babbage +3.38, Curie +3.41); for base models like GPT-2 XL it barely matters (-0.61).
- Constituency parser — CRF-based (e.g.
benepar_en3), produces the phrase units. - Paraphrase model — PEGASUS (
tuner007/pegasus_paraphrase), independent of the target LLM. - Score set — 20 examples minimum (degraded), 100 recommended, with class balance and ground-truth labels.
- Scoring function — balanced accuracy + α × entropy; both terms are load-bearing.
- Deleted-phrase pool (internal) — stores removed phrases so the add operation can reinsert them.
Default hyperparameters and sensible ranges:
| Parameter | Default | Range | Effect |
|---|---|---|---|
m (candidates) | 5 | 3–10 | More candidates = broader search, higher cost |
l (composition) | 1 | 1–3 | More composed edits = more aggressive |
n (max iterations) | 10 | 5–20 | Longer search |
P (patience) | 2 | 1–5 | Higher = less premature stopping |
α (entropy weight) | 10 | 5–20 | Higher = stronger diversity incentive |
| Score set size | 100 | 20–200 | Larger = more reliable scoring |
Beam width B | 1 or 5 | 1–10 | Wider = better results, higher cost |
Score set size has a measurable floor: on Babbage, 20 examples bought +1.00 pts, 50 bought +2.50, and 100 bought +4.27. For multi-class tasks, raise α to match the higher maximum entropy (ln k for k classes), use 150+ examples, and consider macro-F1 in place of balanced accuracy. For content moderation, push the score set to 200+ and include borderline adversarial examples.
The scoring function is the one piece worth seeing in code — the entropy term is what everyone forgets:
import numpy as np
from collections import Counter
def compute_score(instruction, eval_set, model_fn, alpha=10.0):
preds, labels = [], []
for ex in eval_set:
preds.append(model_fn(instruction + "\n\n" + ex["input"]).strip().lower())
labels.append(ex["label"].strip().lower())
# Balanced accuracy: mean per-class accuracy
per_class = []
for cls in set(labels):
idx = [i for i, l in enumerate(labels) if l == cls]
per_class.append(sum(preds[i] == labels[i] for i in idx) / len(idx))
balanced_acc = np.mean(per_class)
# Entropy of predictions — rewards diverse outputs, blocks label collapse
n = len(preds)
probs = [c / n for c in Counter(preds).values()]
entropy = -sum(p * np.log(p + 1e-10) for p in probs)
return balanced_acc + alpha * entropy
Implementation workflow
The end-to-end path, once dependencies are installed (torch, transformers, spacy, benepar, and the parser model):
- Prepare data. Gather 100+ labeled examples, balance the classes, split into a score set (100) and a held-out test set, and include edge cases.
- Write the initial instruction. Clear, task-specific, with the output format and label options stated explicitly. Keep it concise — GrIPS will trim the fat.
- Baseline. Run the original instruction on the test set; record balanced accuracy and entropy.
- Optimize. Start greedy (B=1) for a fast read; if it's promising, follow up with beam (B=5). Log every accepted edit and run several seeds.
- Validate. Evaluate on the held-out set, test for statistical significance, eyeball the optimized instruction, and check for degenerate single-class behavior.
- Decide. Deploy if the gain is significant. If the winning instruction is incoherent but performs, document that and deploy with monitoring and periodic re-evaluation for drift.
Connecting the target model is just a function that takes a prompt and returns a string — here against an inference API:
def make_evaluator(client, model="gpt-3.5-turbo"):
def evaluate(prompt: str) -> str:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0,
max_tokens=50,
)
return resp.choices[0].message.content.strip()
return evaluate
The authors' reference implementation runs the same loop from the CLI:
python run_grips.py --num-compose 1 --num-candidates 5 --num-iter 10 \
--patience 2 --scoring-function balanced_accuracy_entropy \
--alpha 10 --model babbage --task task019
Do: start task-specific (for tuned models), log the full edit trajectory, run multiple seeds and keep the best, use beam when budget allows, validate on held-out data, and watch the entropy component for collapse.
Don't: evaluate on the score set you optimized against, drop the entropy term, expect GrIPS to fix a fundamentally wrong instruction, use fewer than 20 examples, assume the result is human-readable, or run it on tasks without clear metrics.
Debugging
| Symptom | Likely cause → fix |
|---|---|
| No improvement over iterations | Model insensitive to edits (check first-iteration std dev) → switch model/technique; or score set too small → raise to 100+; or patience too low → 3–4; or too few candidates → 8–10 |
| Performance degrades mid-run | Over-deletion of a key phrase → protect it and restart; or score set unrepresentative → validate on held-out each iteration |
| Label collapse (all one class) | Missing/weak entropy term → ensure α>0, raise to 15–20; or imbalanced score set → rebalance |
| Optimized instruction is incoherent | Usually expected and fine if it scores well; if critical info is lost, reduce delete probability or protect key phrases |
| Inconsistent across seeds | Score set too small → enlarge; or high variance → run 5+ seeds; or switch to beam (less sensitive to random choices) |
The single most common mistake is testing final performance on the same score set used for optimization. The second is forgetting the entropy term and then wondering why the model predicts one class for everything.
Limitations
These are structural, not tuning problems:
- Can't generate new information. GrIPS only deletes, reorders, paraphrases, or reinserts existing phrases. Missing a critical constraint? It can't invent one.
- No semantic understanding. Edits are mechanical. That's how it finds wins humans miss — and also how it burns iterations on nonsense.
- Classification-shaped scoring. Adapting to generation tasks means hand-designing a scoring function, which reintroduces the engineering effort the technique was meant to remove.
- Diminishing returns on strong models. The models that benefit most are the ones least likely to be in production.
- Local search ceiling. Four phrase-level operations cover a small slice of instruction space; the global optimum may be unreachable from your starting point.
- PEGASUS dependency. Paraphrase quality drops on domain-specific or technical language, and the parser/paraphraser are English-centric — non-English prompts need different tooling, or restrict edits to delete and swap.
Watch edge cases: single-phrase instructions (only paraphrase does anything), instructions with code or JSON (the parser mangles them — protect formatted spans), embedded few-shot examples (separate them out before editing), conditional logic (don't let an edit split an if-then), and near-chance baselines (the entropy term can dominate, rewarding diverse-but-wrong predictions).
Coherence is not enforced — by design. GrIPS will happily delete a label definition or the entire definition of "toxicity" (documented on Task 137, Curie) if the score goes up. That's a feature for performance and a hazard for safety: an optimized instruction can drop a safety-relevant phrase or rely on model-specific quirks that break on the next model update. Include safety examples in the score set, protect critical phrases from deletion, and human-review before production.
Ethics and risk
GrIPS's results say something uncomfortable about LLMs: behavior is driven by the surface form of instructions, not their meaning, and incoherent prompts can outperform clean ones. That challenges the idea that these models "understand" instructions, and it makes optimized prompts hard to audit — if you can't explain why it works, can you trust it in high-stakes settings?
Practical risks worth managing: the optimizer amplifies any bias baked into the score set (the entropy term mitigates label imbalance but not systematic labeling bias), so audit across demographic subgroups. Opaque instructions complicate accountability, so log the full edit trajectory. And because GrIPS can only edit existing text, its potential for crafting adversarial prompts is real but bounded — milder than generation-based optimizers.
Ecosystem
GrIPS arrived in 2022 (arXiv March 2022, EACL 2023) and effectively kicked off automatic prompt optimization, drawing ~130 citations and directly inspiring a wave of successors. Where it sits:
| Aspect | GrIPS | APE | ProTeGi | OPRO | RLPrompt |
|---|---|---|---|---|---|
| Year / venue | EACL 2023 | ICLR 2023 | EMNLP 2023 | 2023 | EMNLP 2022 |
| Edit mechanism | Heuristic, 4 ops | LLM generation | LLM "gradients" | LLM as optimizer | RL policy |
| Needs optimizer LLM | No | Yes | Yes | Yes | No |
| Needs model weights | No | No | No | No | Yes |
| API compatible | Yes | Yes | Yes | Yes | No |
| Avg. improvement | 2–10 pts | 15–20% | 20–31% | 20–50% | Variable |
| API cost | Low ($20–175) | Low | Medium | High | N/A (compute) |
| External tools | Parser + PEGASUS | None | None | None | RL framework |
Choose GrIPS over the rest when you can't afford an optimizer LLM, when interpretability of the process matters, when you want a cheap baseline before investing in heavier methods, or when working with tiny models where LLM-based optimization costs more than it returns.
It composes, too. Run GrIPS first for fast, cheap exploration, then hand its output to ProTeGi or OPRO for directed refinement — the partially-optimized instruction gives those methods a head start. It also pairs with example selection (optimize instruction, then pick examples), with chain-of-thought (optimize the preamble, leave the reasoning scaffold fixed), and doubles as a standalone sensitivity analyzer.
Future directions the field is exploring: hybrid heuristic-plus-LLM scoring to cut evaluation cost, adaptive operation sampling (favor delete if delete keeps winning), multi-objective GrIPS (accuracy, brevity, coherence, safety at once), cross-lingual variants with language-specific tooling, and compositional optimization that tunes task description, format, and constraints as separate modules. The deepest open question is the one nobody has answered: why do incoherent instructions work?
Key sources: the GrIPS paper (Prasad, Hase, Zhou, Bansal; UNC Chapel Hill), the EACL 2023 version, the reference code, and successors APE, ProTeGi/APO, and OPRO.
Summary
- GrIPS optimizes prompts by mechanical phrase-level edits — delete, swap, paraphrase, add — scored by balanced accuracy plus entropy and selected via greedy or beam search. No gradients, no optimizer LLM, no model weights.
- It delivers consistent 2–10 point gains, and with beam search (B=5) it beat prefix-tuning, adapter tuning, and direct finetuning on GPT-2 XL (56.50% vs 55.88%).
- Smaller, less instruction-tuned models gain most (GPT-2 XL +9.36) while RLHF-tuned models gain least (Curie +2.36).
- Its signature finding: semantically broken instructions can outperform clean ones, because LLMs respond to surface form, not human-style meaning.
- Best for classification with clear metrics, 20–100 labeled examples, and API-only models; it costs roughly $20–$175 per run versus GPU-hours for finetuning.
- It can't invent missing content, struggles with generation tasks, and yields opaque instructions — so log the trajectory, validate on held-out data, and human-review before deploying.
- Historically it launched automatic prompt optimization, directly inspiring APE, ProTeGi, OPRO, and EvoPrompt; today it's best as a cheap first pass or as initialization for heavier optimizers.
Read Next
Start reading to get personalized recommendations
Explore Unread
Great job! You've read all available articles