Why Agent Frameworks Fail in Production
EverSwift Labs CEO & Founder

How high-level harnesses conceal state mutation
Agent frameworks do not eliminate the complexity of production software; they relocate it into layers where it is harder to inspect, measure, and control. When teams transition from an interactive prototype to a live workload, execution loops managed by autonomous agent harnesses routinely degrade under real traffic. The failure is structural. These frameworks wrap iterative model calls in abstractions that treat prompt construction, tool execution, and state persistence as internal implementation details rather than discrete system boundaries.
Most off-the-shelf agent frameworks provide a single unified interface: an input query goes in, an autonomous loop executes multiple model calls and tool invocations behind the scenes, and a final answer emerges. Behind this convenience lies an append-only memory model.
In a standard autonomous harness, every intermediate interaction is appended directly to the conversational context. When the model requests a tool invocation, the harness executes the external function, serializes the response, appends that payload to the message history, and immediately re-prompts the model.
This pattern assumes that context accumulation is free and that language models maintain consistent reasoning capabilities regardless of message volume. In production, both assumptions fail. By hiding the mechanics of how state is accumulated and modified between steps, agent runtimes prevent engineers from enforcing state boundaries. A tool failure does not return an isolated error to a deterministic handler; instead, it dumps a stack trace or an error payload directly into the prompt history, hoping the model will figure out how to recover on the next iteration.
Compounding token volume across retry steps
The direct consequence of append-only execution loops is quadratic token consumption. When an agent attempts a multi-step workflow, the payload sent over the wire expands with every tool execution.
Consider an agent tasked with fetching customer records, parsing log files, and updating an internal status. In step one, the harness sends the system prompt, tool definitions, and user instruction. In step two, the harness appends the model's tool call and the tool's raw output. If that tool returns an unpruned database record or a detailed JSON response, the prompt for step two grows substantially. By step three, the context includes the system instructions, the first tool call, the first response payload, the second tool call, and the second response payload.
Step 1: System Prompt + Tool Schemas + User Query
Step 2: [Step 1 Context] + Tool Call 1 + Raw Tool Output 1
Step 3: [Step 2 Context] + Tool Call 2 + Raw Tool Output 2
Step 4: [Step 3 Context] + Tool Call 3 + Raw Tool Output 3
If a tool fails and triggers a retry, the harness appends both the failed execution and the retry attempt to the context. Across a five-step interaction with two retries, the cumulative token count sent to the API provider is not the sum of distinct operations; it is the accumulation of all prior states across every step. The team pays for the transfer and processing of the entire operational history on every single step. What appeared cheap in single-turn evals becomes unsustainable at production scale.
Context pollution and silent cyclic loops
Compounding token volume is not merely a billing problem. It directly causes attention degradation and execution failure.
Language models do not process all tokens in a long prompt with uniform fidelity. When conversational history is flooded with thousands of tokens of raw schema definitions, verbose JSON objects, and prior stack traces, the model's ability to attend to the primary instruction degrades. We call this context pollution.
As irrelevant tool output fills the context window, models frequently enter cyclic loops. The model calls a search endpoint, receives an extensive response, fails to parse the specific field it requires due to attention degradation over the bloated context, and then issues the exact same tool call again on the next turn.
Because agent harnesses typically use naive loop termination conditions (such as a maximum iteration count rather than cycle detection), the agent will repeat this pattern until it exhausts its budget or hits a hard timeout. The harness reports a generic timeout error, obscuring the fact that context bloat caused the reasoning loop to collapse three turns earlier.
The observability barrier in black-box execution
Diagnosing production failures requires isolating whether an error originated from prompt drift, improper tool parameter generation, external network failure, or ambiguous instructions. Black-box agent frameworks make this separation difficult.
When an agent harness manages its own execution graph, production telemetry is often reduced to a single outer span. Logs capture when the agent started and when it failed, but inspecting the intermediate inputs, outputs, and token counts requires opting into heavy framework-specific tracing libraries that add runtime overhead and proprietary abstractions.
In a standard service architecture, an engineer debugging an issue looks at discrete function inputs, return values, and status codes:
def update_user_record(user_id: str, patch_data: dict) -> ServiceResponse:
# Step 1: Validate schema
validated_data = validate_patch_schema(patch_data)
# Step 2: Execute update
result = db_client.update(user_id, validated_data)
# Step 3: Return typed response
return ServiceResponse(status="success", data=result)
In an autonomous agent harness, those three distinct steps are merged into a continuous reasoning loop where the prompt, the execution logic, and the error recovery are bundled together. When an error occurs, the engineer cannot easily determine if the model generated an invalid parameter, if the tool schema description was ambiguous, or if an upstream API changed its response structure. Debugging becomes an exercise in post-hoc prompt archaeology rather than standard log analysis.
The case for dynamic routing and why it fails in production
The common defense of autonomous agent harnesses is flexibility. Proponents argue that static execution pipelines cannot handle open-ended tasks where the exact sequence of steps cannot be known in advance. In research environments or personal exploration tools, allowing a model to determine its own execution path dynamically can yield working results across unpredictable inputs.
This flexibility, however, becomes an operational liability under production requirements. Production systems demand bounded execution time, predictable cost ceilings, reproducible error states, and clear compliance boundaries. Giving an unconstrained execution loop the authority to dynamically route actions without hard intermediate state validation means system availability is tied directly to the probabilistic reliability of prompt following.
When an edge case occurs in a dynamic harness, the recovery strategy is simply to re-prompt the model and hope for convergence. In contrast, robust distributed systems require deterministic failure boundaries where every state transition is typed, validated, and explicitly controlled.
Replacing autonomous loops with explicit state machines
Closing the demo-to-production gap requires discarding autonomous execution wrappers in favor of explicit, deterministic state machines.
A production AI architecture should treat the language model as a discrete processing node within a broader system graph, not as the orchestrator of the graph itself. State should be managed in an external, deterministic data store, and context passed to the model must be explicitly filtered at every step.
| Architectural Component | Autonomous Agent Harness | Deterministic State Machine |
|---|---|---|
| Control Flow | Dynamic loop driven by model output | Explicit directed graph or finite state machine |
| State Management | Append-only conversational history | Typed application state with targeted updates |
| Context Handling | Unpruned accumulation of tool payloads | Strict extraction and context distillation per node |
| Error Recovery | Re-prompting model with error trace | Deterministic retry policies and fallback routes |
| Observability | Opaque single-span execution | Granular step-level tracing with typed inputs and outputs |
To implement this pattern, we enforce three architectural constraints:
First, isolate state transitions. Each node in the workflow receives only the specific data fields required for its task, rather than the entire conversational history. If a tool returns a massive JSON payload, a parsing function extracts the necessary scalar values before anything is passed to subsequent prompts.
Second, prune context proactively. Raw tool outputs should never be appended directly to the operational message history. Once a node completes its execution, its intermediate scratchpad data is dropped, preserving only the distilled output in the application state.
Third, define deterministic transitions. Routing decisions between system states should rely on typed schema validation and explicit business logic wherever possible. The model may classify intent or extract structured data, but deterministic application code determines which branch of the workflow executes next.
Moving complexity out of opaque framework abstractions and back into explicit system code makes production AI measurable, debuggable, and financially predictable.
Benchmarks reflect our own workload and configuration; your results will differ.
Get the next one first.
New writing on AI systems, distribution and building solo. No spam, unsubscribe in one click.
