The framework landscape, 2026

Chapter 49 read Strands as one framework and found it thin conveniences over the Part 1 loop. That verdict generalizes, and this part cashes it across the whole crowded 2026 landscape. A reader today faces a dozen credible agent frameworks and needs two things this chapter provides: a map (which frameworks exist, what each is actually good at) and a reduction (the three orchestration idioms they all package, so the map stops being intimidating). The framework you pick is the worker's inside; the platform this book builds wraps any of them, which is why the choice is real but not load-bearing.

Because this is the fastest-moving corner of the field, treat specific version claims as of mid-2026 and verify current status before adopting; the shapes below are stable even as the names churn.

Lab 13.1: three idioms, one loop

The frameworks look different and reduce to three control-flow shapes. The lab implements all three over one trivial agent primitive, running the same investigate-then-report task:

python3 framework_idioms.py
same task, three orchestration idioms:

--- CREW  (role pipeline, CrewAI-style) ---
  path: researcher (gather) -> analyst (reason) -> writer (report)
  out : REPORT: v841 shrank the payments-db pool; roll back.

--- GRAPH (state machine, LangGraph-style) ---
  path: researcher (gather) -> analyst (reason) -> researcher (gather) -> analyst (reason) -> writer (report)
  out : REPORT: v841 shrank the payments-db pool; roll back.

