1. Introdução ao LCEL

LCEL (LangChain Expression Language) é um framework simples e fácil de usar para construir cadeias complexas. Ele fornece uma interface unificada e primitivas compostas para facilitar a construção de cadeias. Cada objeto LCEL implementa a interface Runnable, definindo um conjunto de métodos de invocação comumente utilizados (como invoke, batch, stream, ainvoke, etc.). Portanto, as cadeias de objetos LCEL também podem suportar automaticamente esses métodos de invocação, tornando a própria cadeia de cada objeto LCEL um objeto LCEL.

2. Invocação

2.1. Sem usar LCEL

Sem utilizar o LCEL, você pode usar o trecho de código a seguir para passar uma string de tópico e recuperar uma string de piada.

from typing import List
import openai

prompt_template = "Conta uma piada curta sobre {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("sorvete")

2.2. Usando o LCEL

Por outro lado, usando o LCEL, é possível alcançar a mesma funcionalidade de forma mais concisa. O trecho de código a seguir demonstra como construir facilmente uma cadeia usando objetos LCEL.

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(
    "Conta uma piada curta sobre {topic}"
)
output_parser = StrOutputParser()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain = (
    {"topic": RunnablePassthrough()} 
    | prompt
    | model
    | output_parser
)

chain.invoke("sorvete")

3. Streaming

3.1. Sem usar LCEL

O trecho de código a seguir demonstra como transmitir os resultados do processo sem usar o 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("sorvete")

3.2. Usando o LCEL

A transmissão dos resultados do processo usando o LCEL é mais conveniente. O trecho de código a seguir demonstra como fazer streaming com o LCEL.

for chunk in chain.stream("sorvete"):
    print(chunk, end="", flush=True)

4. Processamento em Lote

4.1. Sem usar LCEL

O trecho de código a seguir demonstra como processar em paralelo um lote de entradas sem usar o 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(["sorvete", "espaguete", "dumplings"])

4.2. Usando o LCEL

O processamento em lote usando o LCEL é muito direto. O trecho de código a seguir demonstra como usar o LCEL para processamento em lote.

chain.batch(["sorvete", "espaguete", "dumplings"])