Is GPT a Model or an Agent? A Code-Level Answer
6 min read
GPT is a model: a named family of trained weights you call by string identifier, such as gpt-4o or gpt-4-turbo. An agent is the software around that call — the code that decides when to call the model, with what prompt, and what to do with the answer. GPT itself has no control flow. It only returns output when something else invokes it.
Short answer: GPT is a model, not an agent
A model is a value: a string you pass to an API call, like model: "gpt-4o". An agent is the thing that constructs that call — deciding when to make it, what to put in it, and what to do with what comes back. GPT names a family of models. It is the noun in options.model, not the verb that runs a wave plan or decides what to build next.
This distinction is easiest to see in code rather than in the abstract, so the rest of this article works from a real file: DevPilot's wave-planner/models.ts, which exists specifically because someone conflated the two and it broke production.
What separates a model from an agent
A model is a versioned artifact with an ID — claude-sonnet-4-20250514, gpt-4o-2024-08-06. You request it. It has no memory of the last request and no opinion about the next one.
An agent is a system with control flow, memory, and tools, that invokes one or more models to get work done. DevPilot's orchestrator is an agent in this sense: it decides what to plan, calls a model to plan it, checks the result, and decides what happens next.
"GPT agent," as a phrase, conflates the artifact with the system that uses it. That's why the search intent behind "is GPT a model or agent" is usually answered badly — most results either dodge the question or answer it with marketing copy about "agentic GPT products" instead of naming the actual distinction: a model is what you call, an agent is what calls it.
Inside DevPilot's wave-planner/models.ts: model IDs as one constant
Here's the file, unedited:
/**
* Model IDs for the planning agents.
*
* These live in one place because they had been hardcoded at six call sites
* (`conductor.ts`, `conductor-graph.ts`, `generator.ts`, the reoptimize route,
* the wiki command, and a doc comment). When `claude-sonnet-4-20250514` was
* retired, every planning run failed with
*
* 404 not_found_error: model: claude-sonnet-4-20250514
*
* and the fix had to be applied six times. A retired model ID is a *when*, not
* an *if* — so the ID belongs in one constant that every caller reads.
*
* Resolution order at every call site: an explicit `options.model`, then the
* environment variable, then the constant. The env override is what lets a
* conductor drop the planner to a cheaper tier for a cost-sensitive run
* without a rebuild.
*/
This is the model/agent distinction as an incident report. Six call sites — conductor.ts, conductor-graph.ts, generator.ts, the reoptimize route, the wiki command, and a doc comment — each hardcoded the same string. When Anthropic retired claude-sonnet-4-20250514, every planning run started failing with the same 404. The agent code hadn't changed. The model it depended on had simply stopped existing.
The fix — replacing six string literals with one constant — is unglamorous, but it's the correct fix precisely because it separates what the agent does (plan, call, retry) from what it happens to be calling right now.
Resolution order: options.model, then env var, then constant
Every call site resolves the model the same way:
- An explicit
options.model, if the caller passed one. - An environment variable, if set.
- The constant, as the default.
The environment variable is not decorative. It exists so a conductor can drop the planner to a cheaper model tier for a cost-sensitive run without touching code or triggering a rebuild — a real operational lever, not a hypothetical one.
The consequence worth naming: this order keeps the agent's behavior swappable without touching the agent's code. The orchestrator doesn't know or care which model ID it's calling. That's the whole point of treating the model as config rather than as identity.
Why the wave planner defaults to Opus, not a cheaper tier
Next block in the same file:
/**
* The wave planner's default.
*
* Opus rather than a cheaper tier is a deliberate product call, not a reflex:
* DESIGN.md §1 argues the bottleneck is *planning throughput and quality*, not
* agent capacity. A plan is one call of at most `maxTokens` output that then
The reasoning in DESIGN.md §1 is that the constraint on a wave plan isn't how many agents you can run — it's whether the plan itself is any good, since a bad plan wastes every agent downstream of it. A plan is one call, bounded by maxTokens, so the cost of using a stronger model once is small next to the cost of a weak plan multiplied across a whole critical path of work.
That's a product decision, not a technical constraint — and the fact that it lives as a one-line default in a config file, overridable by an env var, is evidence of the model/agent split doing its job. Nobody has to touch orchestration logic to change which model plans. See how a plan gets scored before anything runs on it for what "quality" is actually checked against.
Clearing up the terminology: ChatGPT, GPT agents, generative vs agentic AI
ChatGPT is a product: a GPT model wrapped in tool-use, memory, and a chat loop. That wrapping — deciding when to search, when to run code, what to remember from three messages ago — is the agent part. The model inside it is still just a model.
A "GPT agent" or custom GPT is a configuration layered on top of a model: a system prompt, some tools, maybe a retrieval index. It is agent-shaped scaffolding around a model, not a new kind of model. Calling it an "agent" is accurate; calling it a "model" is not, and marketing copy that uses the terms interchangeably is the reason this question gets asked at all.
Generative AI describes what the model does: produce output from a prompt. Agentic AI describes the loop around it — a system that calls a model repeatedly, checks results, and decides what to do next. GPT is generative. Whether something is agentic depends entirely on the code wrapped around it, not on the model name.
Related reading
- How DevPilot's orchestrator differs from a single agent
- What a wave plan is and how model output feeds into one
- Why wave-based execution beats a task queue
- What happens when a plan hits the token ceiling
- Claude Code, the only agent currently wired into DevPilot
Worth being plain about that last one: DevPilot doesn't run GPT models today. Claude Code is the wired agent, and Anthropic's Claude family are the models in models.ts. The reasoning above — model as config, agent as control flow — applies identically to GPT; this codebase just doesn't call it.