In the early days of LangChain, building LLM applications was a “manual” process. Developers had to juggle different methods for different components: format() for prompts, predict() for models, and parse() for output parsers. As applications grew complex, this lack of standardization made it difficult to connect components flexibly.
To solve this, LangChain introduced Runnables—a standardized interface that allows you to treat every component as a “Lego block” that can be easily plugged into another.
1. What is a Runnable? #
A Runnable is a unit of work. It has four key characteristics:
- Input/Output Flow: It takes an input, processes it, and returns an output.
- Standardized Interface: Every Runnable follows the same set of rules and implements methods like
invoke(for single inputs),batch(for multiple inputs), andstream(for real-time output). - Composability: You can connect multiple Runnables so that the output of one automatically becomes the input of the next.
- Self-Similarity: When you connect two Runnables, the resulting “chain” is itself a Runnable, allowing you to build infinitely complex structures.
2. Categories of Runnables #
LangChain divides Runnables into two main types:
A. Task-Specific Runnables
These are core LangChain components converted into Runnables to perform specific tasks.
- PromptTemplate: To design and format prompts.
- Chat Models (e.g., ChatOpenAI): To interact with LLMs.
- Output Parsers: To clean and structure the LLM’s response.
B. Runnable Primitives
These are the “connectors” that orchestrate how task-specific Runnables interact.
3. Key Runnable Primitives & Implementation #
RunnableSequence (The Linear Chain)
The most common primitive. it connects two or more Runnables sequentially.
- Workflow: R1 Output → R2 Input.
- Declarative Syntax (LCEL): LangChain Expression Language (LCEL) uses the pipe operator (
|) to define these sequences.
# Modern LCEL syntax for a sequential chain
chain = prompt | model | parser
result = chain.invoke({"topic": "AI"})
RunnableParallel (Branching)
This allows multiple Runnables to execute simultaneously using the same input. It is ideal for generating different formats (like a tweet and a LinkedIn post) at once.
- Output: Returns a dictionary containing the results of all parallel paths.
from langchain_core.runnables import RunnableParallel
parallel_chain = RunnableParallel({
"tweet": tweet_chain,
"linkedin": linkedin_chain
})
RunnablePassThrough
A special primitive that passes the input to the output without any changes.
- Use Case: Often used within a
RunnableParallelto keep the original data (like a generated joke) alongside a processed version (like an explanation of that joke).
RunnableLambda
This allows you to convert any standard Python function into a Runnable.
- Use Case: Perfect for custom logic, such as data cleaning, pre-processing, or counting words in an LLM’s response.
# Converting a word counter function to a Runnable
word_counter = RunnableLambda(lambda x: len(x.split()))
chain = joke_chain | word_counter
RunnableBranch (Conditional Logic)
This is the “If-Else” statement for LangChain. It allows you to define different paths based on a condition.
- Workflow: It checks a condition (e.g., “Is the text > 500 words?”). If true, it runs Path A; otherwise, it runs Path B (the default).
4. Why Use Runnables and LCEL? #
- Standardization: You no longer need to remember if a component uses
format()orpredict(); you just useinvoke(). - Declarative Design: Using the pipe operator (
|) makes your code more readable and defines the intent of the pipeline clearly. - Flexibility: You can build anything from a simple joke generator to a complex RAG (Retrieval-Augmented Generation) application with minimal lines of code
Quiz #
Q.1 In the context of LangChain's history, what was a primary issue with using specific chain classes like 'LLMChain' or 'RetrievalQA'?
The sheer number of specific chains led to a heavy code base and a steep learning curve.
They were too computationally expensive to run on standard hardware.
They could only handle single-step tasks and were incapable of being combined.
They were only compatible with OpenAI models and could not use open-source alternatives.
Explanation
LangChain originally included many specialized chain classes. This increased the library’s complexity, made it harder to maintain, and created a steeper learning curve for developers.
Q.2 Which standard method is implemented across all Runnables to allow them to process a single input and return an output?
format
predict
invoke
execute
Explanation
Every Runnable implements the invoke() method, which accepts a single input and returns the corresponding output, providing a consistent interface across LangChain components.
Q.3 How does 'RunnableParallel' handle the input it receives?
It sends the exact same input to every internal Runnable simultaneously.
It splits the input into chunks and sends one chunk to each internal Runnable.
It modifies the input based on the requirements of each specific component before sending it.
It passes the input to the first Runnable and then passes that output to the next one.
Explanation
RunnableParallel broadcasts the same input to multiple Runnables at the same time, allowing them to execute independently and return their results together.
Q.4 What is the primary purpose of the 'RunnablePassThrough' primitive?
To filter out unnecessary metadata from the LLM response.
To convert a standard Python function into a format compatible with LangChain.
To output the input exactly as it was received without any processing.
To automatically retry a failed API call in a sequential chain.
Explanation
RunnablePassThrough simply forwards the input unchanged. It is useful when you need to preserve or reuse the original input while other processing occurs in the chain.
Q.5 When using 'RunnableBranch', what happens if none of the defined conditions are met?
A default Runnable, provided as the last argument, is executed.
The input is passed directly to the next component in the sequence.
The chain throws an error and stops execution.
It randomly selects one of the existing branches to execute.
Explanation
RunnableBranch works like an if-else statement. If none of the specified conditions match, the default Runnable provided as the final argument is executed.
Q.6 What is the main benefit of converting a Python function into a 'RunnableLambda'?
It allows the custom logic within the function to be seamlessly piped into a LangChain sequence.
It automatically translates the Python code into JavaScript for cross-platform use.
It encrypts the function's logic to protect sensitive intellectual property.
It makes the function run 10× faster by using the LangChain runtime.
Explanation
RunnableLambda wraps a normal Python function as a Runnable so it can be combined with other LangChain components using LCEL operators like the pipe (|).
Q.7 Which LCEL (LangChain Expression Language) operator is used to create a 'RunnableSequence'?
The Ampersand operator (&)
The Arrow operator (->)
The Pipe operator (|)
The Plus operator (+)
Explanation
The pipe (|) operator connects Runnables into a RunnableSequence, where the output of one component becomes the input of the next.
Q.8 Which of the following best describes 'Task-Specific Runnables'?
They are temporary runnables that only exist during the debugging phase of development.
They are runnables specifically designed to manage hardware resources like GPU memory.
They are core LangChain components, like models or parsers, that have been standardized as runnables.
They are the building blocks that define how other runnables interact.
Explanation
Task-specific Runnables are standard LangChain components such as chat models, prompt templates, retrievers, output parsers, and tools that all implement the Runnable interface.
Q.9 According to the analogy provided in the source material, why are Runnables like Lego blocks?
Because they are inexpensive and easy to replace if a better version is released.
Because every block has a specific purpose and uses a standard interface to connect to any other block.
Because they come in different colors to help organize the code visually.
Because they are designed primarily for educational purposes and not for professional applications.
Explanation
Like Lego blocks, every Runnable has a specific role but follows the same interface, allowing developers to combine different components together in flexible and reusable workflows.
Q.10 If you wanted to calculate the number of words in an LLM's response using a custom Python function inside a chain, which primitive would you use?
RunnableLambda
RunnableParallel
RunnablePassThrough
RunnableSequence
Explanation
RunnableLambda is designed to wrap custom Python functions so they can participate in LangChain pipelines alongside prompts, models, retrievers, and other Runnables.