The Case for Shipping an AI Agent With No Framework
Search for AI agent framework and you get rankings. Ten frameworks, eight frameworks, seven frameworks, each list sorted into best for prototyping, best for production, best for multi agent. The lists are competent and they mostly agree with each other, which is the first clue that none of them is answering a hard question.
The hard question is not which one. It is whether a framework is the thing standing between you and a working agent, and in most of the builds we are asked to look at, it is not. The agent is a loop. The loop is about a page of code. What is missing is usually a decision nobody made about what the thing may touch and when it must stop, and no framework supplies that.
What a production agent actually contains
Strip the vocabulary away and the shape is small and dull. There is a loop that sends a request and reads the reply. There is a registry mapping tool names to functions, with a schema for each one. There is a retry policy for the calls that fail in the ordinary ways. There is a transcript, because you cannot debug what you did not record. And there is a ceiling on rounds and tokens, because the thing choosing how many steps to take is the thing you are paying for.
That is the whole machine for a large class of useful agents. You own every line of it, you can read the request that went out, and when it misbehaves the stack trace is yours. The two properties that actually decide the difficulty of the build, what the agent may touch and how long it runs, are the subject of the sort worth having for agent shapes, and both are decisions you make in your own code regardless of what you install.
What a framework genuinely gives you
This is the part the comparison posts rush, because the honest list is shorter than a feature matrix and more useful. Graph or state machine structure, so a branching workflow is declared rather than nested. Tool schema generation from your function signatures. Streaming and event plumbing. Conventions for handing work between several agents. Tracing hooks. Adapters to whichever vector store, model provider and queue you already run.
Most of those are conveniences. You would write each of them in an afternoon and yours would fit your system better. One item on the list is different, and it is durable state.
LangGraph's persistence documentation is the clearest statement of what that means in practice: a checkpointer persists a thread's graph state as checkpoints, and the use cases it names are human in the loop workflows, time travel and fault tolerance, for agents that have to resume after an interruption or recover from a failure. That is not an afternoon of work. A correct checkpoint store, a deterministic replay path and a resume that does not duplicate a side effect is a genuinely hard piece of engineering, and it is the one thing on the list worth adopting someone else's version of.
The cost the comparisons do not price
A framework sits between you and the model's request and response, and that position is exactly where the cost lands.
Debugging goes through its layers. The question you need answered at two in the morning is what text and what tool schemas were actually sent, and an abstraction whose job is to assemble that for you is an abstraction you now have to read backwards. When a tool result or an attachment arrives in the payload as an encoded blob, a base64 decode on the captured request is often the fastest way to see what the model was really given rather than what you believe it was given.
Token accounting gets harder in the same way. The framework decides what history to resend and how to summarise it, so the number you are billed for is produced by code you did not write. Count a realistic late step with the token counter and put it through the LLM cost calculator at your expected step count, because a loop that re-sends its whole context every round costs a multiple of the first call and that multiple is set by the library's choices, not yours.
Then there is churn. These projects move quickly, their interfaces change, and a breaking change in your orchestration layer is a change to the heart of the product rather than to its edge. That is an acceptable trade for durable state. It is a poor trade for tool schema generation.
The two situations that justify one
Both of these are real, and if you are in one of them, adopt a framework and stop reading comparison posts.
The run has to survive a restart. If a single piece of work spans minutes or hours, pauses for a human to approve something, and must pick up where it left off after a crash or a deploy, you need persistence, replay and resume. That is the capability described above, it is hard to build correctly, and a framework that treats it as the centre of its design rather than as a plugin is worth the dependency.
More than a couple of engineers work on the same agent. A shared vocabulary has real value once there are enough people to disagree about structure. A framework supplies naming, a place to put each thing, and a pattern for review that nobody has to invent. The flexibility you give up buys consistency, which is the right way round at that size and the wrong way round for one engineer and one agent.
Notice what is not on this list. Multi agent coordination is not a reason by itself, because several agents are a context trade rather than an upgrade. Nor is wanting tracing, which you can have from an observability tool without handing over control of the loop.
Decide these before you shop
Each of these changes the answer, and none of them is a question a framework answers for you.
- Enumerate the write tools. Every action that changes something outside the agent, listed, with a decision for each on whether it fires unattended, needs confirmation, or may only be proposed. This is the liability decision and it is yours.
- Set the ceiling. Maximum rounds, maximum total tokens, and the behaviour at the limit. An open ended loop without one is an unbounded invoice.
- Decide whether a run must survive a restart. This is the question that actually selects for or against a framework, so answer it explicitly rather than discovering it in production.
- Decide who owns the prompt text. If the answer is the library, you have accepted that the most important string in the system is assembled out of your sight.
- Decide how you will know it works. A scored set of real cases, which is the subject of how to evaluate an LLM system, tells you whether a change helped. Without it, every framework migration is a matter of taste.
How we build them
Our own production agents are the small machine described at the top: a loop we own, a tool registry, a retry policy, a transcript of every call, and hard ceilings set by us. Approval gates on write tools are plain code in the loop, because that is where we want them visible during review.
We removed a framework from a client build, and the thing that improved was not speed. It was that the exact request going to the model became readable in one file, so a wrong answer stopped being a search through layers and became a diff. The cases where we would keep one are the two above, and when a client's workflow needs durable resumable state we reach for a library rather than writing a checkpoint store from nothing.
The honest version of this whole category, which the ranked lists cannot print because they exist to rank, is that the framework question is downstream of the design questions. If you cannot say which of the two situations you are in, you are not choosing a framework, you are adopting a default and inheriting its opinions. Working out what you are building first is the cheaper order, and it is roughly what our approach to custom builds comes down to.