Guardrails from failures
An agent that merely logs its failures repeats them; an agent whose failures become preventive memory doesn't. This pattern turns each failure into something that changes future behavior — a rule that always injects, a confidence penalty on the memories that misled, or a distilled guardrail lesson with trigger conditions. Use it whenever a class of failure is expensive enough that "we'll notice next time" isn't good enough.
How it works
Failures harden into guardrails at three tiers, from fully manual to fully automatic:
| Tier | You do | Memory gets |
|---|---|---|
| Explicit rule | Write the invariant yourself | A rule entry — always injected, exempt from relevance competition |
| Attributed failure | record_outcome(outcome="failure") on the entries that misled | Confidence penalties that demote bad guidance in ranking |
| Automatic distillation | Nothing — instance feature | Repeated failure-credited traces distilled into guardrail lessons with trigger preconditions |
Tier 1 — explicit. When you already know the invariant, don't wait for the system to learn it. A rule entry is a hard constraint: context assembly injects it in the always-on Active Rules section (see context priority), it never has to win a relevance contest, and it is authored — never produced or promoted by the automatic loop.
client.remember(
session_id="nimbus:ari:support",
content="Never deploy a regex change to the export pipeline without a green run "
"of the multi-line CSV fixture suite.",
intent="rule",
)Tier 2 — attributed. When a task fails, penalize the memories that led it there. record_outcome(outcome="failure", signal=-0.8, entry_ids=[...]) drops each entry's knowledge_confidence, so the same misleading guidance stops outranking better candidates. This is the negative half of the outcome attribution loop.
Tier 3 — automatic. Hosted instances run guardrail distillation as an always-on maintenance feature (per-instance configurable): when similar failure-credited traces recur, they are distilled into a preventive lesson with trigger preconditions — "before doing X under condition Y, check Z" — that competes into the lesson overlay of future queries. You feed it simply by doing tier 2 consistently.
diagnose(): have I hit this error before?
Guardrails also need a read path at the moment of failure. diagnose() takes the error your agent just hit and retrieves the failure lessons that match it — returning failure_lessons (scored evidence items), a summary, and total_failure_lessons. Call it in your error handler, before the retry:
Walkthrough: the bad regex becomes a guardrail
Ari ships a regex "fix" that takes down Nimbus's export pipeline. Here is the incident becoming prevention, tier by tier:
# Tier 2 — the incident run attributes the failure to the memories used.
client.record_outcome(
session_id="nimbus:incident-0142",
reference_id=misleading_lesson_id, # the lesson that suggested the quick fix
outcome="failure",
signal=-0.9,
entry_ids=recalled_entry_ids, # everything that informed the change
verified_in_production=True, # it failed in prod — that is a verdict too
rationale="Regex change broke multi-line CSV rows in the export pipeline",
)
# Reflection distills the incident into a failure lesson with conditions.
client.reflect(session_id="nimbus:incident-0142")
# Tier 1 — the postmortem writes the invariant down as a hard rule.
client.remember(
session_id="nimbus:ari:support",
content="Never deploy a regex change to the export pipeline without a green run "
"of the multi-line CSV fixture suite.",
intent="rule",
)Tier 3 does the rest: because export-regex failures have now recurred, guardrail distillation reinforces the pattern into a preventive lesson with its trigger precondition attached.
Weeks later, Ari attempts another regex change. The rule arrives unconditionally in Active Rules; the guardrail lesson arrives via the lesson overlay because the query matches its conditions; and when the deploy still throws, diagnose(error_text=...) surfaces the incident lesson at error time — so Rex, the reviewer agent, blocks the deploy until the fixture suite passes. When the guarded attempt succeeds, record_outcome(..., outcome="success", verified_in_production=True) strengthens the guardrail instead of the shortcut.
verified_in_production=True applies to failures as much as successes — "this guidance demonstrably failed in prod" is exactly the trust signal that should demote it hardest. See Trust, confidence, and staleness.
When not to use it
- Transient faults — a network blip or a 503 is not a lesson; handle it with plain retries and backoff. Persisting one-off noise as failure memory dilutes the guardrails that matter. (For behavioral pathologies like retry loops, see Loop safety.)
- Failures with no recurring shape — distillation and
diagnose()both work off similarity; a truly unique failure is better captured as a plain observation. - Secrets in error text —
error_textis stored and retrieved like any memory. Scrub tokens and customer data before recording.
Related
- The outcome attribution loop — the failure-crediting mechanics this pattern builds on
- Reflection to lessons — how the incident run becomes a conditioned failure lesson
- The learning loop — guardrail distillation among the runtime maintenance features
- Trust, confidence, and staleness — how penalties surface as
knowledge_confidence - Retrieval semantics — a
diagnose-style error lookup traced through retrieval - The memory model —
rulevslessonvsobservation
Configuration. Guardrail distillation is instance configuration — MUBIT_CL_GUARDRAIL_DISTILL on the instance, enabled by default on hosted instances alongside the other maintenance features listed in The learning loop.