Choose your SDK
What changes and what doesn't across the five adapters.
agentFast runs on five orchestration SDKs. The short answer to "which should I pick" is: the one you already know, because the part you're buying is identical across all five.
What doesn't change
Everything in the production layer. Durable execution, the approval gate,
guardrails, memory, tools, observability, evals, streaming — same code, same behaviour, same
configuration, whichever SDK is underneath. The flagship kill/resume property is tested
independently on two of them and the SupportAgent runs on all five against the same suite
(tests/test_support_matrix.py).
Switching is a config change:
sdk: crewai # langgraph | vanilla | crewai | claude_agent_sdk | openai_agents_sdk
What does change
Your orchestration code — deliberately. agentFast does not wrap your SDK in a house interface, so LangGraph code stays real LangGraph code. That's the trade: you write the SDK's own idioms, and the adapter translates its lifecycle into the seam.
| SDK | Best when | Notes |
|---|---|---|
| LangGraph | You want explicit graph state and native interrupt() | Deepest integration. Uses LangGraph's own checkpointer contract, backed by the agentFast store |
| Vanilla | You want no orchestration framework at all | A plain tool-use loop with the whole production layer around it. The reference durability implementation — the crash surface is entirely ours |
| CrewAI | You're modelling roles and hand-offs | Checkpoints at task level rather than per iteration, and no token-level streaming (see below) |
| Claude Agent SDK | You want Anthropic's own agent loop | Tools bridged as an MCP server; the SDK keeps its native loop |
| OpenAI Agents SDK | You're already on Runner/Agent | Real FunctionTool bridge, native Runner loop preserved |
The one real difference
Token-level streaming. Four of the five stream tokens as the model produces them. CrewAI's
kickoff() is a single synchronous call that returns only when the whole crew is done — there is
no seam to publish deltas from, and inventing one would mean reimplementing CrewAI's orchestration.
CrewAI is not silent, though. Tool calls, guardrails, memory and planning all stream from the runtime hooks, and the assistant text is backfilled from the model step. You get every event the others produce; the text simply arrives in one chunk per turn instead of character by character. See Streaming.
Adding a sixth
An adapter is roughly 200 lines. It translates one SDK's lifecycle into the seam:
ctx = await runtime.start_run(...)
req = await runtime.on_llm_call(ctx, req) # memory injection, PII redaction
res = await runtime.on_llm_result(ctx, req, res) # guardrails out, cost, tracing
result = await runtime.execute_tool(ctx, call) # policy → rate limit → HITL → trace
await runtime.checkpoint(ctx, snapshot) # after every iteration
snap = await runtime.resume(run_id) # after a restart
await runtime.end_run(ctx, "completed", ...)
That's the whole contract. The existing five under adapters/ are the reference — vanilla is the
clearest to read, because it owns its own loop and has nothing else going on.