Week 5 · Day 23 of 40 · Build AI

Agent frameworks: LangGraph.js, Claude Agent SDK, OpenAI Agents SDK

You ship: The Day 11 research agent on 3 frameworks, measured on 5 topics, with a one-page comparison. Builds on: Day 11, Day 12.

By the end of the day you can

  • Build the same agent on 3 frameworks and explain where each one earns its abstractions
  • Model an agent as an explicit graph with typed state, checkpoints and conditional edges
  • Choose a framework for a given problem with a written tradeoff

Topics

What a Framework Adds

  • The baseline: the Day 11 loop (messages, tool registry, stop conditions). A framework is that loop plus opinions
  • Where frameworks differ: state representation, control flow, handoffs between agents, built-in tracing
  • Cost: lock-in to the framework's state shape, upgrade churn, debugging through its abstractions
  • Benefit: checkpointing, retries, tracing and handoffs you do not write yourself
  • When to skip the framework: a single-agent tool loop under 200 lines

LangGraph.js

  • Graphs: nodes are functions over typed state, edges are static or conditional, the graph compiles to a runnable
  • State: an annotated schema with reducers (append messages, overwrite fields)
  • Checkpointers: persist state per thread, resume after failure, rewind to an earlier step
  • Human-in-the-loop: interrupt before a node, inspect state, resume with edits
  • Streaming modes: values, updates, messages; which one a UI needs

Claude Agent SDK

  • Agent definition: instructions, tools, model; built-in tool loop and context management
  • Sub-agents and handoffs; permissions and hooks around tool execution
  • Position: the machinery Claude Code runs on, exposed as a library

OpenAI Agents SDK (TypeScript)

  • Primitives: agents, tools, handoffs, guardrails
  • Runner: executes an agent until a final output, with a maximum number of turns
  • Tracing: built-in traces of every run, exportable to other backends
  • Sessions: conversation memory across runs

Choosing

  • Explicit graph, retries at specific steps, resumable long runs: LangGraph.js
  • Anthropic models, parity with Claude Code tooling, hooks and permissions: Claude Agent SDK
  • OpenAI models, handoff-heavy designs, guardrails as code: OpenAI Agents SDK
  • Record the choice in the architecture document with the 2 reasons that decided it

Build

Features shipped today

  • WorkBot: The investigation agent rebuilt on all 3 frameworks and run on the 5 test topics

Build the Day 11 research agent 3 times, once per framework, with identical tools (web_search, read_url, save_finding, write_report) and the same system prompt:

  1. LangGraph.js: a plan node, a research loop with a conditional edge back to itself, a write node, and a checkpointer so a run resumes after the process is killed mid-research
  2. Claude Agent SDK: one agent with the 4 tools and a hook that logs every tool call
  3. OpenAI Agents SDK: a Researcher agent that hands off to a Writer agent, with a guardrail that rejects topics outside an allowed list
  4. Run all 3 on the same 5 topics; record wall time, tokens, cost and tool calls per run
  5. Write a one-page comparison: what each framework made easier, what it made harder, which one your products will use

Walkthrough

The same research agent 3 times: on LangGraph.js, on the Claude Agent SDK and on the OpenAI Agents SDK, with one set of 4 tools and one system prompt, run on the same 5 topics. A framework is the Day 12 loop plus opinions: compare what each adds to that loop, then record the choice.

Part 1

One tool set, three loops

The tools and the prompt are defined once, as plain functions; each framework wraps them.

Study first

  • What a framework adds to the loop you wrote on Day 12: a shape for state, control flow you can draw, handoffs between agents, tracing and checkpoints you do not write. What it costs: its state shape in your code, upgrade churn, debugging through its abstractions.
  • The 4 tools: web_search, read_url, save_finding, write_report. Search is the web when BRAVE_SEARCH_KEY is set and WorkBot's knowledge base (Day 9, under content/knowledge/) otherwise, and read_url reads either kind of address. Findings and the report go to a notebook, a small object the run owns, which each framework's own state reads from, so the two stay in step.
  • When to skip the framework: a single-agent tool loop the size of Day 12's, 90 lines. Day 15's analyst runs on it and stays as it is.

