What is Choosing a stack?
Choosing a stack is an AI engineering fundamental for people who work with LLMs. The layers of an LLM system, and when LangChain, LangGraph, a provider SDK, or another library earns its place.
Escolhendo a stack
Choosing a stack is an AI engineering fundamental for people who work with LLMs. The layers of an LLM system, and when LangChain, LangGraph, a provider SDK, or another library earns its place.
You join a team and the README says “we use Claude”. At the last job the same order bot ran on OpenAI. A post tells you to throw LangChain out. A teammate says everything must be LangGraph. This week’s ticket is only this: classify the customer’s message and send billing to one prompt, orders to another.
The three folders implement the same design. What changes is who writes the sequence, the branch, and the retry. Learning a vendor’s name as if it were the job means you start over at every API change.
An LLM system has four layers. The library lives in one of them.
flowchart TD
APP["1. Your product"]:::tool --> ORCH["2. Orchestration"]:::llm
ORCH --> MODEL(["3. Model provider"]):::io
ORCH --> SIDE["4. Retrieval, eval, and trace"]:::tool
A catalog pattern describes layer 2. The Python tab’s SDK describes one shape of layer 3. Switching tabs does not switch the pattern.
Before the library, pick the rung in Chains, agents, and workflows. A chain, a workflow, and an agent all fit in LangGraph. Using LangGraph does not turn ad moderation into an agent.
| You need | Write it yourself | A library already wrote it |
|---|---|---|
| Two to four calls in a fixed order | Functions | LangChain, if you will swap providers |
| Branches, a loop, a human pause, resume | A handful of functions | LangGraph |
| A tool loop until it stops | A while with a cap |
create_agent (LangChain 1.x, runs on LangGraph), the OpenAI Agents SDK, the Claude Agent SDK, or Pydantic AI |
| A chat UI with streaming | Your API | Vercel AI SDK |
| Questions over a document base | Your retriever | LlamaIndex, when the index is the product |
| Schema output | Pydantic or Zod on the provider SDK | with_structured_output, if the stack is already LangChain |
| A prompt that improves from a dataset | Your eval | DSPy, when the dataset is the spec |
init_chat_model("anthropic:claude-haiku-4-5") and init_chat_model("openai:gpt-4o-mini") return the same kind of object. The prefix is the vendor. The rest of the function does not change. That is what the LangChain tab is showing on the pattern pages: the decision stays in your if, the model is a string.
Text goes in. Text, a tool request, or a schema object comes out. Anthropic returns blocks in content and stop_reason. OpenAI returns choices[0].message and finish_reason. Google uses another envelope. None of those names should show up in the module that writes the invoice. The smell is Provider Format Leak.
LangChain and the Vercel AI SDK hide the envelope. A thin client you write does the same job when you only have two call sites. From the third one on, the abstraction starts to pay, because a model swap stops being a diff in every file.
create_agent hides the turn. The Python tab on ReAct Loop is the one to read first.recursion_limit and the OpenAI Agents SDK’s max_turns count framework steps. Time, money, and an honest stop stay yours. See Stop Conditions.langchain.chains.LLMChain. That API moved to langchain-classic in 1.0. Read the pattern on this site and the import in the current docs, not the other way around.Take the message “I need a second copy of the invoice for order 8831” and mark the layer of each piece. Five minutes.
Classify billing or orders orchestration (workflow)
Call the small model provider
The if that picks the route your code
The billing prompt your product
Anthropic or OpenAI SDK adapter, not the rule
The same classification, two vendors, one function:
from typing import Literal
from langchain.chat_models import init_chat_model
from pydantic import BaseModel
class Route(BaseModel):
kind: Literal["billing", "orders", "other"]
confidence: float
# Change only this string. The shape is "provider:model".
MODEL = "openai:gpt-4o-mini"
# MODEL = "anthropic:claude-haiku-4-5"
# MODEL = "google_genai:gemini-2.5-flash"
router = init_chat_model(MODEL, temperature=0).with_structured_output(Route)
def classify(message: str) -> Route:
return router.invoke([
("system", "Classify: billing, orders, or other."),
("human", message),
])
If your classify function still imports anthropic or openai in the middle of the business rule, the leak is there, not in the model you picked.
if.with_structured_output.while and create_agent.interrupt and a checkpointer when the wait can last for days.Start with the rung. Pick the library that writes that rung in less code than you can still read on an incident Tuesday.
Choosing a stack is an AI engineering fundamental for people who work with LLMs. The layers of an LLM system, and when LangChain, LangGraph, a provider SDK, or another library earns its place.