--- HANDOFF (delegation, OpenAI-Agents-style) ---
  path: triage: handoff to analyst (it's a performance incident) -> researcher (gather) -> analyst (reason)
  out : why did checkout latency spike overnight? | facts: p99 14x a

The three shapes, and what packages each:

  • The crew is a role-based sequential pipeline: agents with a role and goal run in order, each consuming the last one's output. It is the "assembly line of specialists" mental model, easy to reason about, and it is the pipeline topology with nicer ergonomics.
  • The graph is a state machine: nodes are steps over a shared state, edges route between them, and a conditional edge can loop (the lab's graph runs research-then-analyze twice before writing). It is the most general shape, a pipeline generalized to a DAG with cycles, and it is what you reach for when control flow is genuinely branchy.
  • The handoff is delegation by transfer: an agent inspects the request and hands control to a more specialized agent. It is hierarchical delegation expressed as a transfer rather than a call, and the lightest way to route among specialists.

Every framework below is one or more of these idioms plus persistence, tracing, tool ergonomics, and a provider story. Seeing that is the antidote to framework anxiety: you are choosing a packaging, not a capability.

The map

The frameworks, grouped by the idiom they lead with and what they are actually best at.

Graph-first (control you can see):

  • LangGraph is the graph idiom as a first-class product: a StateGraph of nodes and edges, with checkpointers for durable state and human-in-the-loop pauses, from the LangChain team but usable without the rest of LangChain. It is the default when you want explicit, inspectable control flow and is the most widely deployed agent-orchestration layer in 2026. LangChain proper is the older, higher-level chains-and-integrations library; LangGraph is where serious agent control flow now lives.
  • LlamaIndex Workflows express the same idea as event-driven steps (a step emits an event, another handles it), with LlamaIndex's strength in data and retrieval underneath, the natural pick when the agent is retrieval-heavy.

Crew-first (teams of roles):

  • CrewAI leads with the role-based crew (agents have a role, goal, and backstory; a Crew runs them under a sequential or hierarchical Process, with Flows for event-driven control). It is the fastest way to stand up a "team of specialists" and is popular for exactly that, standalone, no LangChain dependency.

Handoff-first (routing among agents):

  • The OpenAI Agents SDK is the production successor to the experimental Swarm: Agents, handoffs, guardrails, sessions, and built-in tracing, built on the Responses API. It is lightweight and handoff-centric, the natural pick in an OpenAI-model shop.
  • Strands (Chapter 49) ships all four primitives (agents-as-tools, swarm, graph, workflow), AWS-native and model-agnostic, which is why this book used it as the exemplar.

Typed and programmatic (structure over prompts):

  • Pydantic AI brings typed agents, dependency injection, and validated structured outputs from the Pydantic team, the pick when you want type safety and testability as first-class concerns.
  • DSPy is a different philosophy entirely: programming, not prompting. You declare Signatures (typed input/output specs) and compose Modules, then an optimizer compiles them, tuning the prompts (or few-shot demonstrations, or weights) against a metric on your data, MIPRO-style Bayesian search or the newer reflective optimizers that learn from execution traces. Reach for it when you want your prompts optimized against evals rather than hand-written, which connects directly to Part 9's eval discipline.

Enterprise and platform-aligned:

  • Google ADK (Agent Development Kit) is Google's open-source framework, agents plus tools plus multi-agent, with first-class A2A support and a deployment path to Vertex AI Agent Engine, the Google-cloud analog of Strands-plus-AgentCore.
  • The Microsoft Agent Framework is the convergence of Microsoft's AutoGen (which pioneered conversable agents and GroupChat) and Semantic Kernel (its enterprise plugins-and-planners SDK) into one line; the community-forked AG2 continues the original AutoGen lineage separately. If you are in the Microsoft/.NET world, this is the native path.
  • The Anthropic Claude Agent SDK (formerly the Claude Code SDK) is Claude Code packaged as a library: a query() loop with built-in file, bash, and web tools, subagents, MCP, and hooks, the pick when you want a batteries-included coding/filesystem agent on your own infrastructure.

Minimal and code-first:

  • smolagents (HuggingFace) is deliberately tiny and leads with a CodeAgent whose action is writing Python code, the code-as-action pattern, minimal surface, easy to read end to end.
  • Mastra brings a TypeScript-native framework (agents, workflows, RAG, evals) to the JS ecosystem, the pick when your stack is Node, not Python.

Choosing, practically

The decision is less about features than about fit, and four questions settle most of it:

QuestionPoints you toward
Control flow branchy and worth seeing?LangGraph (graph), or Strands/ADK graph
A team of role-specialists, fast?CrewAI (crew)
Routing among specialists, OpenAI models?OpenAI Agents SDK (handoff)
Type safety and validated outputs central?Pydantic AI
Want prompts optimized against evals, not written?DSPy
AWS-native, model-agnostic, all idioms?Strands (Part 3)
Google Cloud / Vertex?Google ADK
Microsoft / .NET?Microsoft Agent Framework
Batteries-included coding agent on your infra?Claude Agent SDK
TypeScript stack?Mastra

Two meta-points keep the choice in proportion. First, team familiarity often dominates: a framework your team can read and debug beats a marginally better-fit one they cannot, because the framework is where the 3 a.m. debugging happens. Second, and this is the book's recurring posture, the framework is swappable behind the platform. Whichever you pick runs as the worker's inner loop inside Hive's scheduler, budget ledger, governor, sandbox, and verification mesh, none of which any framework supplies. A migration between frameworks is days of adapter work on the worker; the platform around it does not move.

Don't be confused: framework vs the model's own agent SDK. Some "frameworks" are vendor SDKs for that vendor's models (OpenAI Agents SDK, Claude Agent SDK, Google ADK) and some are model-agnostic orchestrators (LangGraph, CrewAI, Pydantic AI, Strands). The distinction matters for lock-in: a vendor SDK is the smoothest path to that vendor's newest features the day they ship, at the cost of a harder model swap later; a model-agnostic framework trades a little feature latency for provider portability. Neither is wrong; know which you are picking, because "we adopted the OpenAI Agents SDK and now want to run Claude" is a migration, not a config change.

Full source

"""Lab 13.1: three framework idioms, one loop underneath.

The 2026 framework landscape looks crowded, but the multi-agent
orchestration patterns reduce to three shapes, and every framework
packages one or more of them:

  - the CREW (role-based pipeline): agents with a role and goal run in
    sequence, each consuming the last one's output. CrewAI's Crew, and
    the "assembly line of specialists" pattern.
  - the GRAPH (state machine): nodes are steps over a shared state, and
    edges route between them, including CONDITIONAL edges that loop.
    LangGraph's StateGraph, LlamaIndex Workflows, Strands' graph.
  - the HANDOFF (delegation by transfer): an agent decides to hand
    control to a more specialized agent. The OpenAI Agents SDK's
    handoffs, Strands' swarm, agents-as-tools.

This lab implements all three as ~15-line patterns over one trivial
Agent primitive, running the same investigate-then-report task, so the
shared substrate is visible. The real frameworks add ergonomics,
persistence, and tracing; the control-flow idea is what you see here.

Standard library only. Deterministic.
"""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass
class Agent:
    """The atom every idiom composes: a named step that transforms text."""
    name: str
    role: str
    fn: callable

    def run(self, text: str) -> str:
        return self.fn(text)


TRACE: list[str] = []


def step(agent: Agent, text: str) -> str:
    out = agent.run(text)
    TRACE.append(f"{agent.name} ({agent.role})")
    return out


# --- three trivial specialists, shared by all three idioms -----------------

researcher = Agent("researcher", "gather", lambda t:
                   f"{t} | facts: p99 14x at 02:00, deploy v841 at 01:55")
analyst = Agent("analyst", "reason", lambda t:
                f"{t} | cause: v841 shrank the db pool")
writer = Agent("writer", "report", lambda t:
               "REPORT: v841 shrank the payments-db pool; roll back.")


# --- idiom 1: the CREW (role-based sequential pipeline) ---------------------

def crew(agents: list[Agent], task: str) -> str:
    """CrewAI-style: a Process runs role agents in sequence, output ->
    input, until the last produces the deliverable."""
    text = task
    for a in agents:
        text = step(a, text)
    return text


# --- idiom 2: the GRAPH (state machine with a conditional edge) -------------

def graph(task: str) -> str:
    """LangGraph-style: nodes mutate a shared state; edges route, and a
    conditional edge loops back until a gate passes."""
    state = {"task": task, "text": task, "passes": 0}

    def research_node(s): s["text"] = step(researcher, s["text"]); return s
    def analyze_node(s):  s["text"] = step(analyst, s["text"]); s["passes"] += 1; return s
    def write_node(s):    s["text"] = step(writer, s["text"]); return s

    node = "research"
    while node != "END":
        if node == "research":
            state = research_node(state); node = "analyze"
        elif node == "analyze":
            state = analyze_node(state)
            # conditional edge: loop once for a second look, then proceed
            node = "research" if state["passes"] < 2 else "write"
        elif node == "write":
            state = write_node(state); node = "END"
    return state["text"]


# --- idiom 3: the HANDOFF (delegation by transfer of control) ---------------

@dataclass
class Handoff:
    to: Agent
    reason: str

def triage(task: str) -> str:
    """OpenAI-Agents-style: a triage agent inspects the request and hands
    off to the specialist best suited, which then runs."""
    if "latency" in task or "spike" in task:
        h = Handoff(analyst, "it's a performance incident")
    else:
        h = Handoff(writer, "general request")
    TRACE.append(f"triage: handoff to {h.to.name} ({h.reason})")
    facts = step(researcher, task)          # gather first
    return step(h.to, facts)


def show(title: str, result: str) -> None:
    print(f"--- {title} ---")
    print("  path: " + " -> ".join(TRACE))
    print(f"  out : {result[:60]}")
    TRACE.clear()
    print()


if __name__ == "__main__":
    task = "why did checkout latency spike overnight?"
    print("same task, three orchestration idioms:\n")
    show("CREW  (role pipeline, CrewAI-style)", crew([researcher, analyst, writer], task))
    show("GRAPH (state machine, LangGraph-style)", graph(task))
    show("HANDOFF (delegation, OpenAI-Agents-style)", triage(task))

    print("all three are control flow over the same primitive: a named step "
          "that transforms state. A framework is a packaging of one or more "
          "of these idioms plus persistence, tracing, and tool ergonomics.")
    print("the platform this book builds (budget, isolation, verification, "
          "scheduling) wraps ANY of them; the idiom is the worker's inside.")

👉 Next: durable execution and the runtime spectrum, where the frameworks meet the engines that keep a long-running agent alive across crashes, Temporal, Restate, DBOS, and where they sit between a free loop and Step Functions.