1. Introduction to LCEL
LCEL (LangChain Expression Language) is a simple and easy-to-use framework for building complex chains. It provides a unified interface and composite primitives to make building chains easier. Each LCEL object implements the Runnable
interface, defining a set of commonly used invocation methods (such as invoke
, batch
, stream
, ainvoke
, etc.). Therefore, the chains of LCEL objects can also automatically support these invocation methods, making the chain of each LCEL object itself an LCEL object.
2. Invocation
2.1. Without using LCEL
Without using LCEL, you can use the following code snippet to pass a topic string and retrieve a joke string.
from typing import List
import openai
prompt_template = "Tell me a short joke about {topic}"
client = openai.OpenAI()
def call_chat_model(messages: List[dict]) -> str:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
)
return response.choices[0].message.content
def invoke_chain(topic: str) -> str:
prompt_value = prompt_template.format(topic=topic)
messages = [{"role": "user", "content": prompt_value}]
return call_chat_model(messages)
invoke_chain("ice cream")
2.2. Using LCEL
In contrast, using LCEL can achieve the same functionality more concisely. The following code snippet demonstrates how to easily build a chain using LCEL objects.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
prompt = ChatPromptTemplate.from_template(
"Tell me a short joke about {topic}"
)
output_parser = StrOutputParser()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain = (
{"topic": RunnablePassthrough()}
| prompt
| model
| output_parser
)
chain.invoke("ice cream")
3. Streaming
3.1. Without using LCEL
The following code snippet demonstrates how to stream process results without using LCEL.
from typing import Iterator
def stream_chat_model(messages: List[dict]) -> Iterator[str]:
stream = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
stream=True,
)
for response in stream:
content = response.choices[0].delta.content
if content is not None:
yield content
def stream_chain(topic: str) -> Iterator[str]:
prompt_value = prompt.format(topic=topic)
stream = stream_chat_model([{"role": "user", "content": prompt_value}])
for chunk in stream:
print(chunk, end="", flush=True)
stream_chain("ice cream")
3.2. Using LCEL
Streaming process results using LCEL is more convenient. The following code snippet demonstrates how to stream with LCEL.
for chunk in chain.stream("ice cream"):
print(chunk, end="", flush=True)
4. Batch Processing
4.1. Without using LCEL
The following code snippet demonstrates how to parallel process a batch of inputs without using LCEL.
from concurrent.futures import ThreadPoolExecutor
def batch_chain(topics: list) -> list:
with ThreadPoolExecutor(max_workers=5) as executor:
return list(executor.map(invoke_chain, topics))
batch_chain(["ice cream", "spaghetti", "dumplings"])
4.2. Using LCEL
Batch processing using LCEL is very straightforward. The following code snippet demonstrates how to use LCEL for batch processing.
chain.batch(["ice cream", "spaghetti", "dumplings"])