LCEL โ The Expression Language
Lesson 3: LCEL โ The Expression Language
LCEL (LangChain Expression Language) is the composition layer that turns LangChain components into pipelines. It's the difference between writing a pipeline as nested function calls and writing it as a readable chain: prompt | model | parser. The pipe operator is not just syntax sugar โ every piece of an LCEL chain implements a common Runnable interface, which buys you a lot for free.
| inherits the same superpowers: invoke(), stream(), batch(), and their async versions โ without writing any glue code.
The Runnable interface
Every component (model, prompt, parser, retriever, tool, or composed chain) exposes the same methods:
| Method | What it does |
|---|---|
invoke(input) | Run once, return the result |
stream(input) | Run, yielding output chunks as they arrive (token-by-token for models) |
batch(inputs) | Run over a list of inputs, returning a list of results |
ainvoke / astream / abatch | Async equivalents |
Because streaming, batching, and async come from the interface rather than each component, a complex chain streams just like a bare model call. That's the "free" part.
Composition primitives
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
# Pipe: feed previous output into next
chain = prompt | model | StrOutputParser()
# RunnablePassthrough: pass input through unchanged (or assign new keys)
passthrough = RunnablePassthrough.assign(upcased=lambda x: x["topic"].upper())
# RunnableLambda: wrap arbitrary Python as a chain step
def word_count(text: str) -> int:
return len(text.split())
step = RunnableLambda(word_count)
# Parallel: run multiple chains on the same input, merge results
parallel = {
"summary": summary_chain,
"keywords": keyword_chain,
} | RunnableLambda(merge_results)
Resilience features
LCEL runnables include built-in failure handling that would otherwise be a lot of boilerplate:
- Retries โ
chain.with_retry(stop_after_attempt=3)handles transient API errors. - Fallbacks โ
model.with_fallbacks([cheaper_model, local_model])degrades gracefully when a provider is down or rate-limited. - Configuration โ
.with_config({"max_concurrency": 5})for batch runs;.bind(...)to pin model parameters. - Timeouts โ
.with_timeout(30)for hard deadlines.
๐ง Knowledge Check
1. What does the | operator do in LCEL?
2. Which superpowers does every LCEL runnable get for free?
3. What is LCEL NOT well-suited for?