Why Some Teams Build a Custom Agent Runtime Instead of Adopting a Framework
A custom agent runtime is your own control plane: you own the model call, tool dispatch, stop rules, and run metadata without LangGraph, CrewAI, Microsoft Agent Framework, or a provider Agents SDK sitting in the middle.
Some teams pick that path deliberately.
This page explains when that decision is rational and when it is not-invented-here dressed up as architecture.
Hand-rolled runtimes win when orchestration needs are small, dependency and compliance constraints are hard, or framework abstractions fight your product shape.
Insight: Frameworks speed demos and multi-pattern orchestration; they also add upgrade risk, opaque magic, and lock-in around tools and state.
Key Concepts:agent runtime, thin loop, control plane, dependency budget, undifferentiated heavy lifting, escape hatch.
When to Use: Writing an ADR, reviewing a POC that "just used LangGraph," or deciding whether a provider Agents SDK is already enough without a third-party graph library.
Limitations/Trade-offs: Custom still uses HTTP clients, queues, and OpenTelemetry. Framework-free means you own the loop, not that you invent TLS or JSON Schema.
Related Topics: framework-free trade-offs, ReAct loops as APIs, eval harnesses, when to abandon the custom runtime.
An agent runtime is the host that repeatedly: builds context, calls a model, executes tools, appends observations, enforces budgets, and emits a final result or stop reason.
A framework-based runtime borrows that host from a library (LangGraph graphs, CrewAI crews, OpenAI Agents Runner, Pydantic AI agents, and peers).
A custom runtime keeps those semantics in your service code, usually as a bounded loop behind FastAPI, Express, a worker, or a CLI.
Framework path: App -> Framework control plane -> Model + tools -> Framework state helpersCustom path: App owns loop -> Model client -> Tool plugins -> Your store + traces
Neither path is "no dependencies."
The question is who owns orchestration truth: stop enums, tool allowlists, retries, and what a "run" means in your product.
Teams that build custom usually share at least one pressure:
Simple loop shape - tools + budgets, not durable multi-day graphs with joins.
Strict observability contracts - every field must land in their warehouse schema, not a vendor UI alone.
Product is the runtime - they sell agent hosting and cannot ship a third-party control plane as the core.
Framework mismatch - they tried a framework and spent more time fighting its graph model than shipping tools.
Analogy: buying a workflow engine versus writing a state machine for one process.
One process with clear stop rules can be a 150-line loop.
Twenty processes with pause/resume and visual ops usually want a platform.
Custom is not automatically more portable than a framework.
Portability comes from pure tools, versioned schemas, and prompts as data.
A custom loop that embeds business rules only in prompt strings is still a trap.
Cost of adopting one new model provider (should be small)
Hours spent reimplementing features frameworks already ship
If provider swaps are cheap and stop-related incidents stay near zero, custom is earning rent.
If you spend every sprint on generic orchestration, migrate.
Is a custom runtime the same as calling Chat Completions in a while-loop?
Often yes at the core. Production custom runtimes add budgets, structured stop reasons, tool plugins, auth context, tracing, and an HTTP or worker API around that loop.
How is this different from OpenAI Agents SDK or Anthropic tool loops?
Provider kits give official agent objects, handoffs, guardrails, or Messages tool primitives. Custom means you own those pieces (or deliberately omit them) without adopting that kit as your control plane.
When is LangGraph still the better default?
When durable resume, complex branching, joins, or multi-path HITL are first-class product needs. Reimplementing those well is expensive.
Can we use OpenRouter inside a custom runtime?
Yes. Treat OpenRouter (or any gateway) as the model client behind your loop. Routing is orthogonal to who owns the agent loop.
What should never live only in the runtime module?
Business invariants, authorization, durable customer records, and tool implementations other services must call. Keep those in domain modules with independent tests.
How big should a "thin" custom loop stay?
If the loop file grows past a few hundred lines of generic orchestration (graphs, joins, DSL parsers), you are inventing a framework. Extract only product-specific policy or switch stacks.
Do custom runtimes support streaming UIs?
Yes. Stream token or event chunks from the host; keep privileged tools on the server. UI SDKs (for example Vercel AI SDK) can present streams without owning the runtime.
Is MCP compatible with custom runtimes?
Yes. MCP is a tool-connection protocol. Your dispatcher can list MCP tools, translate schemas, and call MCP clients like any other plugin source.
What belongs in the ADR when we choose custom?
Drivers (compliance, simplicity, platform product), alternatives considered, stop/budget requirements, ownership, eval plan, and explicit exit triggers that force migration.
How do we prevent each team from forking a private loop?
Publish an internal thin runtime package with plugin interfaces, or standardize on one framework. Custom without a shared platform multiplies operational cost.
Are evals optional if the loop is small?
No. Small loops still change prompts, models, and tools. Gold-set evals and CI gates are how you know the custom path still works after edits.
When should we abandon custom for a maintained framework?
When roadmap needs outgrow the loop, staffing cannot own reliability, or you are reimplementing generic orchestration weekly. Use the decision cheatsheet in this section.