AI Engineer World's Fair 2026
Your Agent Failed in Prod. Good Luck Reproducing It.
Read the talk
Your Agent Failed in Prod. Good Luck Reproducing It.
A successful API call can still execute the wrong trade. Recording agent boundaries turns an otherwise elusive failure into a repeatable test of the code that must stop it.
From a talk by Tisha Chawla and Susheem Koul
Before you start: Familiarity with LLM tool calls, function inputs and outputs, and basic automated testing will help you follow the replay example.
The production failure that disappears locally
An agent calls the wrong tool or writes the wrong data in production. You retrieve the prompt from telemetry, send it to the same model, and run the workflow locally. It works. Further reruns work too. The damaging run is the one you cannot recover, so a successful retry tells you little about whether the next customer is protected. For Tisha Chawla and Susheem Koul, whose agents interact with production backends, this is a debugging problem with consequences beyond a bad answer.
Consider their broker scenario. A user asks to sell $1,000 of stock. Instead of converting that dollar amount into shares, the agent copies 1000 into the order’s quantity field. At $190 per share, the illustrated 1,000-share order sells $190,000 rather than the requested $1,000. The tool has received a valid number with the wrong meaning.
In the speaker-presented scenario, the broker returns HTTP 200 in 30 milliseconds, with zero exceptions and zero alerts. Those are scenario details, not an independently measured incident. The green dashboard accurately reports that the API accepted the request; it does not establish that the trade matched the user’s intent. Successful execution and correct action are different properties.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Temperature zero does not freeze the serving system
Turning temperature down to zero does not repair the dollars-to-shares mistake. Greedy decoding can consistently choose a wrong answer. Nor does greedy selection alone guarantee identical output across requests: choosing the highest-scoring token is repeatable only when the scores themselves remain the same. The talk cites engineering reports of dozens of different responses across a thousand identical-prompt runs; that observation should not be treated as a universal failure rate.
The numerical explanation separates several mechanisms:
- Sampling versus system determinism. An
argmaxrule specifies how to select from the logits, not how the serving system computes those logits. - Floating-point arithmetic. Addition is not associative at finite precision. Different reduction orders can produce slightly different scores; if the leading tokens are sufficiently close, the winning token can change.
- Batch composition. The talk contrasts an isolated matrix operation under fixed conditions with a request grouped alongside changing traffic. Batch-dependent computation can change the numerical path even when the request itself is unchanged.
- Expert capacity. In a capacity-limited mixture-of-experts implementation, competition for an expert can change how tokens are handled. Overflow and rerouting depend on the implementation; they are not properties of every MoE model.
These mechanisms explain why controlling a request parameter is weaker than controlling the inference stack. The isolated-operation comparison also requires fixed execution conditions, rather than a guarantee across hardware, releases, or kernels.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Recover the run instead of regenerating it
For the broker, identical prose is not the engineering objective. The consequential question is whether the system executes an acceptable state transition. Once a bad decision has occurred, debugging needs that particular decision available again—not another attempt to persuade the model to produce it.
| Property | What it asks for | Engineering role |
|---|---|---|
| Bitwise determinism | Same input, identical output | Control generation |
| Replayability | Reconstruct a recorded run | Inspect and retest execution |
Replayability changes the dependency: capture what the model actually produced, then use that captured result when investigating the failure. The speakers favor leaving room for exploratory generation because they value its creative benefits. That preference does not require every application to use randomness; the essential point is that debugging should not depend on regenerating the same answer through an uncontrolled hosted stack.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Record semantic boundaries
A network recorder cannot see every meaningful agent operation. Local retrieval, in-process tools, and memory access may never issue a network request. The useful recording surface is therefore the boundary of each node: what entered the operation and what left it. Those inputs and outputs preserve the meaning of a step more directly than packets do. Streaming and asynchronous execution make preserving that structure especially important.
The workflow is to annotate, record, visualize, understand, fix, replay, and verify. Recording supplies the evidence for diagnosis; replay makes that evidence usable as a test input. If all model boundaries are stubbed with their recorded outputs, the test can run offline without making model calls.
Chronicle is the proof of concept used to demonstrate this approach. A boundary annotation wraps a method, whether it calls an LLM, retrieves RAG context, or executes a tool. The wrapper records the method’s input/output pair, with metadata such as the model version and code version. Together, those records form the trace.
The recording is an execution envelope, not a snapshot of every external system: side effects inside a boundary are not automatically captured. There is also a version distinction when following the demonstration: current Chronicle documentation uses ReplayPlan for stub/live selection and still lists streaming capture as planned. The demonstrated boundary mechanism should not be read as support for every streaming or asynchronous configuration.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Find where dollars became shares
The stock agent has three annotated methods. A planning LLM receives the user’s request, the place_order tool performs the trade, and a finalization LLM prepares the response. Recording each method gives the trace two model boundaries and one tool boundary.
In the recorded demonstration, the user requests about $1,000 of Acme stock. The planning node generates a place_order call with symbol Acme and quantity 1000. The tool then sells those 1,000 units at $190 each. The trace preserves both the erroneous instruction and the tool’s execution of it; a final response alone would not expose that distinction.
The detailed node JSON adds model-version and sampling metadata alongside the inputs and outputs. Diagnosis proceeds backward from the consequential action: inspect the sell tool’s input, confirm that it received quantity 1000, and then inspect the preceding planner node. Its output already contains the wrong quantity. The tool did not invent the number; the planner converted the user’s monetary intent into the wrong tool argument.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Replay the bad decision against the repaired tool
Locating the bad tool call does not make future model outputs controllable. The repair moves enforcement to the tool boundary: a guardrail must reject an unacceptable order even when the planner requests it. The demonstration does not specify the stock guardrail’s exact rule, so the test establishes its response to this recorded failure rather than a general trading policy.
The same boundary that records a method can substitute its recorded output during a test. This lets you hold upstream decisions fixed while executing changed code:
- Load the trace of the failed run.
- Enable replay and choose which boundaries to stub.
- Stub the planning LLM so it emits the original incorrect order.
- Run the repaired
place_ordermethod against that order. - Capture its result and assert that the order was blocked.
In the general regression strategy, unchanged nodes can all be stubbed. The essential choice is to keep the code under test live while preventing a fresh model decision from removing the failure condition.
A small Python harness expresses the assertion contract without depending on Chronicle’s entry-point syntax. Here, the tool adapter returns a dictionary with a status field, and the recorded planner arguments stay fixed:
python
from copy import deepcopy
from unittest.mock import Mock
def verify_recorded_order_is_blocked(repaired_tool):
recorded_arguments = {
"symbol": "Acme",
"side": "sell",
"quantity": 1000,
}
planner = Mock(return_value=deepcopy(recorded_arguments))
order = planner("Sell about $1,000 of Acme stock")
result = repaired_tool(**order)
assert order == recorded_arguments
assert result["status"] == "blocked"
return result
The tool still receives the bad quantity. The test succeeds because the repaired boundary refuses it, not because the planner produced a better answer. A live boundary executes real code and its side effects, so a destructive tool needs gated execution or a sandbox; replay mode itself is not a safety barrier.
The displayed replay stubs the first LLM, preserving its recorded input and output, while leaving the tool and downstream LLM live. The tool returns a blocked result and the blocked-order assertion passes. This is selective replay, not the fully stubbed configuration with no model calls. It verifies the repaired tool against the original bad decision while still allowing later response generation to run.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Test enforcement and behavior separately
A recorded session can become a test case, but that does not make every aspect of agent quality a deterministic assertion. The two testing approaches answer complementary questions:
| Test type | Target | Method |
|---|---|---|
| Deterministic regression | Guardrails and tool logic | Recorded inputs, live changed code, assertions |
| Behavioral evaluation | Tone and trajectory quality | Assess generated behavior, potentially with an LLM judge |
For deterministic regression, recorded model outputs hold the surrounding context fixed. Stubbing every model boundary removes sampling from the test and avoids model calls and their associated inference charges; ordinary execution costs remain. Behavioral evaluation instead asks whether the agent’s generated response or chosen trajectory is appropriate. The speakers propose LLM-as-judge techniques for those more subjective assessments. A passing tool assertion protects a specific invariant, while behavioral evaluation examines the quality of the decisions and responses around it.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep the execution envelope as a test case
Production debugging should not depend on bitwise identity from a hosted stack you do not control. Record the variables that explain the session: the LLM version, build ID, retrieved RAG chunks, and the inputs and outputs at the relevant boundaries. A prompt is only one ingredient in the resulting action. Capturing the full execution envelope preserves the context needed to understand why that action occurred.
Use that envelope to investigate the failure, run the repaired code against it, and retain it as a regression test. The speakers’ closing recommendation is to preserve generation-time variation rather than treat temperature zero as a reliability fix; their closing QR offers Chronicle code and related articles. For the stock agent, the durable improvement is that the recorded bad order remains available to test the tool that must stop it, even if the model never generates that exact mistake again.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Python tooling for recording method boundaries and testing changed code against recorded agent decisions, with examples and replay assertions.
Further reading
Tisha's extended discussion of inference variability, execution recording and complementary testing approaches.
A companion walkthrough of selective replay, tool guardrails, structural assertions and trace redaction.
Original experiments explaining how batch-dependent numerical behavior affects greedy decoding and how batch-invariant kernels restore repeatability.
Research on block-sparse MoE training that avoids dropping tokens when expert workloads vary.
Read the complete timestamped transcript
- 0:00
Imagine something your agent didn't prod was wrong. It called the wrong tool, it wrote the wrong thing, and now suddenly your team is on, on call rotation to figure out what actually went wrong.
- 0:14
Pretty common, right? Now, as per standard engineering response, your gut will tell you to pull the raw prompt from the telemetry logs, pass it to the same model using the same prompt, and run it locally to isolate the bug, which we'll all do.
- 0:31
And surprisingly, it will work as well. Run it again, it will work again. You run it ten more times, it will be just perfect every time. But now let's talk about that one run which costed you, and that will be gone.
- 0:46
You can reproduce it, and if you can reproduce it, you can debug it. But if you can debug it, you can't promise it won't happen to your next customer or user, right?
- 0:57
Now, I am Tisha. I have Susheem with me as my co-presenter. We both run agents against real production backends. You know, the kind of place where a bad write isn't, "Oh, well, run it again."
- 1:09
It's you on a call with a customer explaining where the data actually went. This whole talk is going to be about that one thing you lose the second an agent goes haywire in production, which is being able to reproduce it.
- 1:24
That will be our North Star for the next ten minutes to follow. Now let's look at how this actually blows up. You've got an agent hooked to a broker API, which is the scenario I'm taking.
- 1:37
The user says, "Hey, sell a thousand dollars of stock." Now comes the interesting part. Instead of doing the math, the agent sells the raw number one thousand and dumps it straight into the quantity field.
- 1:53
Guess what? It sells one thousand shares instead. Now, at a hundred and ninety bucks a share, a thousand dollar intel will become how much? A hundred and ninety thousand dollars disaster, right?
- 2:07
And the terrifying part is that the API on my end returned a clean two hundred OK in thirty milliseconds. We got zero exceptions, zero alerts. If you see the trade, it's completely wrong, but your dashboards are sitting there perfectly green, perfectly flawless.
- 2:26
When such a scenario as we last discussed comes up, what's the first thing which you will do to try and fix this? The reflex here is to, you know, just turn the model temperature down to absolute zero, assuming greedy decoding will make everything deterministic, right?
- 2:45
But that's a complete misconception. Setting the temperature to zero doesn't fix a broken reasoning path. It just means the model is going to make the exact same logical error, the exact same way, at the exact same time, and honestly, even worse than that.
- 3:05
To back up the scenario we just discussed, look at the engineering threads on Reddit and Hacker News. The hard data shows that temperature zero isn't even truly deterministic on a hardware level.
- 3:17
Running the same prompt a thousand times can still return dozens of completely different responses just due to the underlying GPU nondeterminism and the MoE architectures which are there.
- 3:32
So to understand why this actually happens, we'll have to look at it from first principles. It comes down to four simple things. One,
- 3:42
sampling determinism isn't system determinism. Temperature zero just means always take the argmax, but it doesn't guarantee that the underlying scores stay identical run to run. Two, floating point math isn't associative.
- 3:59
The order you add your decimal matters, right? But a timing shift in matrix operation alters the final logits, and which in turn will flip the winning token. Three, it's not a concurrency issue.
- 4:15
Run the same matrix multiplication alone on a GPU a thousand times, and I'll guarantee you'll get the exact same bits. So the real culprit is batch invariance here, because your request gets grouped with whatever else hits the server that millisecond.
- 4:32
Four, mixture of experts routing has the exact same bottleneck. Experts have strict capacity limits. If a batch overflows a specific subnetwork, tokens get rerouted. Whether a to-token makes the cut depends entirely on the traffic you got batched with.
- 4:51
So the ultimate takeaway here is that chasing text output is a losing battle, completely. We don't need the model to return the exact same token back every time. We just need our system to execute the exact same state transition, which means we've been asking the wrong question all along, right?
- 5:13
The wrong question is, how do I make the model deterministic? And I've seen teams burning weeks on that and walk away deciding the system's just unknowable. The right question is, how do I debug and retest a run I can't reproduce?
- 5:29
Because determinism was never the North Star, debugging was. Two words we keep mixing up, which I'll talk about now, is bitwise determinism and replayability. Bitwise determinism is same input, same output.
- 5:44
That's controllability. You're not getting it from a hosted API, and you don't actually want it, because the randomness is what makes the model good. Once the model explores more, you'll get more creative answers.
- 5:56
The other one is replayability, which is rebuild a run that already happened well enough to debug it. That's observability. You don't need the model deterministic. You need the run recorded, and you don't freeze the model, you capture what it did.
- 6:14
Now the question which we are all thinking about is where do you record? For sure, not at the network layer because half your agent will never touch the network.
- 6:25
The local retrieval, the in-process tools, the memory, and the parts that do not shred under streaming and async.
- 6:33
Record at the boundary instead because you need to capture what enters each node and what leaves it, the meaning of each step and not the packets. What replay adds here is a deterministic CI where you stop the model, you'll rerun the exact failure offline with zero model calls.
- 6:57
Let's talk about the loop end-to-end now. It starts with annotation, recording, visualization, understanding, fixing, then the part we're working on, which is replaying, and finally verifying. All right. Let's now see how to bring the workflow we just discussed into action.
- 7:15
So we've established that replayability is a core tenet of productionizing any AI agent. But how do we build this in code? As a proof of concept for this, what we have done is we have built something called Chronicle.
- 7:27
At the heart of Chronicle lies the concept of a boundary. Think of a boundary as a bounding box around any node in your agentic workflow. A node can be a tool call, it can be a call to an LLM or a retrieval from a RAG.
- 7:40
It doesn't matter. As long as it's a method, it can be annotated with the boundary annotation. Now, what does this annotation do? It ensures that anything that goes into the method and comes out of the method gets recorded.
- 7:51
So any input and output pair will get recorded. On top of that, you can define parameters like your model version or the version of the code that is running, so that the entire state during which the agent run happened gets frozen and saved as a trace.
- 8:05
Now, let's see this in action. We've been talking about this stock selling agent, which went haywire in production. This is a representation of the same. You have your initial planning step, which takes into account the user input.
- 8:17
It can use the place order tool to do the actual selling and buying of the stocks. And then finally, it delegates to the finalize agent, which generates a succinct response for the end user.
- 8:27
We have annotated all three of these methods with the boundary annotation. The first one is a tool, and the second and the third one is an LLM. Let's run this.
- 8:40
So here's what happened. You gave a user request to sell about thousand dollars worth of Acme stocks. You have the three nodes, you have the input and the output recorded for every one of them.
- 8:49
You can see that the LLM mistook the thousand as the quantity and generated a tool call of place order with the symbol Acme and quantity thousand. And the place order tool obviously executed this input and sold thousand dollars or a thousand units of Acme stock at one ninety dollars per piece.
- 9:08
This is where the problem started. Now you have a trace for it, but is this all you can see? No. We record much more details than this. So you can go and see the hyper detailed JSON for each of the nodes that the call went into or went out of.
- 9:24
You can see the metadata, like the model version, the sampling versions that was there in the LLM call. You can see the input and the output. For example, this is for the place order tool.
- 9:32
You can see the input went in as a sell of thousand quantity on Acme, and the output was obviously that it sold that quantity. And if you go one step back, you can see the agent one node creating that tool call that caused this entire problem.
- 9:46
It created a tool call to place order with the symbol Acme and quantity thousand. Now, this is all good. You have a recording, you have a trace, you figured out what the problem is.
- 9:56
Now what do you do with it? You figured out that the problem is the LLM created a wrong tool call. You cannot control the LLM. That's what this entire discussion is about.
- 10:06
You cannot enforce bitwise determinism. What you can do is you can put guardrails on your tools to enforce some level of, uh, credibility on your production agent. But how do you test this?
- 10:18
Once you have built the guardrails, how do you test it? That is another tenet that Boundary offers. Since the boundary annotation is already providing a bounding box around your methods, it can be used to stub your methods in during testing.
- 10:32
So think of it like this. You have a run which is recorded. That run recorded every input and output for every node. Now, you have fixed your code at, let's say, the tool level, but you want the rest of the nodes to be stubbed so that the entire exact stack trace remains the same.
- 10:48
How do you do it? You run a test suit with the same trace that was recorded earlier. You stub every node other than the node that you changed, and you let Boundary handle the rest, right?
- 11:00
So let's see this one in action. This is your test case. You have loaded the trace, and you have enabled the replay mode on Boundary, which allows Boundary to stub every node that you want.
- 11:12
So, for example, in this case, you want to stub the first agent call, which generated the tool output, but you want the tool to be run live so that you can test out your code changes.
- 11:21
Once you do this, you run your agent. Now, another good thing about Boundary is that since it's already capturing the output of your tool as well, you can use it to run assertions.
- 11:30
So you can take the output and assert that this time around, the tool call got blocked. So let's run this once.
- 11:41
Perfect. So you can see that agent one, which was the LLM, was stubbed. You can see the same input/output as the recorded, uh, trace, but the tool and the LLM ran live.
- 11:52
And you can see that for the tool, this time the output is blocked, and your assertion on the tool has passed because the order is blocked. So this is the power of merging your, uh, replayability traces with auto-generated testing and stubbing and assertions.
- 12:08
Tool and the LLM. So we just saw how Chronicle not only records your agentic sessions, but it also uses those recordings as test cases. Now, when we talk about testing in AI agents, I want to draw a very clear distinction here.
- 12:20
There are two ways of testing AI agents, and both of them are equally important. There's the deterministic testing and then the behavioral testing. The deterministic testing applies to, obviously, the deterministic nodes of your agent graph.
- 12:32
This could be your guardrails or your tool calls. Now, this is exactly where Chronicle shines because, as we just saw, Chronicle freezes the entire agent run as a context.
- 12:42
So you can use the LLM nodes context to stub the LLM outputs. This essentially kicks the probability out of the window, and your entire agent run can become a test case.
- 12:53
This is re-runnable, and since it never calls the model, it is free. On the behavioral side of things, you measure things like the tone of the agent or whether the trajectory it took was right.
- 13:03
This is more subjective, and this is where techniques like LLM as a judge are better off. Now, at this point, I want to talk about the key takeaways. First, stop chasing bitwise determinism through the API.
- 13:14
The fundamental principles on which the APIs are built today do not make this possible. Second, know what are variables for your session. For example, your LLM version or your build ID or your RAG chunks, and make sure that you are logging these.
- 13:28
Third, capture the full envelope. Don't focus on just the prompt. There are, there are a lot more ingredients that go into that final response. Fourth, use the replays to debug.
- 13:38
Make sure that you find the issues, fix the failures, and then finally, use the same trace as a test case. Fifth and final, keep the generation time variation alive.
- 13:47
Don't try to pin the temperature to zero. After all, that is what brings the agency into your agent. So the QR on your screen, you can scan that to get access to the code for Chronicle and a bunch of nicely written articles.
- 13:59
And finally, thanks again for your time, and we hope that the traces that you put today in your agent make sure that your on-call cycle tomorrow is much better.
- 14:08
Thank you.