LangChain
LangChain is a toolkit that joins prompts, models and parsers into one pipeline, so you can swap any part without rewriting the rest.
- 11 min read
- 3 reading levels
- Updated
Read these first
On this page 9
One lesson, three depths. Pick the one that fits you today — you can switch any time.
Beginner — No maths. Plain English.
The short answer
LangChain joins the steps of an AI feature into one pipeline, where each step hands its result to the next.
The analogy you have already lived
Making chai is a fixed sequence. Boil water. Add tea leaves. Add milk and sugar. Strain into a cup.
Each step takes what the last step produced. You cannot strain before you boil. The order is the recipe.
Now the useful part. If you change milk brand, the recipe does not change. Only that one step changes, and everything around it keeps working. A LangChain pipeline behaves exactly like that with AI models.
Why it exists
An AI feature is almost never one call to a model. Look at what "answer a customer's question" really involves.
- Take the question.
- Slot it into a carefully written prompt, with instructions and examples.
- Send that to a model.
- Pull a clean answer out of the reply.
- Retry if the network hiccups.
Written by hand, that becomes a tangle of string joining and error handling, all mixed together in one function. Then your manager says "try a different model", and you discover every provider expects a different shape of request.
LangChain gives all the pieces one common shape. Because they share a shape, you can join them with a single symbol and swap any one of them out.
How it works
{question}
|
v
[ prompt template ] fills your question into a written instruction
|
v
messages
|
v
[ model ] any model: local, or a paid service
|
v
a reply object
|
v
[ parser ] pulls out plain text, or JSON, or a list
|
v
your answerIn code, those arrows are written with the pipe symbol |. The whole pipeline becomes one object you can call, test and reuse.
The one word to learn: chain
A chain is a sequence of steps joined end to end. The output of one step becomes the input of the next.
That is the entire idea, and the library is named after it. Everything else is details.
Where you have already seen this
- A support bot that reads your order number, looks it up, and replies in your language.
- A tool that turns a meeting recording into bullet-point minutes.
- An app that reads your notes and produces flashcards.
Each is a short pipeline: prepare the input, ask a model, tidy the output.
The honest part
This one needs saying plainly, because a lot of people find out the hard way.
LangChain is genuinely disputed. Many experienced developers start with it and later remove it. The complaint is real. The library wraps things you could have written yourself. When something breaks, the error comes from three layers down.
It changed a lot between versions. Tutorials from a couple of years ago will not run today. Whole classes have been removed. If you are following a video, check the date first.
So here is honest advice. Learn what a chain is, because that idea is worth having. Build your first project with plain HTTP calls, so you see every moving part. Reach for LangChain when you are tired of writing the same retry and parsing code for the fifth time.
That is a real recommendation, not a compromise. A tool that saves you work is worth it. A tool you adopt before you have the work is a layer of confusion.
Remember this
- A chain is steps joined end to end, each feeding the next.
- LangChain's value is a common shape, so any step can be swapped out.
- It is a convenience layer, not a requirement. Learn the idea before the library.
What to learn next
- LlamaIndex — the same pipeline idea, aimed squarely at your documents.
- Function calling and tools — letting the model run your Python.
- AI agents — what happens when the chain decides its own next step.
Developer — Code and libraries.
Setup
The pieces are split into separate packages, on purpose. You install only what you use.
pip install langchain-corelangchain-core holds the interfaces: prompts, messages, parsers, and the pipe operator. It is pure Python and installs in seconds. That alone is enough for the first example.
Providers live in their own packages: langchain-ollama, langchain-openai, langchain-anthropic, langchain-google-genai. The langchain package on top adds agents and pulls in LangGraph.
A chain that prints the same thing for everybody
Real models produce different text on every run, which makes them useless for a first example. FakeListChatModel returns a script you write, so this file behaves identically on your machine and mine.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.language_models import FakeListChatModel
# 1. A prompt with holes in it.
prompt = ChatPromptTemplate.from_messages([
("system", "You are a support agent. Reply in {language}. One sentence only."),
("human", "{question}"),
])
# 2. Filling the holes is a step you can run on its own. Do this when a chain misbehaves.
filled = prompt.invoke({"language": "Hindi", "question": "Where is my refund?"})
for message in filled.to_messages():
print(f"[{message.type}] {message.content}")
# 3. A stand-in model, so this file prints the same thing for you as for me.
model = FakeListChatModel(responses=["Aapka refund 5 kaam ke dinon mein aa jayega."])
# 4. The pipe operator wires the steps into one callable object.
chain = prompt | model | StrOutputParser()
print()
print("chain type:", type(chain).__name__)
print("steps:", [type(step).__name__ for step in chain.steps])
print()
print(chain.invoke({"language": "Hindi", "question": "Where is my refund?"}))[system] You are a support agent. Reply in Hindi. One sentence only. [human] Where is my refund? chain type: RunnableSequence steps: ['ChatPromptTemplate', 'FakeListChatModel', 'StrOutputParser'] Aapka refund 5 kaam ke dinon mein aa jayega.
Line by line, the parts that are not obvious
prompt.invoke({...}) on its own. This is the most useful debugging habit in the whole library. Every step is independently callable. When a chain gives strange answers, run the prompt step alone and read the messages you are actually sending. Nine times out of ten the bug is visible right there.
[system] and [human]. A chat model does not receive a string. It receives a list of role-tagged messages. system sets behaviour, human is the user's turn, ai is the model's. The template produced the roles for you.
type(chain).__name__ is RunnableSequence. The pipe operator did not run anything. It built an object. Nothing executes until you call .invoke(). That distinction explains why a typo in a prompt template can stay silent until much later.
StrOutputParser. Without it the chain returns an AIMessage object, and print() shows the whole repr with metadata attached. The parser pulls out .content as a plain string.
The same chain against a real model
Swap one line. Nothing else changes. That is the payoff.
pip install langchain-ollamafrom langchain_ollama import ChatOllama
model = ChatOllama(model="llama3.2:1b", temperature=0, num_ctx=4096)
chain = prompt | model | StrOutputParser()
print(chain.invoke({"language": "English", "question": "Where is my refund?"}))No output block, deliberately. The answer changes with the model, the version and the run. Printing an invented sample would teach you to expect something that will not happen.
Every chain also gives you three more methods for free:
chain.batch([...])— many inputs, run concurrently.chain.stream({...})— yields pieces as they arrive, so a UI can show words appearing.await chain.ainvoke({...})— the async version.
You did not write any of those. They came from the shared interface, and that is most of what the library is buying you.
Common mistakes
Following a tutorial written for an older version. This is the big one. Run this against a current install:
from langchain.chains import LLMChainModuleNotFoundError: No module named 'langchain.chains'
LLMChain, SimpleSequentialChain and initialize_agent belong to an older design that has been replaced. If a tutorial uses them, it will not run. Pipes replaced chains; agents moved to create_agent and LangGraph.
Unescaped curly braces. A template treats { as the start of a variable. A prompt containing JSON like {"name": "x"} raises a missing-variable error. Double them: {{"name": "x"}}.
Importing from the wrong package. langchain_core for interfaces, langchain_<provider> for models, langchain for agents. A ModuleNotFoundError here usually means you installed one and imported from another.
Debugging a whole chain at once. Run the steps individually. prompt.invoke(...), then model.invoke(messages), then the parser. The broken step announces itself in seconds.
Losing track of cost. A chain hides how many model calls happen. With a paid API that is money leaving your account invisibly. Set LANGCHAIN_VERBOSE=true, or use a tracing tool, before you run anything in a loop.
Try it yourself
Take the working chain.py and add a fourth step that upper-cases the answer:
from langchain_core.runnables import RunnableLambda
chain = prompt | model | StrOutputParser() | RunnableLambda(str.upper)Then break it on purpose. Delete StrOutputParser and read the error. Understanding why str.upper cannot accept an AIMessage teaches you more about the library than any amount of reading.
What to learn next
- LlamaIndex — the same pipeline idea, aimed squarely at your documents.
- Function calling and tools — letting the model run your Python.
- AI agents — what happens when the chain decides its own next step.
Researcher — Mathematics and papers.
The Runnable interface
LangChain Expression Language (LCEL) is a small algebra over one protocol. A Runnable[Input, Output] implements invoke, batch, stream, and their async counterparts, plus configuration and schema introspection.
__or__ is overloaded to build a RunnableSequence, which is function composition with three properties that matter:
- Automatic batching.
RunnableSequence.batchpushes the batch down to each step, so a model step receives a list and can issue concurrent requests rather than N sequential ones. - Streaming by transformation. A step declares whether it can transform an input iterator into an output iterator.
StrOutputParsercan; a step that needs the complete string (a JSON parser over a whole object, for example) cannot, and it collapses the stream at that point. This is why adding a parser can silently remove token-by-token streaming from a UI. - Uniform callbacks. Tracing hooks attach at the protocol level, so every composed graph is instrumentable without per-step code.
RunnableParallel (constructed implicitly from a dict) executes branches concurrently and merges results. RunnablePassthrough.assign adds keys while forwarding the original input, which is the standard idiom for building a RAG context dictionary.
Typed input and output schemas are derived with Pydantic, which is what allows LangServe to generate an OpenAPI specification from a chain object.
Where the abstraction leaks
Three well-documented costs, stated without euphemism.
Stack depth in tracebacks. An exception raised in a provider client surfaces through several layers of generic runnable machinery. The line that actually failed is often not in the visible frames.
Prompt opacity. Convenience constructors inject text you did not write — format instructions from output parsers, tool descriptions, scratchpad scaffolding. Since prompt wording measurably drives model behaviour, hidden text is a correctness issue, not a style one. chain.get_prompts() and callback tracing are the antidotes.
Version churn. The 0.1 → 0.2 → 0.3 → 1.0 sequence relocated packages, removed the original chain classes and moved agents onto LangGraph. Published tutorials therefore have a short half-life, which is the single most common reason a newcomer's code fails.
Against those: retry with backoff, provider fallbacks, concurrent batching, streaming, structured-output binding and tracing are each a few hundred lines you would otherwise own and maintain. The trade is real in both directions. It depends on how many providers you support and how long the code must live.
Agents belong to LangGraph now
The original initialize_agent implemented ReAct (Yao et al., 2022) as a while-loop over a prompt. It could not checkpoint, resume, branch, or admit human review mid-run.
LangGraph reframes an agent as a directed graph with explicit state. Nodes are functions over a typed state object; edges may be conditional; a checkpointer persists state after each node. That buys durable execution across process restarts, time-travel replay to any prior checkpoint, and interrupt points for human approval.
The theoretical point is that a loop with a hidden accumulator is a state machine written badly. Making the state machine explicit is what makes the system debuggable, and it is why the reference agent framework moved in that direction.
Evaluation
A chain is a pipeline, and a pipeline needs stage-level measurement. Aggregate end-to-end scores hide which step is failing.
- Fix a dataset of inputs with expected outputs, versioned in the repository, not in a notebook.
- Measure each stage separately: retrieval recall for a retriever, schema-validity rate for a structured-output step, task accuracy for the whole.
- LLM-as-judge (Zheng et al., 2023) is workable for open-ended output, with known biases: position bias, verbosity bias, and self-preference. Randomise position, calibrate against human labels, and report agreement rate.
References
- Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models, 2022 — arxiv.org/abs/2210.03629
- Schick et al., Toolformer, 2023 — arxiv.org/abs/2302.04761
- Zheng et al., Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena, 2023 — arxiv.org/abs/2306.05685
- Khattab et al., DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines, 2023 — arxiv.org/abs/2310.03714 — the main alternative philosophy: optimise prompts programmatically rather than hand-write them.
What to learn next
- LlamaIndex — the same pipeline idea, aimed squarely at your documents.
- Function calling and tools — letting the model run your Python.
- AI agents — what happens when the chain decides its own next step.