Chains with LCEL

LangChain · Essentials · 12 min

LCEL is the standard way to build deterministic LLM pipelines with streaming and batching built in.

The LangChain Expression Language (LCEL) composes runnables with |. Each step's output becomes the next step's input:

chain = prompt | model | StrOutputParser()
chain.invoke({"topic": "tides"})

This creates a RunnableSequence, itself a runnable, so it gets the full interface for free: invoke, batch, stream, their async versions, and tracing in LangSmith.

Output parsers turn the AIMessage into something convenient:

  • StrOutputParser() returns the text.
  • JsonOutputParser() parses JSON, including partial JSON while streaming.
  • For typed objects prefer model.with_structured_output(...) (a later lesson), which uses the provider's native structured output or tool calling.

Plain Python functions in a chain are wrapped automatically: chain = prompt | model | StrOutputParser() | (lambda s: s.upper()).

Streaming flows through a chain: if every step supports it, tokens stream from the model through the parser to your loop.

LCEL is ideal for fixed pipelines (prompt, call, parse, post-process). When the flow needs loops, decisions or tool use, move to agents or LangGraph, which accept LCEL chains as nodes.

Example

from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("Write a haiku about {topic}.")
# model = init_chat_model("openai:gpt-5-mini")   # real model
model = GenericFakeChatModel(messages=iter([
    "Moon pulls the ocean", "Waves fold into sleeping sand"]))

chain = prompt | model | StrOutputParser() | (lambda s: s.upper())
print(type(chain).__name__)            # RunnableSequence
print(chain.invoke({"topic": "tides"}))  # MOON PULLS THE OCEAN

parts = list((prompt | model | StrOutputParser()).stream({"topic": "sand"}))
print(len(parts) > 1, "".join(parts))  # True Waves fold into sleeping sand

Remember

  • prompt | model | parser builds a RunnableSequence
  • Chains get invoke, batch, stream and async methods for free
  • Plain functions in a chain are wrapped as runnables automatically

Watch out for

  • Calling .invoke on each step manually instead of composing, losing streaming and tracing for the whole pipeline.

Check yourself

  1. What is the output type of prompt | model | StrOutputParser() when invoked?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: reference.langchain.com/python/langchain_core/runnables/