This document describes how to build flexible, composable chains using LangChain Expression Language (LCEL), including prompt templates, pipe operators, runnable primitives, and type coercion mechanisms.
This document explores LangChain Expression Language (LCEL), a modern pattern for building composable chains using the pipe operator to connect components. It covers prompt template structuring with variables, runnable composition primitives including sequential and parallel execution, type coercion mechanisms, and practical implementation patterns for developing reusable AI applications with enhanced readability and flexibility.
LangChain Expression Language (LCEL) is a pattern for building LangChain applications that utilizes the pipe operator to connect components. This approach ensures a clean, readable flow of data from input to output.
LangChain has evolved significantly, and LCEL represents the newer, recommended pattern rather than the traditional LLM chain approach. This modern method provides better composability, clearer visualization of data flow, and greater flexibility when constructing complex chains.
To create a typical LCEL pattern, several key steps must be followed to ensure proper chain construction and execution.
The following steps outline the process of creating an LCEL pattern:
First, define a template with variables and curly braces. Next, create a prompt template instance. Then, build a chain using the pipe operator to connect components. Finally, invoke the chain with input values.
This structured approach enables developers to construct chains that are both maintainable and easy to understand. The pattern emphasizes clarity in data flow and component interaction.
In LangChain, runnables serve as an interface and building blocks that connect different components like LLMs, retrievers, and tools into a pipeline. Understanding these components is essential for effective chain construction.
There are two main runnable composition primitives that form the foundation of LCEL:
RunnableSequence chains components sequentially, passing the output from one component as input to the next. This creates a linear flow of data processing.
RunnableParallel runs multiple components concurrently while using the same input for each. This enables parallel processing of tasks on identical input data.
LCEL provides elegant syntax shortcuts that simplify chain construction. Instead of using runnable sequence explicitly, the same sequential chain can be created by simply connecting runnable 1 and runnable 2 with a pipe, making the structure more readable and intuitive.
This shorthand notation reduces boilerplate code while maintaining clarity in expressing the data flow through the chain.
LCEL handles type coercion automatically, which means it converts regular code into runnable components. This automation simplifies development by eliminating manual component wrapping.
When a dictionary is used, it becomes a runnable parallel, which runs multiple tasks simultaneously. When a function is used, it becomes a RunnableLambda, which transforms inputs. This happens behind the scenes, eliminating the need to handle the conversion manually.
The following example demonstrates type coercion in action:
1# Pipe operator combines prompt templates with the LLM
2chain = prompt | llm
3
4# Dictionary structure creates a RunnableParallel
5parallel_chain = {
6 "summary": prompt1 | llm,
7 "translation": prompt2 | llm,
8 "sentiment": prompt3 | llm
9}
In this code, the pipe operator combines the prompt templates with the LLM. The dictionary structure creates a RunnableParallel which processes all three tasks simultaneously. Each task receives the same input text but processes it differently. When executed, it automatically becomes a RunnableParallel. The result will contain three keys: summary, translation, and sentiment, each with the output from the respective LLM call.
The pipe operator is the core mechanism for connecting components in LCEL chains. Understanding its usage is crucial for building effective workflows.
Creating a simple chain demonstrates how components can be connected using the pipe operator:
1from langchain.schema.runnable import RunnableLambda
2
3# Define format function
4def format_prompt(inputs):
5 return f"Tell me a {inputs['adjective']} joke about {inputs['content']}"
6
7# Create chain with pipe operator
8chain = RunnableLambda(format_prompt) | llm | StrOutputParser()
9
10# Invoke chain
11result = chain.invoke({"adjective": "funny", "content": "chickens"})
The RunnableLambda in this chain wraps the format_prompt function, transforming it into a runnable component that LangChain can work with.
When the chain runs, RunnableLambda takes the input dictionary containing adjective and content keys, passes this dictionary to the format_prompt function. The function formats the prompt template with these variables. The formatted prompt is then passed to the next component, the LLM.
The pipe operator creates a sequence by connecting runnable components together. In this joke chain, first, the RunnableLambda formats the prompt with variables. The pipe operator passes the formatted prompt to the LLM. Another pipe passes the LLM’s response to the StrOutputParser.
Understanding when and how to use LCEL effectively ensures optimal application development and performance.
LCEL is best suited for simpler orchestration tasks where workflows are relatively straightforward. For more complex workflows, consider using LangGraph while still leveraging LCEL within individual nodes. This hybrid approach allows developers to benefit from both frameworks’ strengths.
As applications are developed, taking advantage of LCEL’s strengths enhances both power and maintainability. These strengths include:
Parallel execution enables multiple components to process data simultaneously. Async support allows for non-blocking operations that improve performance. Simplified streaming facilitates real-time data processing. Automatic tracing provides visibility into chain execution for debugging and monitoring.
These capabilities make LCEL a powerful tool for building modern AI applications that require both performance and maintainability.
LCEL pattern structures workflows using the pipe operator for clear data flow, making chains more readable and maintainable. Prompts are defined using templates with variables and curly braces, providing flexibility in input handling. Components can be linked using RunnableSequence for sequential execution, ensuring ordered processing. RunnableParallel allows multiple components to run concurrently with the same input, enabling efficient parallel processing. LCEL provides a more concise syntax by replacing RunnableSequence with the pipe operator, reducing code complexity. Type coercion in LCEL automatically converts functions and dictionaries into compatible components, simplifying development and reducing manual configuration requirements.
The key steps are:
(2) Type coercion in LCEL automatically converts regular code into runnable components. Dictionaries become RunnableParallel and functions become RunnableLambda without manual intervention.
The pipe operator in LCEL creates a sequence by connecting runnable components together.
True. The pipe operator is the core mechanism for connecting runnable components together in sequence, creating a clear flow of data transformation through the chain.
(2) When a dictionary structure creates a RunnableParallel, it processes all tasks simultaneously. Each task receives the same input but processes it differently, returning results with multiple keys.
The four key strengths are:
(3) RunnableParallel is most appropriate because it runs multiple components concurrently while using the same input for each, making it ideal for performing multiple analyses on identical input data.
| Component | Function |
|---|---|
| A. RunnableSequence | 1. Wraps functions into runnable components |
| B. RunnableParallel | 2. Chains components sequentially |
| C. RunnableLambda | 3. Parses LLM output into string format |
| D. StrOutputParser | 4. Runs components concurrently |
A-2, B-4, C-1, D-3.
(3) This is incorrect. Type coercion in LCEL happens automatically without explicit configuration. The system handles conversions behind the scenes, eliminating the need for manual component wrapping.
(2) Automatic tracing provides visibility into chain execution, which directly facilitates debugging and monitoring by making the execution flow transparent and trackable.
(2) When a pipe operator chain fails, the first priority is verifying that components are compatible and properly connected, as the pipe operator requires runnable components that can pass data to each other.
LCEL reduces code complexity by replacing RunnableSequence with the pipe operator.
True. LCEL provides a more concise syntax by replacing the explicit use of RunnableSequence with the pipe operator, reducing boilerplate code while maintaining clarity in data flow.
(2) Automatic database management is not a strength of LCEL. The documented strengths include parallel execution, async support, simplified streaming, and automatic tracing.