The short version: A classic postmortem asks "what was the root cause?" and walks a deterministic chain back to a broken line of code. Autonomous agents break that method, because the same input that failed today might pass tomorrow — the system is non-deterministic by design. So you change the question from "what was THE cause" to "which contributing factors, across which layers, made a bad outcome likely" — then reproduce, locate the failure in one of four layers, and ship a guardrail plus an eval case, not just a better sentence in the prompt. Here's the template.
This is the review half of running an agent in production; the capture half — what to log so you can reproduce a failure — is its prerequisite. Without those logs, this whole exercise stalls at step 1.
1. Reproduce before you theorize#
The first move is not "why did it happen" — it's "does it happen." Replay the run from the resolved prompt and the raw tool results you logged, with the model version and seed pinned. Two outcomes, both useful:
- It reproduces. Good — you have a deterministic handle on a non-deterministic bug. Now you can bisect.
- It doesn't. Then it's sampling variance or a moved dependency (a changed retrieval index, a model snapshot). That's a different investigation — and a sign you may need more log fields before theorizing.
Skipping this step is how teams spend a week fixing a failure that happened once in ten thousand runs and will never recur.
2. Build the timeline from steps, not from memory#
Lay out the run as an ordered list of steps from your trace: at each step, what the model saw, what it chose, what came back, and its terminal state. The timeline is where "the agent went rogue" resolves into "at step 4 it called refund with an amount it read from a tool result that was already wrong at step 3." Vague blame dissolves into a specific step index.
3. Locate the failure in one of four layers#
Most agent incidents are a stack of contributing factors, but there's usually one layer where the intervention belongs. Classify it:
- Context / prompt — the model saw the wrong or incomplete input (bad retrieval, stale field, truncated history). The most common layer.
- Model decision — the input was fine and the model still chose badly (wrong tool, hallucinated argument, overtrusted a result).
- Tool / data — the tool errored, returned a misleading result, or had a contract the model misread.
- Orchestration / guardrail — the loop had no cap, no validation, no approval gate, so a single bad step reached a real side effect.
Naming the layer names the owner and the fix. A retrieval bug and a missing spend cap are not solved by the same person or the same PR.
4. Write causes as weighted contributing factors#
Drop the single "root cause" box. Replace it with a short, honest list:
Contributing factors (weighted):
[High] No schema validation on refund.amount — a bad value reached a side effect.
[Med] Tool `lookup_order` returned a stale total (cache TTL too long).
[Med] Prompt didn't tell the model to re-verify amounts before a refund.
[Low] temperature 0.7 widened the range of plausible-but-wrong actions.
Notice the ordering: the top factor is a missing guardrail, not the model's mistake. That's deliberate — it points the fix at the durable layer.
5. Ship a guardrail, then an eval case, then maybe a prompt#
Rank your fixes by durability:
- A guardrail that holds regardless of what the model decides — schema validation on tool arguments, a spend or step cap, an ask-for-help threshold, a human approval on the irreversible action, a runtime kill switch.
- An eval case that reproduces the failure — it must fail on the old code and pass on the new. This is how the bug can't quietly return the next time someone edits the prompt.
- A prompt change — useful, but the least durable: it regresses silently on the next model update or edit. Tune it, don't rely on it.
The definition of done isn't "we changed something." It's: the eval fails on old code, passes on new, and a guardrail bounds the blast radius even if the model misbehaves again.
6. Keep it blameless — toward the humans and the system#
"The model hallucinated" is a blameless starting question, not a conclusion. The productive follow-up is: and what in the system we built let a hallucinated value reach a side effect unchecked? That single reframe does two things at once — it keeps the review from turning into finger-pointing at a teammate, and it keeps it from dead-ending at "well, the model's just like that." The agent is a system you assembled; every incident is a gap in that system you now get to close.
Scale the ceremony to the blast radius: a cosmetic wrong answer earns a fifteen-minute note and one eval case; an agent that double-charged a customer earns the full template, same day, while the logs are still warm. Do this a dozen times and the postmortems stop reading like mysteries and start reading like a changelog of guardrails — which is exactly what a reliable autonomous product is underneath.



