Comparing Loop Traces to Find Regressions
Most teams catch agent regressions by watching aggregate metrics. That's too late and too coarse. Agent loop trace comparison finds the exact step a change broke. Here's how to do it.
def compare_traces(baseline_version, candidate_version, test_inputs):
regressions = []
for inp in test_inputs:
base = run_and_trace(baseline_version, inp)
cand = run_and_trace(candidate_version, inp)
diff = diff_traces(base, cand)
if diff.diverged:
regressions.append({
"input": inp,
"diverged_at_step": diff.first_divergence,
"baseline_action": diff.base_action,
"candidate_action": diff.cand_action,
})
return regressionsMost teams catch agent regressions the slow way: they ship a change, watch aggregate metrics for a few days, and notice success rate drifted down. By then the regression is live, the cause is buried under everything else that changed, and you're reverse-engineering which of your edits broke things. Agent loop trace comparison flips this — you diff the traces a change produces against a known-good baseline and see the exact step where behavior diverged, before you ship. It's faster and far more precise than waiting for metrics to tell you something's wrong.
This guide shows how to compare traces to catch regressions at the step level, so a bad change is obvious before it reaches production.
Quick-Start (Copy This Right Now)
Here's the core of trace comparison — run a fixed set of inputs through two versions and diff the resulting traces.
[object Object], ,[object Object],(,[object Object],):
regressions = []
,[object Object], inp ,[object Object], test_inputs:
base = run_and_trace(baseline_version, inp)
cand = run_and_trace(candidate_version, inp)
diff = diff_traces(base, cand)
,[object Object], diff.diverged:
regressions.append({
,[object Object],: inp,
,[object Object],: diff.first_divergence,
,[object Object],: diff.base_action,
,[object Object],: diff.cand_action,
})
,[object Object], regressionsWhat this does: It runs the same fixed inputs through the baseline and the candidate, diffs each pair of traces, and reports exactly which inputs diverged and at which step — turning "did this change break something?" into a concrete list of where behavior changed, via agent loop trace comparison.
Understanding the Variables
Trace comparison hinges on three things, and the subtlety is that not every difference is a regression.
The baseline is your reference — a set of traces from a version you trust, on a fixed set of representative inputs. The quality of your comparison is only as good as the quality and coverage of this baseline. It needs to include the cases you care about, especially the tricky ones a regression would most likely break.
The divergence point is what you're hunting: the first step where the candidate does something different from the baseline. Finding the first divergence matters more than counting total differences, because once two traces diverge, everything after is downstream of that first split — the real cause is at the divergence, not in the cascade that follows.
The hard part is that difference is not the same as regression. Agent runs are somewhat nondeterministic, so two traces of the same input can differ innocently — a slightly different phrasing, a different-but-equivalent tool order. The skill in trace comparison is separating meaningful divergences (a different tool that leads to a worse outcome) from noise (a cosmetic difference that changes nothing). This is where naive diffing fails and thoughtful comparison earns its keep.
It helps to think about what agent loop trace comparison catches that aggregate metrics can't. Aggregate metrics tell you that success rate dropped two points across thousands of runs; they can't tell you which runs changed or why. Trace comparison tells you that on these seven specific inputs, the agent now chooses a different tool at step three, and that's where the regression lives. The difference is between knowing a problem exists and knowing exactly where it is. Metrics are a smoke alarm — useful, but they only tell you something's burning. Trace comparison is the map that points at the room. You want both, but the map is what actually lets you fix the thing quickly, and it works before you ship rather than days after.
⚡ Pro tip: Keep your baseline set small enough to review by hand but rich enough to cover your real failure surface — usually a few dozen carefully chosen inputs beats thousands of random ones. The goal is a set where a human can actually look at every divergence a change produces. A baseline so large that divergences pile up faster than anyone can review them gives you the illusion of coverage without the ability to act on what it finds.
⚡ Pro tip: Run trace comparison with deterministic settings on both versions. If you compare two nondeterministic runs, you can't tell whether a divergence came from your change or from random sampling. Pin the model to deterministic mode and use recorded tool results, so any divergence you see is attributable to the change you made, not to noise. Without this, trace comparison drowns in false positives.
Step-by-Step: Building Trace Comparison Into Your Workflow
Start by building a stable baseline set — a fixed collection of representative inputs and the trusted traces they produce. Version this alongside your code, so you always have a known-good reference to compare against.
[object Object], ,[object Object],(,[object Object],):
,[object Object], {
inp.,[object Object],: run_and_trace(version, inp, deterministic=,[object Object],)
,[object Object], inp ,[object Object], test_inputs
}What this does: It runs your representative inputs through a trusted version with deterministic settings and stores the resulting traces as a baseline, giving you a stable reference that future candidates get diffed against.
Next, make the diff meaningful by comparing at the level of decisions, not raw text. You care whether the candidate chose a different tool or took a different path, not whether a string was phrased slightly differently.
[object Object], ,[object Object],(,[object Object],):
,[object Object], i, (b, c) ,[object Object], ,[object Object],(,[object Object],(base.steps, cand.steps)):
,[object Object], b.action_name != c.action_name ,[object Object], b.action_args != c.action_args:
,[object Object], Divergence(diverged=,[object Object],, first_divergence=i,
base_action=b, cand_action=c)
,[object Object], Divergence(diverged=,[object Object],)What this does: It walks both traces step by step and flags the first step where the chosen action or its arguments differ, focusing the comparison on meaningful decision-level divergence rather than cosmetic text differences that don't affect behavior.
Finally, wire trace comparison into your change process — run it on every prompt or loop change before merging, and treat unexpected divergences as something to explain before shipping.
⚡ Pro tip: Not every divergence is bad — some are your change working as intended. So when a trace diverges, the question isn't "did it change?" but "did it change for the better, the worse, or neither?" Review divergences by hand at first and label them, then feed those labels into an automated judge that learns which kinds of divergence in your system are regressions versus improvements versus noise.
Pro-Level Variations
For prompt engineering specifically, trace comparison is the fastest feedback loop you can have — change a line of the prompt, diff the traces across your baseline set, and immediately see which cases the change affected and how, instead of guessing at the change's blast radius.
For a large agent with many task types, segment your baseline by type and run comparison per segment, so a change that helps one task type while hurting another shows up as divergences concentrated in the hurt segment rather than averaging out to "no change."
For continuous deployment, run trace comparison automatically in CI against your baseline, and block a merge if it produces divergences in cases marked critical — turning regression-catching from a manual review into an automated gate.
For prompt-heavy agents where small wording changes ripple widely, trace comparison becomes a blast-radius estimator. Before shipping a prompt tweak you think is minor, diff it across the baseline and count how many cases it actually touches. A change you expected to affect two edge cases that turns out to diverge on twenty is telling you it's not minor at all — the wording shifted the agent's behavior far more broadly than you intended. This early blast-radius read has saved teams from shipping "tiny" prompt changes that quietly rewrote behavior across half their traffic, which is a failure mode aggregate metrics only reveal after the damage is done.
⚡ Pro tip: When a divergence turns out to be an intended improvement, update your baseline to the new traces immediately, with a note on why. A baseline that isn't maintained rots — it keeps flagging your deliberate improvements as divergences until people learn to ignore the tool. Treating baseline updates as a normal, documented part of shipping a good change is what keeps trace comparison trustworthy over months instead of becoming noise everyone tunes out.
Troubleshooting Common Issues
If trace comparison reports divergences on nearly every input, you're comparing nondeterministic runs and drowning in noise. Pin both versions to deterministic mode with recorded tool results so only real changes surface.
If it reports no divergences but users still see regressions, your baseline set doesn't cover the cases that broke. Expand it — especially with the tricky, edge-case inputs where regressions actually hide, not just the happy-path cases that rarely change.
If divergences are real but you can't tell good from bad, your diff is too raw. Add outcome information to each trace — did the run succeed, what did it cost — so you can see not just that a path changed but whether the new path led somewhere better or worse.
⚠️ Common mistake: Treating any trace difference as a regression. Agent behavior legitimately varies, and many divergences are either your intended improvement or harmless noise. A comparison setup that flags every difference as a problem generates so much false alarm that people stop looking at it, which is worse than no comparison at all. Compare deterministically to kill the noise, and judge divergences by their effect on outcomes, not by their mere existence.
Your Turn
Build a versioned baseline of trusted traces over representative inputs, diff candidates against it at the decision level with deterministic settings, and judge divergences by whether they improve or worsen outcomes. Wire it into your change process so regressions surface before they ship, not days later in aggregate metrics.
The baseline builder, the trace differ, and the divergence-review workflow are reusable across every agent and every change you make. A prompt and snippet library like PromptABCD is a natural home for your comparison scaffolding and the baseline prompts it tests, so catching regressions at the step level becomes a standard part of shipping changes rather than a thing you wish you'd had after the fact.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
