Você já se frustrou tentando extrair informações específicas das respostas de um modelo de linguagem? As saídas estruturadas do LangChain resolvem esse problema, permitindo que você obtenha dados em formatos precisos como JSON, listas ou tipos específicos – perfeito para integração com seus sistemas!
Neste guia prático, você vai aprender a implementar esta funcionalidade essencial em qualquer projeto de IA, seja com GPT-4, Claude ou modelos gratuitos como Llama.
Caso prefira este conteúdo em formato de videoaula, assista ao vídeo abaixo ou acesse nosso canal no YouTube!
O que você vai aprender hoje
- Por que saídas estruturadas são essenciais para projetos reais
- Como implementar estruturas com Pydantic (com validação)
- Alternativa usando TypedDict para streaming
- Trabalhando com múltiplos schemas
- Melhorando resultados com few-shot prompting
- Exemplos práticos prontos para usar em seus projetos
Por que você precisa de saídas estruturadas?
Quando trabalhamos com LLMs como ChatGPT ou Claude, eles naturalmente respondem com texto livre. Isso é ótimo para conversas, mas problemático quando precisamos extrair dados específicos para usar em aplicações.
Veja um exemplo comum:
# Resposta padrão em texto livre
resposta = llm.invoke("Me conte uma piada sobre gatos")
print(resposta.content)
# Saída: "Por que o gato foi ao médico? Porque estava se sentindo miau!"
Como extrair apenas o punchline desta piada? Ou categorizar partes específicas? Sem estrutura, você precisaria de regex complexos ou processamento de texto pouco confiável.
As saídas estruturadas transformam respostas caóticas em dados organizados que você pode usar diretamente no seu código:
resposta = llm_estruturado.invoke("Me conte uma piada sobre gatos")
print(f"Setup: {resposta.setup}")
print(f"Punchline: {resposta.punchline}")
print(f"Nota: {resposta.rating}")
# Saída:
# Setup: Por que o gato foi ao médico?
# Punchline: Porque estava se sentindo miau!
# Nota: 7
Isso é perfeito para:
- Extrair informações específicas de documentos
- Criar entradas para banco de dados
- Retornar resultados em formato JSON para APIs
- Garantir tipos de dados consistentes (inteiros, floats, listas)
Configurando o ambiente
Vamos começar configurando nosso ambiente. Para este tutorial, usaremos o modelo Llama3 através da integração com Groq, mas você pode adaptar para qualquer modelo compatível com LangChain:
# Instalação da biblioteca
!pip install -qU langchain-groq
# Configuração do modelo
import os
from langchain_groq import ChatGroq
# Configurando a API key
os.environ["GROQ_API_KEY"] = "sua_api_key_aqui"
# Inicializando o modelo
llm = ChatGroq(model="llama3-8b-8192", temperature=0.3)
Método 1: Saídas estruturadas com Pydantic
A abordagem mais robusta para criar saídas estruturadas usa classes Pydantic. Suas vantagens incluem:
- Validação automática dos dados
- Detecção de campos faltantes
- Verificação rigorosa de tipos
Criando um schema para piadas
Vamos implementar um exemplo simples que estrutura piadas:
from typing import Optional
from pydantic import BaseModel, Field
class Joke(BaseModel):
"""Piada para contar ao usuário."""
setup: str = Field(description="A preparação da piada")
punchline: str = Field(description="O desfecho da piada")
rating: Optional[int] = Field(
default=None,
description="Quão engraçada é a piada, de 1 a 10"
)
# Transformando o modelo para retornar este schema
structured_llm = llm.with_structured_output(Joke)
# Testando
response = structured_llm.invoke("Me conte uma piada sobre gatos")
print(response)
O método with_structured_output()
é a mágica aqui. Ele instrui o modelo a formatar sua resposta seguindo exatamente a estrutura que definimos, usando a classe Pydantic como schema.
A saída será algo como:
setup='Por que o gato é bom em videogame?' punchline='Porque ele tem nove vidas!' rating=8
Agora você pode acessar cada campo individualmente:
print(f"Setup: {response.setup}")
print(f"Punchline: {response.punchline}")
print(f"Nota: {response.rating}")
O LangChain utiliza recursos nativos do modelo como function calling (para modelos que suportam) ou instruções formatadas para garantir que a saída siga o schema.
Método 2: TypedDict para mais flexibilidade
Uma alternativa ao Pydantic é usar TypedDict, que oferece:
- Sintaxe mais simples
- Possibilidade de streaming (resultados em tempo real)
- Maior flexibilidade (sem validação rigorosa)
Veja como implementar o mesmo exemplo com TypedDict:
from typing_extensions import Annotated, TypedDict
from typing import Optional
class Joke(TypedDict):
"""Piada para contar ao usuário."""
setup: Annotated[str, ..., "A preparação da piada"]
punchline: Annotated[str, ..., "O desfecho da piada"]
rating: Annotated[Optional[int], None, "Quão engraçada é a piada, de 1 a 10"]
# Criando modelo estruturado
structured_llm = llm.with_structured_output(Joke)
# Testando com streaming
print("Gerando piada com streaming:\n")
for chunk in structured_llm.stream("Me conte uma piada sobre gatos"):
print(chunk)
A grande vantagem aqui é o suporte a streaming, que permite receber os resultados em tempo real, à medida que o modelo gera a resposta. Isso é particularmente útil para interfaces onde você quer mostrar a resposta sendo gerada gradualmente.
Quando usar cada abordagem?
- Use Pydantic quando: Precisar de validação rigorosa, trabalhar com tipos complexos, ou quiser detecção de erros robusta
- Use TypedDict quando: Preferir sintaxe mais simples, precisar de streaming em tempo real, ou quiser estruturas mais flexíveis
Trabalhando com múltiplos schemas
Em cenários mais avançados, você pode precisar que o modelo escolha entre diferentes schemas baseado na entrada. O LangChain facilita isso:
from typing import Union
from pydantic import BaseModel, Field
class Joke(BaseModel):
"""Piada para contar ao usuário."""
setup: str = Field(description="A preparação da piada")
punchline: str = Field(description="O desfecho da piada")
rating: Optional[int] = Field(default=None, description="Nota de 1 a 10")
class ConversationalResponse(BaseModel):
"""Resposta conversacional amigável."""
response: str = Field(description="Uma resposta conversacional à pergunta do usuário")
class FinalResponse(BaseModel):
final_output: Union[Joke, ConversationalResponse]
# Criando modelo estruturado
structured_llm = llm.with_structured_output(FinalResponse)
# Testando diferentes tipos de entrada
print("Pedindo uma piada:")
print(structured_llm.invoke("Me conte uma piada"))
print("\nFazendo uma pergunta normal:")
print(structured_llm.invoke("Como você está hoje?"))
Neste exemplo, o modelo escolherá automaticamente entre retornar uma piada estruturada ou uma resposta conversacional normal, dependendo da pergunta. Isso é extremamente útil quando você tem diferentes tipos de interações em um mesmo sistema.
Melhorando resultados com Few-Shot Prompting
Para schemas complexos, fornecer exemplos no prompt (few-shot learning) pode melhorar drasticamente a qualidade e consistência das respostas estruturadas:
from langchain_core.prompts import ChatPromptTemplate
system = """Você é um comediante hilário. Sua especialidade é piadas curtas.
Retorne uma piada que tenha setup e punchline.
Aqui estão alguns exemplos:
example_user: Me conte uma piada sobre aviões
example_assistant: ["setup": "Por que os aviões nunca ficam cansados?", "punchline": "Porque eles têm asas para descansar!", "rating": 8]
example_user: Me conte uma piada sobre gatos
example_assistant: ["setup": "Por que o gato foi ao médico?", "punchline": "Porque estava se sentindo miau!", "rating": 7]
"""
prompt = ChatPromptTemplate.from_messages([
("system", system),
("human", "{input}")
])
# Criando modelo com few-shot
few_shot_llm = prompt | structured_llm
# Testando
response = few_shot_llm.invoke({"input": "Me conte uma piada sobre programadores"})
print(response)
Este método combina o poder dos prompts cuidadosamente projetados com a estruturação de saída, resultando em respostas mais consistentes e de maior qualidade.
Exemplo prático: Análise de sentimentos
Vamos implementar um exemplo real: um analisador de sentimentos que extrai:
- O texto original
- O sentimento (positivo/neutro/negativo)
- Pontuação de confiança
- Palavras-chave emocionais
from typing import Literal
class AnaliseSentimento(BaseModel):
texto_original: str = Field(description="Texto original")
sentimento: Literal["positivo", "neutro", "negativo"] = Field(
description="Sentimento (positivo/negativo/neutro)"
)
pontuacao_confianca: float = Field(
description="Pontuação de confiança (probabilidade) entre 0 e 1"
)
palavras_chave_emocionais: list[str] = Field(
description="Palavras-chave emocionais"
)
# Criando o modelo estruturado
llm_sentimento = llm.with_structured_output(AnaliseSentimento)
# Testando
resultado = llm_sentimento.invoke(
"Este produto é horroroso! Nunca mais compro essa pasta de amendoim."
)
print(f"Texto: {resultado.texto_original}")
print(f"Sentimento: {resultado.sentimento}")
print(f"Confiança: {resultado.pontuacao_confianca}")
print(f"Palavras emocionais: {resultado.palavras_chave_emocionais}")
Observe o uso de Literal["positivo", "neutro", "negativo"]
– isso restringe o campo sentimento a aceitar apenas esses três valores específicos, garantindo consistência nos resultados.
A saída será algo como:
# Texto: Este produto é horroroso! Nunca mais compro essa pasta de amendoim.
# Sentimento: negativo
# Confiança: 0.95
# Palavras emocionais: ['horroroso', 'nunca']
Este tipo de análise estruturada é ideal para:
- Monitoramento de satisfação de clientes
- Análise automática de reviews
- Triagem de tickets de suporte
- Análise de menções em redes sociais
Aplicações práticas no mundo real
As saídas estruturadas do LangChain são cruciais para diversas aplicações práticas:
- Processamento de documentos: Extrair entidades nomeadas, datas, valores e relacionamentos
- Catalogação de conteúdo: Gerar metadados, tags e categorias para bibliotecas digitais
- Sistemas de recomendação: Estruturar preferências e feedback de usuários
- Automação de workflows: Extrair parâmetros específicos de solicitações em texto livre
- Processamento de formulários: Converter respostas abertas em dados categorizados
Exemplo: Extração de informações de receitas
class Receita(BaseModel):
nome: str = Field(description="Nome da receita")
ingredientes: list[str] = Field(description="Lista de ingredientes necessários")
tempo_preparo: int = Field(description="Tempo de preparo em minutos")
dificuldade: Literal["fácil", "médio", "difícil"] = Field(
description="Nível de dificuldade da receita"
)
passos: list[str] = Field(description="Passos para preparar a receita")
# Criando modelo estruturado
llm_receita = llm.with_structured_output(Receita)
# Testando
receita = llm_receita.invoke("Como fazer um bolo de chocolate simples?")
print(f"Nome: {receita.nome}")
print(f"Tempo: {receita.tempo_preparo} minutos")
print(f"Dificuldade: {receita.dificuldade}")
print("\nIngredientes:")
for item in receita.ingredientes:
print(f"- {item}")
Dicas para obter melhores resultados
- Seja específico nas descrições: Quanto mais detalhes você fornecer sobre cada campo, melhor será o resultado
- Use tipos adequados: Aproveite a tipagem para garantir o formato correto (int, float, list, etc.)
- Teste com modelos menores primeiro: Antes de usar modelos caros, teste com alternativas gratuitas como Llama
- Combine com few-shot prompting: Forneça exemplos claros do formato esperado
- Use campos opcionais quando apropriado: Para informações que podem não estar disponíveis
Exercícios práticos para você experimentar
Aqui estão alguns exercícios para você praticar:
- Sistema de FAQ estruturado:
- Pergunta original
- Resposta direta
- Links relacionados
- Nível de confiança
- Extrator de informações de currículos:
- Nome do candidato
- Habilidades técnicas
- Anos de experiência
- Formação acadêmica
- Histórico profissional
- Analisador de notícias:
- Título da notícia
- Pessoas mencionadas
- Organizações mencionadas
- Localidades
- Principais tópicos
Conclusão: Transforme suas aplicações de IA
As saídas estruturadas do LangChain transformam a maneira como integramos modelos de linguagem em sistemas reais. Ao invés de lidar com texto livre e imprevisível, você obtém dados organizados, validados e prontos para uso.
O mais impressionante é que esta funcionalidade funciona com praticamente qualquer modelo compatível com LangChain – desde os mais avançados como GPT-4 e Claude, até modelos menores e gratuitos como o Llama.
Domine esta técnica e você elevará suas aplicações de IA a um novo patamar, garantindo dados consistentes e facilitando integrações com bancos de dados, APIs e interfaces de usuário.
Quer aprender mais técnicas avançadas para desenvolver com LangChain? Acesse nossa plataforma de ensino de Inteligência Artificial e Automações e crie sua conta gratuitamente!
E você, já implementou saídas estruturadas em algum projeto? Compartilhe sua experiência nos comentários!