Skip to content

Chapter 3 of 8

Composing Runnables and Controlling Execution

LCEL's real power lies in its composition primitives, which let you express branching, fan-out, and routing as dataflow. RunnableSequence chains runnables sequentially, while RunnableParallel runs multiple runnables concurrently and returns a dict of their results — useful when you want summary, translation, and keyword extraction produced in parallel from the same input. RunnablePassthrough forwards its input unchanged; it is the workhorse of retrieval-augmented generation pipelines because it keeps the original question alongside retrieved context in the same dict that flows into the next prompt. RunnableLambda wraps any Python function as a runnable so it can participate in the chain, and RunnableBranch routes an input to different chains based on a list of condition and runnable tuples plus a default, enabling if-then-else logic at the chain level.

Several auxiliary methods fine-tune chain behavior without rewriting it. The assign method on a RunnableParallel adds a new key to the output dict while preserving the original input, enabling map-step-style dataflow where each step sees the results of all prior steps. The pick method returns a runnable that extracts a single entry from a dict (or a single index from a tuple), short-circuiting upstream computation by skipping unselected branches. The bind method freezes keyword arguments onto every call, so you can pre-attach a stop sequence, a tools list, a response_format, or per-call parameters. For runtime flexibility, wrapping a parameter with a ConfigurableField lets you override it per call through config, and configurable_alternatives lets you swap whole sub-chains — different models or prompts — at invoke time without rebuilding the graph.

Reliability primitives attach to any runnable. with_retry retries the same runnable on transient errors such as rate limits and timeouts, with arguments like stop_after_attempt, wait_exponential, wait_exponential_jitter, and retry_if_exception_type controlling backoff and jitter. with_fallbacks runs an alternative runnable when the primary raises a non-retried exception — for example, a cheaper model as backup when the primary fails. The two are composable: wrap the primary with retries first, then wrap that result in fallbacks, so you get transient-error recovery plus graceful degradation. Other safeguards include a configurable recursion_limit on RunnableConfig to prevent infinite loops, and runtime caps enforced by guard runnables or routers for cost control.

Streaming and event inspection deserve special attention. In an LCEL chain, the pipe operator ensures tokens flow through as they are produced, so the final parser's stream method yields the model's text deltas instead of waiting for the whole response. The most granular streaming API is astream_events, which emits typed events such as on_chat_model_start, on_tool_start, and on_chain_end correlated by run_id and parent_run_id. Together, these primitives let you build deterministic, testable, production-grade chains that stream, batch, and fall over gracefully without per-component glue code.

All chapters
  1. 1Foundations of LangChain and the Runnable Interface
  2. 2Prompts, Chat Models, and Output Parsers
  3. 3Composing Runnables and Controlling Execution
  4. 4Memory and Conversation History
  5. 5Documents, Embeddings, and Vector Stores
  6. 6RAG Pipelines and Document Processing
  7. 7Tools, Agents, and LangGraph
  8. 8Observability, Evaluation, Deployment, and Caching

Drill it

Reading is not remembering. These come from the LangChain deck:

Q

What is LangChain?

LangChain is an open-source Python framework for building applications powered by large language models (LLMs). It provides modular components for prompts, chai...

Q

What is LCEL (LangChain Expression Language)?

LCEL is a declarative syntax for composing LangChain components using the pipe operator (|). It creates RunnableSequence chains that support streaming, batching...

Q

How do you create a ChatPromptTemplate?

Use ChatPromptTemplate.from_messages() with a list of (role, template) tuples. Variables are wrapped in curly braces.from langchain_core.prompts import ChatProm...

Q

How do you invoke a ChatModel in LangChain?

Instantiate the model class and call .invoke() with a list of messages or a prompt value. The model returns an AIMessage.from langchain_openai import ChatOpenAI...