An AI agent can make a perfectly reasonable decision and still do the wrong thing.
That sounds like a model problem at first. Maybe the prompt was unclear. Maybe the model misunderstood the request. Maybe the reasoning wasn't good enough.
But in production, there is another failure mode that is much more interesting.
Sometimes the agent was right when it made the decision.
The problem is that reality changed before the agent acted on it.
A payment that was pending failed. An available product went out of stock. A customer who was eligible for a refund submitted another request. A security policy changed while the agent was processing an operation. Another service, user, or agent modified the state that the first agent was relying on.
The model didn't necessarily misunderstand anything.
It simply acted on yesterday's truth.
And once an AI agent starts interacting with real systems, that distinction becomes extremely important.
An AI Agent Doesn't Reason in a Frozen World
When we build a traditional function, we often imagine something relatively simple:
result = process(input)
The input arrives, the function runs, and the result is returned.
An agent is different.
An agent might first retrieve information from five different services, reason over it, call another tool, receive additional information, revise its plan, and eventually execute an action.
A simplified flow might look like this:
Read → Reason → Decide → Validate → Act
The problem is that the systems underneath this flow don't stop changing while the agent is thinking.
Imagine an agent handling a refund request.
At 10:00:01, it reads:
Order status: Delivered
Payment status: Captured
Refund eligibility: Yes
Fraud status: Clear
The agent reasons that the customer can receive a refund.
At 10:00:03, the payment service receives a chargeback notification.
The state changes:
Payment status: Captured → Chargeback initiated
At 10:00:04, the agent executes the refund.
From the agent's perspective, everything looked fine.
From the system's perspective, the world changed between the decision and the action.
That tiny gap is where a surprising number of production problems can hide.
The Read-Reason-Act Gap
I like to think about this as the read-reason-act gap.
The agent reads a state of the world.
It reasons about that state.
Then it acts on the conclusion.
The dangerous assumption is that the state it is in is still valid when the action happens.
Consider this timeline:
Time ───────────────────────────────────────────────>
Agent
|
| Read payment = Pending
|
| Read risk = Low
|
| Read customer = Verified
|
| Reasoning
|
| Decide: Approve
|
|-----------------------------> Approve Payment
|
v
Payment Service
|
| Payment becomes Flagged
|
v
The agent's reasoning may have been completely correct given the information available to it.
The problem is that the information stopped being current.
This is one of the reasons building reliable AI agents has less to do with making the model "smarter" than many teams initially expect.
The model needs good reasoning, but the surrounding system needs to make sure the reasoning is still applicable when execution happens.
A Database Version Check Helps, But It Doesn't Solve Everything
This is where traditional distributed-systems techniques become useful.
Suppose our payment record has a version number:
{
"payment_id": 123,
"status": "pending",
"version": 42
}
The agent reads the version 42.
When it eventually tries to approve the payment, the application performs an optimistic concurrency check:
UPDATE payments
SET status = 'approved',
version = version + 1
WHERE payment_id = 123
AND version = 42;
If another process has already changed the payment, the update affects zero rows, and the application knows that its view is stale.
That's good.
But there is an important limitation.
What if the agent's decision didn't depend only on the payment?
Maybe it also looked at:
Payment Service → Payment status
Risk Service → Risk score
Customer Service → Account status
Fraud Service → Fraud result
Policy Service → Applicable policy
Inventory Service → Availability
The payment might still be at version 42 while the fraud service has already changed its assessment.
The payment version check can pass.
The agent's decision can still be wrong.
This is why I don't think of agent consistency as simply a database versioning problem.
It's a decision-input consistency problem.
The Agent's Context Is Really a Snapshot
One of the easiest mistakes to make with agents is to treat retrieved context as truth.
It isn't the truth.
It is a snapshot of what the system looked like when the information was retrieved.
Imagine the agent receives:
Customer: Verified
Risk score: 12
Payment: Pending
Policy: v18
Inventory: Available
The agent doesn't actually know that these things will remain true.
What it knows is:
"These were the values I observed."
That difference may seem small when building a prototype.
In production, it becomes a major architectural concern.
A better mental model is:
Context = Evidence + Version + Timestamp + Freshness requirement
Now the system can reason about whether the information is still usable.
For example:
Payment status
Freshness: Very high
Fraud assessment
Freshness: High
Customer name
Freshness: Low
Policy document
Freshness: Version-specific
Not every piece of context needs to be refreshed constantly.
The important thing is knowing which pieces matter for the action you're about to take.
The Decision Should Carry Its Dependencies
One practical approach is to record the inputs that influenced a consequential decision.
Suppose an agent decides to approve a payment.
Instead of storing only:
{
"action": "approve_payment",
"payment_id": 123
}
The system could retain something closer to:
{
"action": "approve_payment",
"payment_id": 123,
"decision_inputs": {
"payment_version": 42,
"risk_version": 19,
"customer_version": 63,
"policy_version": 7
}
}
Now the execution layer knows what the agent actually relied on.
Before executing, it can decide which dependencies need to be checked again.
For example:
def validate_action(decision):
if payment.version != decision["payment_version"]:
return False
if risk.version != decision["risk_version"]:
return False
return True
This doesn't mean every value must be perfectly synchronised.
It means the system has an explicit way of saying:
"This action was based on these inputs, and these are the inputs that matter enough to validate."
That is a much stronger architecture than simply trusting whatever happens to be sitting in the agent's context window.
ETags Are Another Piece of the Same Idea
This doesn't have to be implemented only with database version columns.
HTTP already gives us a useful mechanism through ETags.
A service might return:
ETag: "payment-v42"
The agent or execution layer can retain that value.
When performing an update, the client can send:
If-Match: "payment-v42"
The server can then reject the operation if the resource has changed since the agent read it.
Conceptually:
The agent reads the resource
|
| ETag = v42
|
v
Agent reasons
|
| Resource changes to v43
|
v
Agent attempts update
|
| If-Match: v42
|
v
Server rejects stale update
This doesn't solve every agent-consistency problem, but it gives the execution layer a useful precondition: only act if the state is still the state I reasoned about.
The More Dangerous Case Is When Everything Looks Fine
The most difficult production bugs aren't always the ones where something obviously fails.
Imagine an agent approves an order.
The database update succeeds.
The API returns 200.
No exception is thrown.
The workflow is marked as successful.
Later, someone discovers that the order should never have been approved because the customer's fraud status changed while the agent was processing it.
From an infrastructure perspective:
Database ✓
API ✓
Authorization ✓
Tool call ✓
Transaction ✓
From a business perspective:
Outcome ✗
This is why monitoring only technical success metrics isn't enough for agentic systems.
We need to know not only whether an action succeeded, but whether the decision remained valid throughout execution.
This Changes How We Think About Observability
Traditional application logs might tell us:
10:00:01 Payment read
10:00:04 Payment approved
10:00:04 API returned 200
That's useful, but incomplete.
For an agent, I would want to know something closer to:
Decision ID: 8f32
Payment version observed: 42
Risk version observed: 19
Policy version observed: 7
Decision created: 10:00:02
Execution started: 10:00:04
Payment version at execution: 43
Risk version at execution: 19
Policy version at execution: 7
Action: Rejected
Reason: Payment state changed
Now we have an explanation.
The agent didn't necessarily "hallucinate."
The decision became stale.
That distinction matters for debugging, incident reviews, and improving the system.
Another Agent Can Change the World Too
External systems aren't the only source of change.
Another agent can create the same problem.
Imagine two agents working on customer accounts.
Agent A reads:
Credit available: $50,000
Agent B reads the same value.
Agent A approves a $40,000 transaction.
Agent B approves another $40,000 transaction for a different account.
Both agents might successfully update their own records.
There may be no conflicting write on either individual account.
But the shared credit limit has now been exceeded.
This is a classic write-skew scenario.
The important lesson is that protecting individual records isn't always enough.
Sometimes the system needs to protect the business invariant itself.
For example:
Total credit exposure <= $100,000
That constraint exists across multiple records.
A version check on Account A doesn't protect it.
A version check on Account B doesn't protect it.
The shared constraint needs to participate in the consistency mechanism.
This is where agent systems start inheriting some of the hardest problems from distributed systems.
Don't Confuse Stale Decisions With Duplicate Actions
There is another distinction worth making here.
Suppose an agent sends a payment request, times out, and doesn't know whether the payment was processed.
It retries.
That's an idempotency problem.
A request might use:
Idempotency-Key: payment-123-abc
So the server can recognise repeated attempts to perform the same logical operation.
That's important, but it solves a different problem.
Idempotency protects against duplicate execution.
It doesn't tell us whether the original decision was still valid.
A useful way to think about these mechanisms is:
Optimistic locking
→ Did the record change?
ETag / If-Match
→ Is the resource still the version I observed?
Read-set validation
→ Are the important inputs behind my decision still valid?
Idempotency
→ Am I accidentally executing the same action twice?
Invariant validation
→ Does this action still preserve the business constraint?
A production agent can need all of them.
The Model Shouldn't Be the Final Consistency Layer
This is probably the most important architectural lesson.
A language model is very good at interpreting information, planning, and choosing between possible actions.
It should not be expected to guarantee that the world hasn't changed since it made its decision.
That responsibility belongs in the surrounding system.
A safer architecture looks something like this:
┌───────────────┐
│ AI Agent │
│ │
│ Read + Reason │
└───────┬───────┘
│
Proposed Action
│
▼
┌─────────────────┐
│ Decision Layer │
│ │
│ Inputs │
│ Versions │
│ Preconditions │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Validator │
│ │
│ Freshness │
│ Concurrency │
│ Invariants │
└────────┬────────┘
│
┌───────┴───────┐
│ │
Invalid Valid
│ │
▼ ▼
Re-read Execute
Re-plan │
▼
External System
The agent proposes.
The deterministic execution layer verifies.
Only then does the action happen.
That separation becomes increasingly important as agents gain more authority.
What Happens When the State Changes?
A stale decision doesn't always mean the workflow should simply fail.
There are several possible responses.
If the change is minor, the system may refresh the affected input and continue.
If the change affects the core business decision, the agent may need to reconsider.
If the action is high-risk, the system may require human approval.
For example:
State unchanged
↓
Execute
State changed, low impact
↓
Refresh input
↓
Continue
State changed, decision-critical
↓
Re-plan
↓
Validate again
High-risk conflict
↓
Human approval
This gives the system a more useful response than simply saying:
VERSION_CONFLICT
The goal isn't just to detect that reality changed.
The goal is to respond appropriately when it does.
Building Agents for a World That Changes
I think one of the biggest differences between an AI demo and a production agent is how they treat time.
A demo often looks like this:
Question → Model → Tool → Answer
Production looks more like:
Read state
↓
Reason
↓
State changes
↓
Validate assumptions
↓
Re-read critical inputs
↓
Check permissions
↓
Check business invariants
↓
Execute
↓
Verify outcome
That additional complexity isn't accidental.
It is the price of allowing software to take actions in a world that continues moving while the software is thinking.
And this is why I don't think the next generation of reliable AI agents will be built only by improving prompts or increasing model intelligence.
They will be built by combining good reasoning with good systems engineering.
Final Thought
AI agents don't operate in a static database.
They operate in a world where users click buttons, services update records, payments settle, policies change, other agents take actions, and failures happen independently.
The agent can make the right decision at 10:00:01 and the wrong decision at 10:00:04 simply because the world changed in between.
That doesn't make the model useless.
It means we need to design the system around the model differently.
The agent should know what information it used.
The system should know which of those inputs matter.
Critical assumptions should be revalidated before consequential actions.
Shared business constraints should be protected.
And when reality changes, the system should be able to re-read, re-plan, or stop instead of blindly executing an old decision.
The question isn't only:
"Can the agent make the right decision?"
The more important production question is:
"Can the system make sure that the decision is still valid when the agent acts?"
That's the difference between an AI agent that works in a demo and one that can be trusted with real work.
Top comments (0)