Build

  1. src/lib/frameworks/tools.ts: the 4 tools from Day 12's tool() helper, with a Notebook of findings and the report; knowledge://section/slug as the address of a document.
  2. src/lib/frameworks/researcher.ts: the instructions, the step limit, the result shape every framework returns: report, findings, tool calls, usage, dollars, milliseconds.
  3. content/eval/topics.json: the 5 questions, all answerable from the knowledge base.

Done when

  • The tools' tests pass: a search with no key finds a document and gives an address read_url reads; a finding and a report land in the notebook.
Technical notes
  • read_url returns a document with its title as the heading, which is what the model then cites as the source.
  • One costOf, with the prices per model from src/lib/providers.ts (Day 19), prices every framework's usage, so the dollars column compares like with like; the Claude Agent SDK also reports its own total, a check on the arithmetic.

Part 2

LangGraph.js: the agent as a graph

The loop becomes a graph: nodes are functions over a typed state, edges are static or conditional, and the compiled graph runs with a checkpointer that saves the state after every node.

Study first

  • State as an annotated schema: a reducer per field (append messages, append findings, add usage) and a default, so a node returns only the part it changed.
  • Nodes and edges: plan once, research with the tools bound, a conditional edge from research to the tools while the model calls them and to write when it stops, write with only write_report bound and tool_choice set to it.
  • Checkpoints: a thread id in the run's config, { configurable: { thread_id } }, the state saved after each node, invoke(null, config) to continue from the last saved node. getState(config) shows where a thread stopped.
  • Streaming modes, for a UI: values is the whole state after each node, updates only the change, messages the tokens.

Build

  1. src/lib/frameworks/langgraph.ts: the state with reducers, the 4 nodes, the conditional edge, compile({ checkpointer }); PostgresSaver from @langchain/langgraph-checkpoint-postgres when DATABASE_URL is a Postgres address, MemorySaver otherwise.
  2. The tools node runs the shared tools against a notebook of its own and returns what they saved as state, so a resumed run has its findings.
  3. A test that scripts the model: the second research call throws as a killed process would; getState shows the findings saved before it; invoke(null) finishes the run without redoing them.

Done when

  • The graph test passes, and pnpm frameworks --only langgraph --topic refunds writes a sourced report; killed mid-run and started again with the same --topic and --thread <id>, it continues from the last node the checkpointer saved, without redoing the findings.
The research graph
The compiled graph as LangGraph draws it: plan, then research, whose conditional edge goes to the tools while the model calls them and to write when it stops.
Technical notes
  • A state field and a node cannot share a name; the plan is folded into the question's message rather than kept as a field called plan.
  • A conversation that ends on the assistant's turn reads to the API as a prefill, which Haiku 5.5 refuses, so the plan node ends with a user message.
  • The Postgres checkpointer makes its own tables on setup(); in tests MemorySaver keeps the same interface.

Part 3

The Claude Agent SDK and the OpenAI Agents SDK

The Claude Agent SDK is Claude Code's loop as a library. The OpenAI Agents SDK is agents, tools, handoffs and guardrails as primitives, on any model the AI SDK reaches through its adapter.

Study first

  • Claude Agent SDK: the tools become an in-process MCP server, the loop and its context management are the SDK's, maxTurns bounds it, and hooks run around every tool call; permissionMode: "bypassPermissions" with allowedTools naming the server's tools means no call asks. The result message carries usage and cost.
  • OpenAI Agents SDK: an Agent has instructions, tools, handoffs and guardrails; run executes until a final output or maxTurns; a handoff is a tool the model calls to pass the conversation, history included, to another agent in the same run. An input guardrail runs on the first agent's input; set not to run in parallel with the model, it runs before any model call, and when it trips the run throws a typed error. Tracing is built in and goes to OpenAI's backend unless a processor sends it elsewhere.
  • Models: the OpenAI SDK takes an AI SDK model through its adapter, so the comparison runs on one Anthropic model throughout.

