LCEL is LangChain Expression Language. It gives you a concise way to compose steps with the pipe operator. Each step is a runnable: it accepts input, returns output, and can be invoked, batched, streamed, retried, or traced.
Once you understand runnables, LangChain becomes easier to reason about. A prompt is a runnable. A model is a runnable. A parser is a runnable. Custom functions can become runnables too. The result is a pipeline that reads like the data flow of the app.
LCEL is not just syntax sugar. It creates a standard execution interface across chains. That means you can call <code>invoke</code> for one input, <code>batch</code> for many inputs, <code>stream</code> for partial output, and <code>with_retry</code> for transient failures.
This chain prepares two inputs in parallel: a direct question and a generated search query. The final prompt receives both values.
from langchain_core.runnables import RunnableLambda, RunnableParallel
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini", temperature=0.2)
def make_search_query(question: str) -> str:
return question.lower().replace("?", "").strip()
prepare = RunnableParallel({
"question": RunnableLambda(lambda x: x["question"]),
"search_query": RunnableLambda(lambda x: make_search_query(x["question"])),
})
prompt = ChatPromptTemplate.from_template(
"Answer the question.\nQuestion: {question}\nSearch query to use: {search_query}"
)
chain = prepare | prompt | model | StrOutputParser()
print(chain.invoke({"question": "How does vector search help RAG?"}))
Retries should be explicit. They help with transient network failures, not bad prompts or invalid business logic.
safe_chain = (prompt | model | StrOutputParser()).with_retry(
stop_after_attempt=3,
wait_exponential_jitter=True,
)
answer = safe_chain.invoke({"question": "Explain LCEL in one paragraph."})
print(answer)
LCEL is LangChain Expression Language, a composition style for connecting runnables with a standard execution interface.
Yes. Wrap them with RunnableLambda when you want them to participate in LCEL composition.
Invoke each runnable separately and inspect the value shape passed across every pipe boundary.
Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.
Explore 500+ free tutorials across 20+ languages and frameworks.
Fresh tutorials, interview guides, and coding practice in your inbox.