LangChain vs LangGraph: when you need chains and when you need a graph

LangGraph does not compete with LangChain, it sits underneath. We cover when a chain or create_agent is enough, when you need a stateful graph, with code on one model.

5 min · · development · агенты

The "langchain vs langgraph" question looks like a choice between two libraries, but it is really a choice of abstraction level. The LangChain documentation says it directly: LangGraph is a low-level orchestration runtime, and LangChain agents are built on top of it. Below we explain the difference in plain terms, show minimal code at each level with one model through a gateway and count what a graph with a loop costs.

LangChain vs LangGraph: what differs

LangChain provides components (models, prompts, parsers, tools) and a ready agent, create_agent: a minimal configurable harness around the loop "the model thinks, calls a tool, gets the result". You describe the model, the tools and the system prompt, and the loop is already written.

LangGraph is a runtime where you describe the graph yourself: nodes (functions), edges between them and shared state. The documentation lists its strengths: mixing fixed steps and model-driven steps in one graph, persisting state and resuming after a failure, human intervention at any point, streaming, memory. LangGraph can also be used without LangChain.

The recommendation in the LangChain documentation boils down to this: take create_agent for a customizable agent, and take LangGraph for complex cases where deterministic steps are combined with agentic ones.

TaskWhat to take
Prompt, model, response parsing in orderA chain of components
The model calls tools in a loopcreate_agent
Branches and returns by conditionLangGraph
State between steps and persistence after a failureLangGraph with a checkpointer
A pause and human confirmationLangGraph
A long process that may be interruptedLangGraph

Setup

You need a fetch_... key from the dashboard and the address https://api.zukko.pro/v1. Install: pip install langchain langchain-openai langgraph. Connection and the request format are described in the Python reference. The shared part for the examples:

bash
from langchain_openai import ChatOpenAI

writer = ChatOpenAI(
    model="claude-sonnet-5-5",
    base_url="https://api.zukko.pro/v1",
    api_key="fetch_your_key",
)

Level 1: a chain

If the steps go in order and there are no branches, you do not need a graph:

bash
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(
    [("system", "Answer briefly."), ("user", "{question}")]
)
chain = prompt | writer | StrOutputParser()
print(chain.invoke({"question": "What is prompt caching?"}))

The | operator glues components into a pipeline. This is the cheapest option by request count: one model call.

Level 2: an agent with a tool

When the model itself decides which function to call and how many times, take create_agent:

bash
from langchain.agents import create_agent

PRICES = {"claude-sonnet-5-5": (0.4, 2.0), "gpt-6.1-sol": (0.27, 1.32)}

def get_price(model: str) -> str:
    """Return the model's input and output price in dollars per 1M tokens."""
    pin, pout = PRICES[model]
    return f"input {pin}, output {pout}"

agent = create_agent(model=writer, tools=[get_price], system_prompt="You are a pricing assistant.")
result = agent.invoke({"messages": [{"role": "user", "content": "What is the output price of gpt-6.1-sol?"}]})
print(result["messages"][-1].content)

For an agent it matters that the model supports tool calling. Check it on the chosen model with one test request. The loop is hidden here: you do not control how many times the model calls a tool, apart from the system prompt and key limits.

Level 3: a graph with state

Now a task with control: write a text, check it with a second model and, if the check fails, rewrite it, but no more than three attempts. This is an explicit loop with a condition, and a graph describes it conveniently.

bash
from typing import TypedDict

from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph

checker = ChatOpenAI(
    model="deepseek-v4.1-flash",
    base_url="https://api.zukko.pro/v1",
    api_key="fetch_your_key",
    temperature=0,
)

class State(TypedDict):

What happens here. State is a shared dictionary that every node reads and partly updates. The write and check nodes are ordinary functions. A plain edge leads from write to check, and a conditional edge after check decides whether to finish or go back. The InMemorySaver checkpointer stores state per thread_id, so a run can be inspected or continued, and in production it is replaced by storage on disk or in a database. One saving detail: the checking node uses a cheap model, and a graph lets you pick a model per step.

Pitfalls

  • The address. In both classes base_url ends with /v1, because this is an OpenAI-compatible client. Without the suffix requests go to a path that does not exist and return 404.
  • A loop costs money. Every pass through the graph makes new requests. That is why the router has an attempt counter and the key has a daily dollar spend limit; when it runs out the gateway returns 429 key_daily_limit_reached. More: how to limit a key's spend. Also look in the LangGraph documentation for the recursion_limit parameter, which bounds the number of execution steps.
  • Claude cache. In the OpenAI format (and ChatOpenAI works exactly that way by default) all Claude input is billed at the input price. If you have a long repeating context, see the cache calculation in the article on cache reads and writes.

What the loop costs

Prices as of October 18, 2026, per 1M tokens. One pass through the graph: write on claude-sonnet-5-5 (input 0.4, output 2) with 300 input and 150 output tokens; check on deepseek-v4.1-flash (input 0.013, output 0.038) with 250 input and 5 output tokens.

  • write: 300 × 0.4 + 150 × 2 = 120 + 300 = 420 micro-dollars.
  • check: 250 × 0.013 + 5 × 0.038 = 3.25 + 0.19 = 3.44 micro-dollars.
  • A pass: 423.44 micro-dollars. Worst case, three passes: 3 × 423.44 = 1,270.32, about $0.0013 per document, about $1.27 per 1,000 documents.

If the same claude-sonnet-5-5 did the checking, check would cost 250 × 0.4 + 5 × 2 = 110 micro-dollars and a pass 530. Most of the cost is in the generation node, so a cheap checker saves little but does no harm: whether such a model is good enough for checking is decided by a test on your texts. Prices and the model list: GET https://api.zukko.pro/v1/models. Payment: SBP (the payment page adds an 8.5% fee on top), cryptocurrency or MasterCard. Basic LangChain connection: LangChain: pointing it at our base_url.

FAQ

Does LangGraph need LangChain?

No. The LangGraph documentation says directly that it can be used without LangChain, but LangChain components are convenient for models and tools.

Are chains obsolete?

For linear tasks they remain the simplest option. In the current documentation the main entry point for agents is create_agent.

What is Deep Agents?

Per the documentation, it is a more ready-made agent with automatic context compression, a virtual filesystem and subagent spawning, built on LangChain agents. You take it when you want more ready-made behavior out of the box.

Do I need LangSmith?

It is optional. It is a separate service for tracing and evaluation that you connect if you want. To begin, logs and the "Usage" section of the Zukko dashboard are enough.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

LangChain vs LangGraph: chain or graph · Zukko