Build

  1. src/lib/frameworks/claude.ts: createSdkMcpServer with the 4 tools, query() with the instructions as the system prompt, tools: [] so no built-in tool is offered, allowedTools for the server, maxTurns: 20, and a PreToolUse hook that counts every call in a closure the run returns.
  2. src/lib/frameworks/openai.ts: a Researcher with search, read and save that hands off to a Writer with write_report; an input guardrail with the research desk's remit as code, remit, a list of 20 subjects (order, shipping, refund, invoice, vendor and the rest): a topic naming none of them sets tripwireTriggered, and the run throws before any model call.
  3. A test that the guardrail refuses an off-remit question with no model call; the live tests run each framework on one topic.

Done when

  • pnpm frameworks --only claude and --only openai each write a sourced report; a question about the capital of France makes the OpenAI agent throw the guardrail's error with no model call, which the test checks.
Technical notes
  • The Claude Agent SDK's default system prompt is minimal, not the Claude Code one; a string replaces it entirely. tools: [] keeps the file and shell tools out of a research agent.
  • The AI SDK adapter is marked beta and wants an AI SDK provider of specification v2 or later; @ai-sdk/anthropic 4 qualifies. Its traces are off here: the exporter wants an OpenAI key.
  • The handoff is a tool named after the target; the researcher's instructions say to transfer rather than write, or a small model writes the report itself.

Part 4

Five topics, three frameworks: the numbers and the choice

Study first

  • What to measure per run: wall time, tool calls, tokens in and out, cost, findings. What to read from them: a framework's overhead is in the tokens (its own prompts and tool descriptions) and in the time (its own calls).
  • Choosing: an explicit graph, retries at specific steps and resumable long runs point at LangGraph.js; Anthropic models and parity with Claude Code's tooling, hooks and permissions at the Claude Agent SDK; OpenAI models, handoff-heavy designs and guardrails as code at the OpenAI Agents SDK. The choice goes in the architecture document with the 2 reasons that decided it.

Build

  1. pnpm frameworks: each framework on the 5 topics, a row per run, totals per framework; the reports under .data/frameworks/.
  2. docs/frameworks.md: the table, what each framework made easier and harder, and which one WorkBot uses.
  3. The decision in brain/architecture/decisions.md and docs/architecture.md.

Done when

  • The table has 15 rows and the comparison names the 2 reasons for the choice.
The matrix
The same 5 questions on the 3 frameworks: time, tool calls, tokens, cost and findings per run, and the totals.
Technical notes
  • 15 runs on Haiku 5.5. Per run: LangGraph.js 23 s, 16 tool calls, $0.005; the Claude Agent SDK 18 s, 14 calls, $0.005; the OpenAI Agents SDK 19 s, 14 calls, $0.003. Findings per run 3 to 7; every run wrote a sourced report. Time and tokens vary more by run than by framework: the model decides how much to read.
  • Tokens tell the frameworks apart where dollars do not: the Claude Agent SDK carries Claude Code's own context into every turn, about 15,000 tokens read from the cache at a tenth of the price, so a run moves 270,000 input tokens and costs the same as LangGraph's 30,000.
  • The choice, recorded as D12: the product keeps the 90-line loop; the Claude Agent SDK stays on the explainer, where parity with Claude Code's hooks and permissions matters; LangGraph.js is the framework for the first agent that must pause for a person and resume, because its checkpoints live in the Postgres the app has and its graph is testable with a scripted model and drawable. The OpenAI Agents SDK is a pattern to copy, not a dependency: WorkBot runs Anthropic models, and the adapter for them is beta.

Resources