Constrain the call, don't filter the output
Tags: prompting, guardrails, cost
When the same kind of bad output keeps coming back, the usual response is a check afterwards: detect it, throw it away, try again. That treats a known, recurring failure as though it were a surprise β and pays full price for every instance of it.
A filter is a toll on failures you already know about
Follow one bad output through a filter-and-retry design. It is generated, which costs money and time. It is inspected, which costs more. It is discarded. The call is made again, at full cost, with the same inputs β which means there is a good chance it fails the same way.
For a genuinely novel failure, that is a reasonable price. You could not have predicted it, so catching it downstream is the only option.
For a failure you have seen a hundred times, it is a strange arrangement. You know what goes wrong, you know how often, and you are choosing to generate it and then pay to notice.
Filters leak, and the leak is confident
There is a second cost, and it is worse than the first.
No filter catches everything. The bad output that slips through is not marked as having slipped through β it arrives in the interface looking exactly like every output that passed. The filter's existence makes this worse rather than better, because everyone downstream assumes checked output is good output.
Constraining the call does not eliminate that risk either. But it lowers the rate at which bad outputs are produced, which lowers the rate at which they leak β and it does it without a per-failure cost.
Put known failures upstream
The mechanisms depend on the kind of generation:
- Exclusions. Negative prompts, explicit "do not" clauses β for failures that are a recognisable thing the model can be steered away from.
- Structure. A schema or a constrained output format, so malformed output is not a possibility rather than something to detect.
- Scope. A narrower set of tools for an agent, so the action you would have had to catch cannot be taken.
- Decomposition. When a single call is fighting too many requirements at once, splitting it into steps with one job each often removes the failure entirely.
Keep a check downstream as well β for the failures you did not predict. The distinction that matters is not filter or no filter; it is whether each failure you handle is known and recurring, which belongs upstream, or novel, which is what a downstream check is genuinely for.
Grounded in
Pathfinder is a character builder whose portraits restyle every time you change equipment, and image models fail at portraits in a small number of very predictable ways.
So the generation carried targeted negative prompts aimed at exactly those: no extra fingers, no melted weapons, no armour when a slot is unequipped. None of those is a surprise worth detecting after rendering. They are the known, recurring failures of the medium β so they were excluded in the request rather than generated, caught, and re-rolled at full cost each time.
Anti-patterns
- Filter-and-retry for a failure you see every day. Paying full price to produce, detect and discard something predictable.
- Retrying with identical inputs. A known failure mode will often simply recur.
- Treating "passed the filter" as "good". Leaked failures arrive looking identical to real passes.
- Constraints so broad they break the good case. Over-constraining a call to avoid one failure can suppress the output that was working.
- No downstream check at all. Constraining the call handles the predictable; something still has to catch the rest.
The smallest version worth building
Pull the last hundred outputs your filter rejected. Group them by reason. Every group that accounts for more than a few percent is a known failure β move each of those into the request as a constraint, and leave the filter for the long tail.
The retry rate usually falls sharply, and the cost falls with it.
Related patterns
- Design the failure state first β knowing your failure modes is the precondition for constraining against any of them.
- Decide what counts as the same request β retries are requests too, and whether they hit a cache or regenerate is its own decision.
- Regeneration is an edit, not a retry β when a user asks for another attempt, it is a new artifact rather than a correction of the failed one.