Olá, leitores do agntbox! Nina aqui, de volta com mais uma exploração do mundo em constante mudança das ferramentas de IA. Hoje, não estamos apenas falando sobre uma ferramenta; estamos falando sobre algo que parece ter acabado de aterrissar do futuro, mas de uma maneira super acessível. Estamos falando sobre LangChain Expressions Language (LCEL), e especificamente, como isso está tornando minha vida como alguém que constrói e testa aplicações de IA muito mais fácil e estruturada.
Eu me lembro que, há apenas alguns anos, construir algo mais complexo do que um prompt de uma única interação com um LLM parecia uma tentativa de juntar uma máquina de Rube Goldberg com fita adesiva. Você passaria saídas de uma função para a entrada de outra, lidando com estados de erro manualmente e torcendo para que seus tipos de dados se alinhassem. Funcionava, na maior parte, mas era complicado. O LangChain surgiu e ofereceu uma estrutura, o que foi um grande avanço. Mas mesmo assim, encadear componentes ainda tinha uma sensação de boilerplate, especialmente quando você queria fazer algo personalizado ou um pouco fora das cadeias pré-construídas.
Então, entra o LCEL. Quando comecei a ver as conversas sobre isso no final do ano passado, admito que estava um pouco cética. Outra abstração? Precisávamos realmente de mais camadas? Mas depois de passar algumas semanas construindo uma ferramenta de resumo e categorização de conteúdo interna bastante complexa para o agntbox usando isso, sou uma convertida. O LCEL não é apenas mais um recurso; é uma mudança fundamental na forma como você compõe aplicações LLM dentro do ecossistema LangChain. É como passar de escrever consultas SQL brutas para cada interação com o banco de dados para usar um ORM muito bem projetado – você ainda tem controle, mas os padrões comuns são apenas… mais suaves.
LCEL: Mais do que Apenas Encadeamento
Então, o que é LCEL? Em sua essência, é uma maneira de compor sequências executáveis de forma declarativa. Pense nisso como um conjunto de regras e primitivas que permitem conectar LLMs, templates de prompt, analisadores e funções personalizadas de maneira altamente flexível e eficiente. É projetado para ser:
- Componível: Você pode combinar pequenos componentes independentes em componentes maiores e mais complexos.
- Transmitível: Suporta operações assíncronas e transmissão de dados, o que é fantástico para aplicações em tempo real.
- Paralelizável: Lida com a execução concorrente de componentes de forma elegante.
- Investigável: Você pode rastrear a execução de suas cadeias, o que é uma salvação para depuração.
- Portátil: Depois de definir um executável, ele pode ser invocado de várias maneiras.
A parte “Expressions Language” pode parecer um pouco intimidadora, mas é realmente sobre usar os operadores nativos do Python (como | para pipeline) de uma maneira específica para construir essas sequências. Começa a parecer muito Pythonico assim que você se acostuma.
Meu Momento “Aha!”: Construindo um Classificador de Conteúdo
Deixe-me contar sobre o projeto que realmente me conquistou com o LCEL. Aqui no agntbox, recebemos uma tonelada de submissões de artigos e, às vezes, a categorização inicial não está exatamente certa, ou um artigo pode abordar vários tópicos. Meu objetivo era construir uma ferramenta que pudesse pegar um rascunho de artigo, resumi-lo e então sugerir categorias primárias e secundárias de uma lista pré-definida, junto com um score de confiança. Antes do LCEL, provavelmente teria escrito algumas funções diferentes:
- Uma função para obter o resumo.
- Uma função para obter as categorias com base no resumo.
- Tratamento de erros e análise de dados para cada etapa.
Isso funcionaria, mas imagine querer trocar o modelo de sumário ou adicionar uma etapa extra para verificar plágio antes da categorização. Cada mudança significaria mergulhar em uma função específica e potencialmente quebrar algo mais.
Com o LCEL, todo o processo pareceu como montar LEGOs. Vamos percorrer uma versão simplificada de como eu construí isso.
Passo 1: Os Componentes Básicos
Primeiro, defini meu LLM (estou usando o gpt-4-turbo da OpenAI para isso, mas você poderia facilmente trocar). Então, eu precisava de um prompt para resumo e outro para categorização.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
# Meu LLM
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
# 1. Prompt de Resumo
summary_prompt = ChatPromptTemplate.from_template(
"Por favor, forneça um resumo conciso do seguinte artigo para revisão interna. "
"Concentre-se nos principais argumentos e lições chave, visando cerca de 150-200 palavras.\n\nArtigo: {article_content}"
)
# 2. Prompt de Categorização
# Defina o formato de saída esperado para categorização
class CategorySuggestions(BaseModel):
primary_category: str = Field(description="A categoria primária mais relevante.")
secondary_category: str | None = Field(description="Uma categoria secundária relevante, se aplicável.")
confidence_score: float = Field(description="Um score de confiança entre 0.0 e 1.0 para a categoria primária.")
category_prompt = ChatPromptTemplate.from_messages([
("system", "Você é um classificador de conteúdo experiente. Dada um resumo e uma lista de categorias disponíveis, "
"sugira as melhores categorias primária e secundária. Também forneça um score de confiança para a categoria primária. "
"As categorias são: AI Tools, Machine Learning, Data Science, Software Development, Cloud Computing, Cybersecurity, Robotics, Ethics in AI."),
("human", "Resumo: {summary}\n\nSugira categorias e um score de confiança no formato JSON.")
])
# Analisadores de saída
str_parser = StrOutputParser()
json_parser = JsonOutputParser(pydantic_object=CategorySuggestions)
Perceba como eu já estou pensando em uma saída estruturada para a etapa de categorização usando Pydantic e JsonOutputParser. É aqui que o LCEL realmente brilha – tornando fácil garantir a integridade dos dados entre as etapas.
Passo 2: Compondo a Cadeia de Resumo
A primeira parte da minha ferramenta é obter um resumo. Com o LCEL, isso é super limpo:
summary_chain = (
summary_prompt
| llm
| str_parser
)
Essa sequência lê quase como inglês: “Pegue o summary_prompt, envie sua saída para o llm, e então passe a saída do LLM pelo str_parser.” Simples, certo?
Passo 3: Compondo a Cadeia de Categorização
A categorização precisa do resumo como entrada, então é um pouco diferente. Eu queria passar a saída da cadeia de resumo para a cadeia de categorias. O LCEL oferece maneiras de fazer isso usando dicionários para entradas.
categorization_chain = (
{"summary": summary_chain} # Passa a saída da summary_chain como 'summary'
| category_prompt
| llm
| json_parser
)
Aqui, {"summary": summary_chain} é um executável que pega a entrada para todo o processo (o conteúdo do artigo), passa para a summary_chain, e então mapeia o resultado para uma chave chamada "summary". Este dicionário é então passado como entrada para category_prompt.
Passo 4: Colocando Tudo Junto
Agora, eu queria executar tanto a sumarização quanto a categorização. A categorização depende do resumo, então não é paralela. Mas e se eu quisesse fazer algo mais em paralelo com a categorização (por exemplo, verificar palavras-chave)? Para esse fluxo específico, é sequencial, mas o LCEL torna mesmo ramificações complexas claras.
Para meu classificador de conteúdo, decidi mantê-lo simples e apenas combinar os passos sequencialmente, garantindo que a saída geral fosse um dicionário combinado de ambos os resultados:
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
# Um executável que pega article_content e retorna um dicionário com resumo e categorização
full_classifier_chain = (
RunnablePassthrough.assign(
summary=summary_chain
)
| RunnablePassthrough.assign(
categorization=categorization_chain
)
)
Vamos detalhar isso:
RunnablePassthrough.assign(summary=summary_chain): Isso pega a entrada inicial (article_content) e a passa adiante. Também executasummary_chaincom essa mesma entrada e atribui seu resultado a uma nova chave chamada"summary". Então, a saída dessa primeira etapa é{"article_content": "...", "summary": "..."}.- O
|então passa este dicionário para o próximoRunnablePassthrough.assign. RunnablePassthrough.assign(categorization=categorization_chain): Isso pega o dicionário existente (que agora possuiarticle_contentesummary), o passa e, crucialmente, executacategorization_chain. Acategorization_chainesperasummarycomo entrada, que encontra no dicionário passado para ela. Sua saída é então atribuída à chave"categorization".
A saída final de full_classifier_chain seria um dicionário como: {"article_content": "...", "summary": "...", "categorization": {"primary_category": "...", ...}}.
Para usá-lo:
article_draft = """
Os mais recentes avanços em grandes modelos de linguagem estão empurrando os limites do que é possível em IA conversacional. Pesquisadores do XYZ Labs recentemente apresentaram o 'Synthetica-7', um modelo capaz de gerar respostas altamente sutis e contextualmente conscientes, mesmo em discussões longas. Diferente de iterações anteriores que tiveram dificuldades em manter a coerência durante várias interações, o Synthetica-7 utiliza um novo mecanismo de atenção que prioriza as dependências de longo alcance. Essa inovação tem implicações significativas para chatbots de atendimento ao cliente, tutores educacionais e até assistentes de redação criativa. O modelo foi treinado em um enorme corpus de textos diversos, incluindo artigos científicos, ficção e manuais técnicos, permitindo que ele adapte seu tom e estilo dinamicamente. Além disso, o XYZ Labs liberou uma versão menor e ajustada para pesquisa acadêmica, promovendo um ambiente colaborativo para mais inovações na área. Considerações éticas em torno do viés e do uso indevido da IA também foram um foco central durante seu desenvolvimento, com testes extensivos realizados para mitigar possíveis saídas prejudiciais.
"""
# Invoke the chain
result = full_classifier_chain.invoke({"article_content": article_draft})
print(result["summary"])
print(result["categorization"])
Esta configuração é incrivelmente poderosa. Se eu quisesse adicionar um passo para verificar o tom do artigo, poderia simplesmente adicionar outro RunnablePassthrough.assign com um novo prompt e chamada LLM. Se eu quisesse mudar de OpenAI para Cohere, seria uma única alteração na definição de llm. A modularidade é fantástica.
Além do Básico: Paralelismo e Retornos
LCEL não é apenas para cadeias sequenciais simples. Ele oferece maneiras poderosas de lidar com cenários mais complexos:
- Execução Paralela: Use
RunnableParallelpara executar vários componentes ao mesmo tempo e combinar suas saídas em um dicionário. Isso é ótimo, por exemplo, para obter várias perspectivas de diferentes LLMs ou executar uma classificação juntamente com uma resenha, onde nenhuma depende da outra. - Retornos: O método
.with_fallbacks()permite que você defina runnables alternativos a serem tentados se o primário falhar. Isso é enorme para construir aplicações mais resilientes, talvez tentando primeiro um modelo mais barato e rápido e retornando a um mais caro e robusto se o primeiro falhar ou der uma saída de baixa confiança. - Funções Personalizadas: Você pode integrar facilmente qualquer função Python em sua cadeia LCEL usando
RunnableLambda. É assim que eu adicionaria um passo de pré-processamento ou uma validação de pós-processamento personalizada.
Por exemplo, se eu quisesse obter o resumo e também verificar por palavras-chave do artigo original simultaneamente, poderia fazer:
from langchain_core.runnables import RunnableParallel
keyword_extraction_prompt = ChatPromptTemplate.from_template(
"Extraia de 5 a 10 tópicos ou palavras-chave do seguinte artigo. Liste-os como valores separados por vírgula.\n\nArtigo: {article_content}"
)
keyword_chain = (
keyword_extraction_prompt
| llm
| str_parser
| (lambda x: [kw.strip() for kw in x.split(',')]) # Análise personalizada para palavras-chave
)
# Execute resumo e extração de palavras-chave em paralelo
parallel_analysis = RunnableParallel(
summary=summary_chain,
keywords=keyword_chain
)
# Isso executará tanto 'summary_chain' quanto 'keyword_chain' concorrentemente
# result_parallel = parallel_analysis.invoke({"article_content": article_draft})
# print(result_parallel)
Esse RunnableParallel torna fluxos de trabalho complexos intuitivos e performáticos. Você não está esperando que uma chamada LLM termine antes de iniciar outra se elas não dependem uma da outra.
Insights Ação para Seus Projetos de IA
Então, após brincar extensivamente com o LCEL, aqui estão minhas recomendações se você estiver construindo aplicações baseadas em LLM:
- Comece Simples, Construa Gradualmente: Não tente arquitetar toda a sua aplicação com LCEL desde o primeiro dia. Comece com uma única cadeia clara (como um prompt -> LLM -> parser). À medida que você se sentir confortável, introduza elementos mais complexos como execução paralela ou retornos.
- Abrace Saídas Estruturadas: Use modelos Pydantic com
JsonOutputParserou métodos semelhantes. Isso torna suas cadeias muito mais confiáveis, já que você está impondo um contrato sobre a saída do LLM. Isso reduz drasticamente erros de análise posteriormente. - Pense em Runnables: Cada componente no LCEL é um “runnable.” Isso inclui prompts, LLMs, parsers e até funções Python personalizadas. Entender isso ajuda você a ver como tudo pode se encaixar.
- Depure com Rastreamento: LangChain fornece ferramentas de rastreamento (como LangSmith) que se integram perfeitamente com o LCEL. Use-as! Ser capaz de visualizar o caminho de execução e as entradas/saídas em cada etapa é inestimável quando algo sai errado.
- Considere Streaming: Se você estiver construindo aplicações interativas (como chatbots), o suporte do LCEL para resultados de streaming pode melhorar significativamente a experiência do usuário, mostrando respostas parciais à medida que são geradas.
LCEL não é apenas uma atualização menor; é uma melhoria fundamental na maneira como construímos com LangChain. Ele torna a construção de aplicações LLM modulares e performáticas menos parecida com um hackathon e mais com engenharia de software estruturada. Se você estava em dúvida sobre se aprofundar no LangChain ou se sentiu sobrecarregado por suas complexidades anteriores, agora é definitivamente a hora de dar uma olhada séria no LCEL. Ele tornou meu fluxo de trabalho de desenvolvimento mais suave, e estou confiante de que pode fazer o mesmo pelo seu.
Isso é tudo por hoje, pessoal! Boa construção, e nos vemos na próxima vez aqui em agntbox.com.
🕒 Published: