How to choose an AI SDK: why the fear of lock-in is a mistake and how to decide based on your app's format
The fear of being locked into an AI provider is a common mistake. Learn how to choose the best SDK based on your project's format and productivity.

For a long time, I did the math wrong in my head. When I was about to start an app with AI, I would discard the Claude Agent SDK and the Vercel AI SDK almost out of reflex — “those must be tied to their provider,” I thought. Then I would go with Genkit, thinking it was the only “free” option. Until the day the Ollama launcher showed me, right to my face, that I was wrong the whole time.
The premise that cost me dearly
The question that stalled everything was: “what if one day I want to switch models? Will I be held hostage by Anthropic / Vercel?”. With that fear, the selection criteria became the vendor — and not the problem I was solving. It is a silent error, because it looks like prudence.
Ollama toppled the premise
I ran the ollama launch and the menu offered me, among other things, “Launch Claude Code (minimax-m3:cloud)” and “Launch OpenCode”. Notice the detail: it was about to run Claude Code — which uses the Claude Agent SDK under the hood — with a minimax-m3 model, which is not from Anthropic. How? It just swaps the base URL pointer.
# É só isto que o launcher do Ollama faz por baixo dos panos:export ANTHROPIC_BASE_URL=http://localhost:11434 # endpoint Anthropic-compat do Ollamaexport ANTHROPIC_MODEL=minimax-m3:cloudclaude # Claude Code rodando um modelo NÃO-Claude, com subagentes e tudoZero code. A third-party model running under the Claude Code harness, just because Ollama exposes an endpoint compatible with the Anthropic protocol. If this applies to Claude Code, it applies to any app built on the same SDK.
The agnostic layer exists in all of them
After that realization, I went to look at the others. They all have the same fundamental mechanism: pointing to an endpoint + a normalization layer. Only the name changes:
SDK | How it points to another provider | Normalizes to |
|---|---|---|
claude-agent-sdk | ANTHROPIC_BASE_URL (endpoint that speaks the Anthropic protocol); Bedrock/Vertex backends | shape Anthropic Messages |
Vercel AI SDK | gateway() / createGateway({baseURL}) / createOpenAICompatible({baseURL}) | neutral contract (text/tool-call/reasoning) |
Genkit | provider plugins (googleAI, openai-compat…) | Genkit model abstraction |
O vendor não é critério. A escolha real é qual batteries-included bate com o formato do app.
— a régua que eu deveria ter usado desde o começo
The four SDKs and what each one already brings ready-made
They are not at the same level of abstraction — and that is exactly what decides the choice. It is not “which one is freer” (all are), but rather “which one already solves the type of app I am building”.
claude-agent-sdk — “Claude Code as a library”
It is a complete agent harness. It gives you the five most annoying primitives to build for free: tool hooks (deterministic guard), human-in-the-loop approval, AskUserQuestion as a protocol, subagents via native tool, and in-process MCP. Fits: autonomous code agent with security and approval.
Genkit — GenAI app framework
Strong in first-class RAG (retriever/indexer/embedder), orchestration flows, and OpenTelemetry observability with dev UI. Fits: content generation, RAG, pipelines, tracing. It was the right choice for my editorial — just for the wrong reason at the time.
pydantic-ai — typed agents in Python
Typed agents and tools, structured output validated by Pydantic, multi-agent. Lightweight, no ready-made RAG/flows/observability. Fits: typed agent logic. I tested it for the editorial and it didn't fit — it is a battery for another format.
Vercel AI SDK — model + tools + provider (+ UI)
streamTextAI SDK CoregenerateText with neutral parts, ToolLoopAgentthe best agnostic provider story, and UI hooks (useChat). Fits: truly provider-neutral (first-class OpenAI-native) + React UI integration.
Capability (comes ready?) | claude-agent-sdk | Vercel AI SDK | Genkit | pydantic-ai |
|---|---|---|---|---|
Agent loop | ✓ | ✓ | ✓ | ✓ |
Guard / tool hooks | ✓ native | — | — | — |
Human approval | ✓ | — | — | — |
Native subagents | ✓ | — | — | ~ |
First-class RAG | — | — | ✓✓ | — |
Observability / tracing | basic | basic | ✓ OTel | basic |
UI hooks (useChat) | — | ✓ | — | — |
Provider agnostic | via base-URL | ✓ neutral | ✓ plugins | ✓ |
The decision ruler
After all this, the question I ask today, before any line of code, is just one: what is the app format?
“Which SDK locks me in the least to the vendor?”
Result: I chose out of fear, not because of the problem.
“Which batteries-included matches the app format?”
Result: the tool that already solves the right type of problem.
Autonomous code agent (security, approval, subagents) → claude-agent-sdk
Content + RAG + flows + observability → Genkit
Typed agent logic (Python) → pydantic-ai
Truly provider-neutral + UI → Vercel AI SDK
Bonus: the settingSources trap
A technical surprise worth keeping in mind, if you use claude-agent-sdk: your agent inherits the Claude Code environment (hooks, plugins, CLAUDE.md, permission rules) even if it is not Claude Code. And it is not because of the API key — it is the default of settingSourcessettingSources
// sdk.d.ts: "When omitted, all sources are loaded (matches CLI defaults).// Pass [] to disable filesystem settings (SDK isolation mode)."buildAgentOptions({ // settingSources OMITIDO -> carrega ~/.claude inteiro (hooks/CLAUDE.md/perms) settingSources: [], // <- modo ISOLAMENTO: só as regras do teu app}), not the key. Make it explicit — accidental inheritance is debt.
What I take to every new projectVendor is not a criterion. Every agent SDK is agnostic via base-URL / gateway / plugins.
Decide by the app format, not by the brand or fear of lock-in.
Batteries-included > theoretical agnosticism. What comes ready-made saves weeks.Testing and migrating without guilt is correct selection, not an error (that's how I went from pydantic-ai to Genkit in a content app).
What is reusable is UI/infra, not the engine: extract chat, streaming, and retriever into a common package.
The good part about discovering you were wrong is that the remaining ruler is simple and reusable.Not “which SDK is the best” — but rather
which SDK is the best for this app. app.


