1. Введение в LCEL (LangChain Expression Language)

LCEL (LangChain Expression Language) - это простая и удобная среда для создания сложных цепочек. Она предоставляет унифицированный интерфейс и композитные примитивы для упрощения построения цепочек. Каждый объект LCEL реализует интерфейс Runnable, определяя набор часто используемых методов вызова (таких как invoke, batch, stream, ainvoke и т. д.). Поэтому цепочки объектов LCEL также могут автоматически поддерживать эти методы вызова, делая саму цепочку каждого объекта LCEL объектом LCEL.

2. Вызов

2.1. Без использования LCEL

Без использования LCEL можно использовать следующий фрагмент кода для передачи строки темы и извлечения строки с шуткой.

from typing import List
import openai

prompt_template = "Расскажи мне короткую шутку про {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("мороженое")

2.2. Используя LCEL

В отличие от этого, с помощью LCEL можно достичь той же функциональности более лаконично. Нижеприведенный фрагмент кода демонстрирует, как легко построить цепочку, используя объекты 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(
    "Расскажи мне короткую шутку про {topic}"
)
output_parser = StrOutputParser()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain = (
    {"topic": RunnablePassthrough()} 
    | prompt
    | model
    | output_parser
)

chain.invoke("мороженое")

3. Потоковая обработка

3.1. Без использования LCEL

В следующем фрагменте кода демонстрируется, как потоково обрабатывать результаты без использования 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("мороженое")

3.2. Используя LCEL

Потоковая обработка результатов с использованием LCEL более удобна. Нижеприведенный фрагмент кода демонстрирует, как проводить потоковую обработку с LCEL.

for chunk in chain.stream("мороженое"):
    print(chunk, end="", flush=True)

4. Пакетная обработка

4.1. Без использования LCEL

В следующем фрагменте кода демонстрируется, как параллельно обрабатывать пакет входных данных без использования 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(["мороженое", "спагетти", "пельмени"])

4.2. Используя LCEL

Пакетная обработка с использованием LCEL очень проста. Нижеприведенный фрагмент кода демонстрирует, как использовать LCEL для пакетной обработки.

chain.batch(["мороженое", "спагетти", "пельмени"])