The one-line version: a cheap model in a long agent loop rarely fails by crashing. It fails by degrading — a dropped instruction here, a slightly-wrong tool call there — until, thirty turns in, it hands back a confident wrong answer that parses cleanly and passes your check. The danger is the silence. Below are the four ways it happens and, for each, a cheap guard you can add today. None of them require a better model.
The temptation is real and the math is good: DeepSeek V4 Flash 0731 lists around $0.14/$0.28 per million tokens, GLM-5.2 is close behind, and both are genuinely capable on single-shot tasks (V4 Flash scores ~50 on the Artificial Analysis Intelligence Index). The mistake is assuming single-shot competence transfers to a 40-step loop. It doesn't — not because the model is dumb, but because loops compound small errors. Here's where they compound.
1. Instruction decay#
Your system prompt says "never call refund() without a confirmed order ID." That rule is crisp at turn 1. By turn 25, buried under 30,000 tokens of tool output, the model's attention on that constraint has thinned — and cheaper models thin faster. The output still looks fine. The refund still goes through. Nothing errored.
The guard: don't rely on a single up-front instruction to survive a long transcript. Re-assert the critical constraints in the step where they matter — right before the tool call that could do damage — and add a long-transcript golden test that deliberately runs past the point where decay shows up. If your eval suite only tests 5-turn tasks, it will never see this.
2. Tool-call drift#
Cheap models produce tool calls that are almost right: an extra sentence of prose before the JSON, an argument typed as a string instead of a number, a nested field flattened. A lenient parser — the kind most agent frameworks ship with — helpfully "fixes" it and executes. Now you've run a subtly wrong call and the loop moves on.
The guard: validate every tool call against a strict schema and reject on mismatch — never coerce. A rejected call you can retry; a coerced call you'll never know about.
from pydantic import BaseModel, ValidationError
class Refund(BaseModel):
order_id: str
amount_cents: int # strict: a string "500" must fail, not coerce
def dispatch(name, raw_args):
try:
args = Refund.model_validate(raw_args, strict=True)
except ValidationError as e:
# feed the error back to the model and retry — do NOT guess the args
return {"tool_error": str(e), "retry": True}
return run_refund(args)
The point is the strict=True and the reject path. A drifted call becomes a loud retry instead of a silent wrong action.
3. Lost in the middle#
A 1M-token context window is a storage claim, not an attention guarantee. Feed a cheap model a long context and the fact it needs from step 3 can be effectively gone by step 30 — present in the buffer, absent from the answer. This is the operational face of what we called agent memory rotting in production, and it's why cheap 1M context doesn't retire context management.
The guard: plant a canary fact. Inject a unique, known token early, and assert it's still retrievable right before you act on the context. If the canary is gone, so is your real signal — compact or re-retrieve instead of proceeding.
CANARY = "canary-7f3a: the customer's tier is ENTERPRISE"
context = CANARY + "\n\n" + retrieved_docs
# ...many turns later, before a tier-dependent decision:
answer = model.ask(context, "What tier is the customer, and the canary token?")
if "7f3a" not in answer or "ENTERPRISE" not in answer:
context = recompact(retrieved_docs) # the middle fell out — rebuild it
Two lines turn an invisible failure into an assertion you can trip on.
4. The confident wrong plan#
Premium models tend to hedge — "I'm not certain, but…" — and that hedge is a signal you can route on. Cheaper models hedge less. They commit to a plausible-but-wrong plan in a clean, assured voice, and the loop dutifully executes all of it.
The guard: self-consistency on the high-stakes steps. Sample the decision twice (or at a slightly higher temperature) and compare. Agreement is cheap confidence; disagreement is your cue to escalate that one call to a premium model.
def decide(prompt, cheap, premium):
a, b = cheap(prompt), cheap(prompt) # two cheap samples
if normalize(a) == normalize(b):
return a # they agree — trust the cheap tier
return premium(prompt) # they diverge — pay for certainty
You spend two cheap calls to avoid one confident mistake, and you only reach for the expensive model on the calls that actually earned it.
The failure surface of an agent is not the failure surface of a chat completion. You monitor completions for errors; you have to monitor agents for quiet drift.
The pattern that keeps the savings#
Notice what none of these guards is: "use a more expensive model." That would throw away the whole reason the cheap tier exists — and premium models fail the same four ways, just slower. The durable shape is tiered:
- Default cheap. Run bulk agent volume on the $0.14 tier.
- Instrument for silence. Golden transcripts on the final state, strict tool-arg validation, canary facts, self-consistency on the risky steps.
- Escalate on failure, not on fear. Route only the calls that trip a guard up to a premium model.
Do that and you keep the roughly 35x output-cost savings on the majority of calls that don't need a flagship, while the silent failures turn into loud, catchable ones. Add a hard per-run spend cap so a retry storm can't run up the bill, and — before you trust any new cheap provider at all — verify the open-weight model is what it claims to be.
The cheap tier is a gift. Just don't let it fail you quietly.



