Explain what the LCEL pipe operator does and why it became modern LangChain's central abstraction
The | overload composes Runnables into a new Runnable whose invoke, batch, stream, and async methods are derived for free, replacing per-shape Chain subclasses with one operator.
Picture a kitchen with three appliances: a mixer, an oven, and a slicer. The old way of making a sandwich shop meant building a single combo-appliance for every menu item: a 'meatball-sub maker,' a 'panini maker,' each one a different machine. LCEL is the moment someone realized you could just snap any three appliances together with a standard plug. Snap prompt then model then parser, and the line you assembled works as one machine. Want to do many sandwiches at once or watch them come out one slice at a time? The standard plug already supports that.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
LCEL is the abstraction that turned LangChain from a 2023 chain-class menagerie into a 2026 production composition layer. The headline is the | operator, but the load-bearing piece is the Runnable interface underneath: a four-method contract every step honors, so composition gives you batch and streaming for free.
The flashcard tests whether you can articulate both halves: what | does mechanically (operator overloading on __or__) and why it earned its central status (the old Chain class hierarchy did not scale and did not stream).
Mental model: LCEL is 'compose any step with any step, get batch and streaming on the composite without doing extra work.' That sentence is the whole point.
The Runnable interface and the | operator
One contract for every step
Every step in LCEL implements the Runnable interface. That includes:
- Prompt templates such as ChatPromptTemplate.
- Models such as ChatOpenAI and ChatAnthropic.
- Output parsers such as StrOutputParser and PydanticOutputParser.
- Retrievers such as VectorStoreRetriever.
- Plain functions wrapped via
RunnableLambda(fn).
The interface defines invoke, batch, stream, astream, and their async siblings, plus utility methods (with_fallbacks, with_retry, with_config, bind).
What | actually does
prompt | model | parser is Python operator overloading. The Runnable base class's __or__ returns a RunnableSequence whose steps are the components in order. The composite is itself a Runnable, so it can be composed again, wrapped, batched, streamed.
When you call .invoke(input) on the composite, it runs prompt.invoke(input), then model.invoke(...), then parser.invoke(...). When you call .batch([input1, input2, ...]), each step's .batch is invoked in turn, parallelizing across inputs. When you call .stream(input), the composite yields chunks as soon as the underlying steps can produce them.
Sibling primitives
- RunnableParallel (with a dict like
{'a': chain_a, 'b': chain_b}) runs branches concurrently and returns a dict. - RunnableBranch (with
(condition, chain), ..., default_chain) routes conditionally. - RunnableLambda lifts a plain function into a Runnable so it can take part in a chain.
Together these handle the long tail of composition shapes.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
Real products, models, and research that use this idea.
- LangChain core ships RunnableSequence, RunnableParallel, RunnableBranch, and RunnableLambda as the canonical LCEL primitives.
- LangServe wraps any Runnable into a FastAPI endpoint, the four-method contract maps onto invoke, batch, stream, and astream HTTP routes directly.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow does LCEL handle parallel branches that produce structured output?
Wrap them in RunnableParallel: {'a': chain_a, 'b': chain_b} returns a dict {'a': ..., 'b': ...}. Each branch runs concurrently when batch or async is used. Combine the outputs in a downstream RunnableLambda that consumes the dict and produces the next step's input.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Thinking of `|` as a Python pipe for shell-style data flow. It is operator overloading on a Runnable interface, not Unix piping.
60 second bullets to scan on the way to the call.
What the Runnable interface is and the four methods it requires
How | composes Runnables into a RunnableSequence
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.