Python & AI Tutorials Logo
LangChain & LangGraph

8. Estado de Conversação e Memória

Até agora, todas as interações com LLM que construímos foram sem estado (stateless). Sem estado significa que cada requisição é independente—o modelo não tem memória de conversas anteriores. Isso funciona bem para tarefas pontuais como resumo de documentos ou perguntas e respostas únicas.

Mas construir um agente conversacional é uma história diferente. Você precisa que o agente se lembre do que foi discutido anteriormente, entenda pronomes como "isso" ou "aquilo", e mantenha o contexto ao longo do diálogo. Para fazer isso, você deve gerenciar explicitamente o estado (state).

(Aqui, "estado" refere-se a informações que um programa lembra. Para agentes conversacionais, o histórico de conversação anterior é o estado.)

Neste capítulo, abordaremos:

  • Por que LLMs não "lembram" conversas
  • Como implementar memória de conversação usando ferramentas de histórico de mensagens do LangChain
  • Como gerenciar orçamentos de tokens para prevenir estouro de contexto

8.1) Por Que LLMs Esquecem

LLMs Não Têm Memória

LLMs têm uma característica crítica: eles não lembram nada de conversas anteriores.

Quando você chama uma API de LLM, o modelo processa sua entrada e gera uma resposta. Mas ele não armazena esse registro em lugar algum. Não há memória dentro do modelo que mantém estado, nenhum histórico de conversação. Cada chamada de API é completamente independente. É como começar do zero toda vez.

Isso é por design. LLMs funcionam como funções sem estado (stateless): você fornece entrada, eles produzem saída, e nada é retido. O modelo rodando nos servidores da OpenAI agora não tem registro do que você acabou de perguntar.

Por Que LLMs Parecem Lembrar

Mas espere—quando você usa ChatGPT ou Claude, parece que eles lembram sua conversação. Você pode dizer "Me fale sobre Paris", depois perguntar "Qual é a população?" e o modelo sabe que você ainda está falando sobre Paris. Como isso funciona?

Aqui está como: a aplicação envia o histórico de conversação anterior junto com cada nova mensagem.

Aqui está o que realmente acontece:

LLMAplicaçãoUsuárioLLMAplicaçãoUsuárioAplicação armazena histórico de mensagens"Me fale sobre Paris"[Mensagem 1: "Me fale sobre Paris"]"Paris é a capital da França...""Paris é a capital da França...""Qual é a população?"[Mensagem 1: "Me fale sobre Paris"Mensagem 2: "Paris é a capital..."Mensagem 3: "Qual é a população?"]"Paris tem aproximadamente 2,1 milhões...""Paris tem aproximadamente 2,1 milhões..."

O LLM não "lembra" que você perguntou sobre Paris anteriormente—ele só sabe porque a aplicação enviou o histórico de conversação anterior junto com a nova mensagem. Em última análise, é a aplicação que gerencia o estado, não o modelo.

Por Que "Estado" Deve Ser Gerenciado em Sua Aplicação, Não no Modelo

Pense em um LLM como uma função pura: dada uma entrada, ele produz uma saída. Não há estado interno que o LLM gerencia além das mensagens que você fornece. Isso é por design.

Para agentes conversacionais, isso significa que o estado deve ser gerenciado em sua aplicação.

Estado—o histórico de conversação—vive no código da sua aplicação, não no modelo. Isso significa que você é responsável por:

  • Armazenar o histórico de conversação
  • Enviar histórico relevante com cada nova requisição
  • Gerenciar o tamanho do histórico (abordado na seção 8.3)

Na seção 8.2, implementaremos isso usando ferramentas de histórico de mensagens do LangChain.

O Que Acontece Se Você Não Gerenciar Estado?

Se você não gerenciar estado, seu agente (sua aplicação) não pode manter uma conversação coerente. Aqui estão as falhas mais comuns:

1. Não Consegue Lembrar Conversação Anterior

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Primeira pergunta
response1 = llm.invoke([HumanMessage(content="Meu nome é Alice")])
print(response1.content)  # Saída: Prazer em conhecê-la, Alice!
 
# Segunda pergunta (sem histórico enviado)
response2 = llm.invoke([HumanMessage(content="Qual é o meu nome?")])
print(response2.content)  # Saída: Não sei o seu nome...

O modelo não tem ideia de que você disse que seu nome era Alice porque não enviamos essa informação na segunda chamada.

2. Não Consegue Identificar a Que Pronomes Se Referem

python
# Usuário pergunta sobre um tópico
response1 = llm.invoke([HumanMessage(content="Me fale sobre Python")])
print(response1.content)  
# Saída: Python é uma linguagem de programação de alto nível conhecida por sua legibilidade...
 
# Usuário faz uma pergunta de acompanhamento com um pronome
response2 = llm.invoke([HumanMessage(content="Quais são suas principais características?")])
print(response2.content)  
# Saída: Ficaria feliz em ajudar! Poderia especificar sobre o que você está perguntando?

Sem a mensagem anterior, o modelo não consegue identificar a que "suas" se refere.

Impacto no Mundo Real:

Imagine construir um agente de suporte ao cliente sem gerenciamento de estado:

Usuário: "Estou tendo problemas com meu pedido #12345"
Agente: "Sinto muito por isso. Qual parece ser o problema?"
Usuário: "O endereço de entrega está errado"
Agente: "Posso ajudar com isso. Poderia fornecer seu número de pedido?"
Usuário: "Acabei de te dizer..."

Essa experiência frustra usuários e perde sua confiança. Gerenciamento de estado não é opcional para agentes conversacionais—é essencial para criar interações coerentes e úteis.

Na seção 8.2, implementaremos gerenciamento de estado usando ferramentas de histórico de mensagens do LangChain e adicionaremos memória de conversação ao chat CLI.

8.2) Gerenciando Estado de Conversação

Agora que entendemos por que o gerenciamento de estado é crítico, vamos aprender como implementá-lo. Usaremos ferramentas de histórico de mensagens integradas do LangChain para gerenciar o histórico de conversação. No final, aplicaremos o que aprendemos para adicionar gerenciamento de estado ao chat CLI do Capítulo 3.

Entendendo Tipos de Mensagem: HumanMessage, AIMessage e SystemMessage

Antes de aprender como gerenciar estado de conversação, você precisa conhecer os tipos de mensagem usados no estado de conversação. LangChain usa três tipos de mensagem para representar conversações: HumanMessage (entrada do usuário), AIMessage (resposta do modelo) e SystemMessage (instruções). Cada mensagem tem um papel (role) (tipo de mensagem) e conteúdo (content) (o texto real).

python
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 
# SystemMessage: Instruções para o comportamento do modelo
system_msg = SystemMessage(content="Você é um assistente prestativo especializado em programação Python.")
 
# HumanMessage: Entrada do usuário
user_msg = HumanMessage(content="Como faço para ler um arquivo em Python?")
 
# AIMessage: Resposta do modelo
# (Na prática, LangChain envolve a resposta do modelo neste objeto - mostrado aqui para ilustração)
ai_msg = AIMessage(content="Você pode usar a função `open()` com um gerenciador de contexto...")

Por que tipos de mensagem separados?

Para que o modelo entenda o histórico de conversação efetivamente, ele precisa saber o propósito de cada mensagem e quem a disse. Os três tipos de mensagem servem propósitos distintos:

  • SystemMessage: Instruções que definem como o modelo deve se comportar (ex: "Seja conciso", "Você é um tutor de Python")
  • HumanMessage: O que o usuário disse
  • AIMessage: O que o modelo respondeu anteriormente

Essa estrutura permite que o modelo distinga entre instruções, perguntas do usuário e suas próprias respostas passadas—o que é essencial para manter conversações coerentes de múltiplas rodadas.

Construindo Histórico de Conversação Manualmente

Agora que entendemos os três tipos de mensagem, vamos ver como construir manualmente um histórico de conversação:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Construir histórico de conversação
# SystemMessage define instruções (uma vez no início)
# Depois: Entrada do usuário → Resposta da IA → Entrada do usuário (fluxo de conversação natural)
messages = [
    SystemMessage(content="Você é um tutor conciso de Python."),
    HumanMessage(content="O que é uma list comprehension?"),
    AIMessage(content="Uma list comprehension é uma forma concisa de criar listas: [x*2 for x in range(5)]"),
    HumanMessage(content="Pode me mostrar um exemplo mais complexo?")
]
 
# Enviar histórico completo com nova pergunta
response = llm.invoke(messages)
print(response.content)

Saída:

Claro! Aqui está uma list comprehension que filtra e transforma:
[x**2 for x in range(10) if x % 2 == 0]
Isso produz:
[0, 4, 16, 36, 64]

O modelo entende que "um exemplo mais complexo" refere-se a um exemplo mais complexo de list comprehension porque enviamos o histórico completo de conversação.

Usando InMemoryChatMessageHistory

Construir listas de mensagens manualmente torna-se trabalhoso à medida que as conversações crescem. O InMemoryChatMessageHistory do LangChain simplifica isso fornecendo métodos para adicionar mensagens e recuperar o histórico completo.

Métodos principais:

  • add_message(message): Adiciona uma única mensagem (HumanMessage, AIMessage, SystemMessage)
  • add_messages(messages): Adiciona múltiplas mensagens de uma vez
  • messages: Propriedade que retorna a lista completa de mensagens
  • clear(): Remove todas as mensagens (útil para começar do zero)

Exemplo:

python
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 
# Criar um armazenamento de histórico de mensagens
history = InMemoryChatMessageHistory()
 
# Adicionar múltiplas mensagens de uma vez
history.add_messages([
    SystemMessage(content="Você é um tutor prestativo de Python."),
    HumanMessage(content="O que é um decorator em Python?")
])
 
# Adicionar mensagens uma por uma
history.add_message(AIMessage(content="Um decorator é uma função que modifica o comportamento de outra função..."))
history.add_message(HumanMessage(content="Pode mostrar um exemplo?"))
 
# Recuperar todas as mensagens
messages = history.messages

Exemplo Prático: Gerenciamento de Estado com InMemoryChatMessageHistory:

python
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
history = InMemoryChatMessageHistory()
 
# Definir mensagem do sistema e adicionar ao histórico (feito uma vez no início)
system_msg = SystemMessage(content="Você é um tutor prestativo de Python.")
history.add_message(system_msg)
 
# Função de chat
def chat(user_input):
    """Processa entrada do usuário, envia para LLM e gerencia automaticamente o histórico de conversação"""
    # Adicionar entrada do usuário ao histórico
    history.add_message(HumanMessage(content=user_input))
    
    # Enviar entrada do usuário junto com histórico de conversação anterior
    response = llm.invoke(history.messages)
    
    # Adicionar resposta do modelo ao histórico
    history.add_message(response)
    
    return response.content
 
# Simular conversação
print(chat("O que é uma função lambda?"))
print(chat("Me mostre um exemplo"))  # Modelo lembra o contexto
print(chat("Qual é a diferença de uma função regular?"))  # Ainda lembra

Saída:

Uma função lambda é uma função anônima definida com a palavra-chave lambda...
 
Aqui está um exemplo: square = lambda x: x**2
Você pode usá-la assim: square(5) # Retorna 25
 
Funções lambda são limitadas a uma única expressão, enquanto funções regulares...

A função chat() lida com o gerenciamento de estado automaticamente: ela envia cada requisição do usuário junto com o histórico de conversação anterior para o LLM, e adiciona tanto a requisição quanto a resposta de volta ao histórico. Simplesmente chamar chat() mantém o estado de conversação sem gerenciamento adicional.

Refatorando o Capítulo 3: Adicionando Memória ao Seu Chat CLI

Vamos pegar o chat CLI com streaming do Capítulo 3 e adicionar memória de conversação. Aqui está a versão original sem estado:

python
# chapter3_cli.py (original - sem estado)
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_loop():
    print("Chat iniciado. Digite 'quit' para sair.\n")
    while True:
        user_input = input("Você: ")
        if user_input.lower() == "quit":
            break
        
        # Sem estado - sem histórico
        response = llm.stream([HumanMessage(content=user_input)])
        print("IA: ", end="", flush=True)
        for chunk in response:
            print(chunk.content, end="", flush=True)
        print("\n")
 
if __name__ == "__main__":
    chat_loop()

Versão refatorada com memória:

python
# chapter8_cli.py (com memória)
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_loop():
    print("Chat iniciado. Digite 'quit' para sair.\n")
    
    # Criar histórico e adicionar mensagem do sistema
    history = InMemoryChatMessageHistory()
    history.add_message(SystemMessage(content="Você é um assistente prestativo."))
    
    while True:
        user_input = input("Você: ")
        if user_input.lower() == "quit":
            break
        
        # Adicionar mensagem do usuário ao histórico
        history.add_message(HumanMessage(content=user_input))
        
        # Fazer streaming da resposta
        print("IA: ", end="", flush=True)
        full_response = ""
        for chunk in llm.stream(history.messages):
            print(chunk.content, end="", flush=True)
            full_response += chunk.content
        print("\n")
        
        # Adicionar resposta da IA ao histórico
        history.add_message(AIMessage(content=full_response))
 
if __name__ == "__main__":
    chat_loop()

O que mudou:

  1. Adicionado armazenamento de histórico: Criada instância de InMemoryChatMessageHistory() dentro de chat_loop()
  2. Mensagem do sistema: Adicionada ao histórico uma vez no início
  3. Enviar entrada do usuário com histórico: llm.stream(history.messages) inclui conversação anterior
  4. Rastrear conversação: Adicionar tanto entrada do usuário quanto resposta do LLM ao histórico

Testando o chat refatorado:

Chat iniciado. Digite 'quit' para sair.
 
Você: Meu nome é Alice
IA: Prazer em conhecê-la, Alice! Como posso ajudá-la hoje?
 
Você: Qual é o meu nome?
IA: Seu nome é Alice.
 
Você: O que acabei de te perguntar?
IA: Você me perguntou qual é o seu nome.
 
Você: quit

O modelo agora mantém contexto ao longo de toda a conversação. Ele lembra seu nome, perguntas anteriores e pode referenciar partes anteriores do diálogo.

Armazenamento Persistente: Indo Além de Opções em Memória

InMemoryChatMessageHistory é conveniente para desenvolvimento local, mas migrar para produção requer substituí-lo por uma solução de armazenamento que garanta persistência.

Limitações Técnicas do InMemory:

  • RAM Volátil: Quando o processo do servidor termina ou reinicia, todo o histórico de conversação armazenado em memória é imediatamente deletado. Atualizações ou recuperação de erros resultam em perda completa do contexto do usuário.
  • Sem escalabilidade horizontal: À medida que seu serviço escala para múltiplas instâncias de servidor, cada servidor mantém sua própria memória isolada. Usuários conectando-se a diferentes servidores não podem compartilhar histórico de conversação.
  • Ineficiência de Recursos: Armazenar todo o histórico de conversação em RAM é intensivo em memória e ameaça a estabilidade do sistema à medida que usuários simultâneos aumentam.

Alternativas Profissionais:

  • PostgresChatMessageHistory (Recomendado): A escolha mais robusta e amplamente adotada. Usa PostgreSQL para armazenamento permanente e se destaca em consultas complexas e análise de dados.
  • SQLChatMessageHistory: Aproveita bancos de dados SQL existentes como MySQL. Permite que você use sua infraestrutura atual sem mudanças.
  • RedisChatMessageHistory: Ideal para serviços onde velocidade de resposta é crítica. Baseado em memória com opções de persistência, especializado para lidar com alto tráfego.

"Armazenamento muda, código permanece o mesmo"

LangChain fornece uma interface unificada em todos os backends de armazenamento. Os mesmos métodos que você usou com InMemoryChatMessageHistory—como add_message() e add_messages()—funcionam identicamente com outras opções de armazenamento. Isso significa que sua lógica de negócio (código de manipulação de conversação) não requer mudanças ao trocar de armazenamento.

python
# [Desenvolvimento] Em memória local
# from langchain_core.chat_history import InMemoryChatMessageHistory
# history = InMemoryChatMessageHistory()
 
# [Produção] Armazenamento persistente PostgreSQL
import psycopg
from langchain_postgres import PostgresChatMessageHistory
 
# Criar conexão PostgreSQL
sync_connection = psycopg.connect(
    "postgresql://user:password@10.1.1.100:5432/agent_db",
    autocommit=True,
)
 
# Especificar conexão DB e ID de sessão
# session_id: Chave única para identificar conversações
#   - Gerenciamento por usuário: session_id = user_id (uma conversação por usuário)
#   - Gerenciamento por sessão: session_id = uuid (novo ID para cada conversação)
history = PostgresChatMessageHistory(
    table_name="chat_history",
    session_id="user_123",
    sync_connection=sync_connection
)
 
# --- Mesma interface independentemente do tipo de armazenamento ---
history.add_message(HumanMessage(content="Me mostre meu pedido anterior."))
print(history.messages)

Importante: Este guia usa InMemory para progressão rápida, mas implantações em produção devem mudar para armazenamento persistente como PostgresChatMessageHistory.

8.3) Gerenciando Comprimento de Conversação

O recurso de memória de conversação que implementamos na seção anterior tem um problema importante: ele apenas adiciona mensagens ao histórico. Isso significa que o histórico continua crescendo, o que cria dois problemas principais:

  1. Custo: O histórico completo é enviado com cada requisição, então o custo por requisição continua aumentando à medida que o histórico cresce
  2. Limites de janela de contexto: Modelos têm um tamanho máximo de entrada que podem processar em uma única requisição (ex: 400K tokens para GPT-5). Quando o histórico de conversação excede esse limite, o modelo não pode processar a requisição adequadamente.

Uma das soluções mais simples é o padrão de janela deslizante (sliding window).

Padrão de Janela Deslizante (Manter Apenas as Últimas N Mensagens)

O padrão de janela deslizante resolve os dois problemas acima mantendo apenas as N mensagens mais recentes no histórico de conversação. Ele gerencia o tamanho do histórico descartando mensagens antigas, fornecendo os seguintes benefícios:

  1. Controle de custo: Ao manter o histórico abaixo de um certo tamanho independentemente do comprimento da conversação, previne que os custos por requisição cresçam infinitamente
  2. Sem estouro: Mantém o tamanho de entrada dentro do limite máximo do modelo

Diagrama conceitual:

Mensagem 1

Mensagem 2

Mensagem 3

Mensagem 4

Mensagem 5

Mensagem 6

Tamanho da Janela = 4

Com um tamanho de janela de 4, mantemos apenas as 4 mensagens mais recentes (3, 4, 5, 6) e descartamos as mensagens mais antigas (1, 2). Quando uma nova mensagem (7) chega, a janela se move em direção à mensagem mais recente, descartando a mensagem mais antiga (3) e mantendo as mensagens 4, 5, 6, 7.

Trade-offs da Janela Deslizante:

  • Prós: Limita o tamanho do histórico para manter custos constantes e previne estouro da janela de contexto
  • Contras: Mensagens além do tamanho da janela são descartadas, então o modelo não pode referenciá-las

Esse trade-off pode ser problemático. A solução é usar uma janela deslizante para conversação recente enquanto recupera informações passadas necessárias de um armazenamento separado quando necessário. Isso pode ser implementado usando RAG (Retrieval-Augmented Generation), que abordaremos no Capítulo 9.

Unidades de Tamanho de Janela: Contagem de Mensagens vs Contagem de Tokens

O diagrama acima mostra um exemplo de definição de tamanho de janela por contagem de mensagens. No entanto, em ambientes de produção, dimensionamento de janela baseado em tokens é mais comumente usado porque os tamanhos de mensagens variam:

Corte Baseado em Contagem de Mensagens:

Limita o tamanho da janela por contagem de mensagens (ex: manter apenas as últimas 20 mensagens).

  • Características: Contagem fixa de mensagens, mas a contagem total de tokens ainda pode variar
  • Usar quando: Tamanhos de mensagens são controlados (SMS, chats com limite de caracteres)
  • Risco: Uma mensagem longa ainda pode exceder a janela de contexto

Corte Baseado em Contagem de Tokens (Recomendado para Produção):

Limita o tamanho da janela por contagem de tokens, a unidade de entrada processada por LLMs (ex: manter apenas os últimos 5.000 tokens).

  • Características: Nunca excede a janela de contexto independentemente do comprimento da mensagem
  • Usar quando: Tamanhos de mensagens variam

Implementando Corte Baseado em Tokens: trim_messages()

LangChain fornece um utilitário trim_messages() que implementa o padrão de janela deslizante baseado em tokens.

Como trim_messages() funciona:

Esta função pega a lista completa de mensagens e a contagem máxima de tokens, retornando apenas as mensagens mais recentes que cabem dentro do limite de tokens.

Parâmetros principais:

  • messages: Lista de mensagens para cortar
  • max_tokens: Contagem máxima de tokens a manter
  • token_counter: Função para calcular contagem de tokens para cada mensagem (usa o tokenizador do modelo para retornar contagem de tokens da mensagem)
  • include_system: Se deve sempre manter SystemMessage (geralmente True)

Configurando o contador de tokens:

python
import tiktoken
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
from langchain_core.messages.utils import trim_messages
 
# Obter tokenizador para seu modelo (diferentes famílias de modelos usam diferentes tokenizadores)
# Forneça o nome do modelo para obter o tokenizador apropriado
# Modelos Claude: Use o tokenizador da Anthropic (tiktoken é específico da OpenAI)
enc = tiktoken.encoding_for_model("gpt-4o")
 
def token_counter(msg: BaseMessage) -> int:
    """Conta tokens em uma única mensagem."""
    return len(enc.encode(msg.content or ""))
 
# Criar histórico de conversação
messages = [
    SystemMessage(content="Você é um assistente prestativo."),
    HumanMessage(content="Oi!"),
    AIMessage(content="Olá! Como posso ajudar?"),
    HumanMessage(content="Quanto é 2+2?"),
    AIMessage(content="2+2 é igual a 4."),
    HumanMessage(content="Quanto é 3+3?"),
    AIMessage(content="3+3 é igual a 6."),
    HumanMessage(content="Quanto é 4+4?"),
]
 
# Manter apenas mensagens dentro da contagem máxima de tokens
trimmed = trim_messages(
    messages,
    max_tokens=30,
    token_counter=token_counter,
    include_system=True,
)
 
print(f"Original: {len(messages)} mensagens")
print(f"Cortado: {len(trimmed)} mensagens")
for m in trimmed:
    print(f"{type(m).__name__}: {m.content}")

Nota: O número de mensagens retidas depende do valor de max_tokens e da contagem real de tokens de cada mensagem. No exemplo acima, max_tokens=30 é um valor muito pequeno escolhido para fins de teste. Em produção, você deve definir um valor apropriado considerando o tamanho médio de mensagens e o alcance de conversação desejado.

Exemplo: Aplicando Corte à Função de Chat

python
import tiktoken
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage, BaseMessage
from langchain_core.messages.utils import trim_messages
 
llm = ChatOpenAI(model="gpt-4o-mini")
history = InMemoryChatMessageHistory()
enc = tiktoken.encoding_for_model("gpt-4o-mini")
 
def token_counter(msg: BaseMessage) -> int:
    """Conta tokens em uma única mensagem."""
    return len(enc.encode(msg.content or ""))
 
def chat_with_trimming(user_input: str, max_tokens: int = 1000) -> str:
    """Chat com corte automático de histórico."""
    history.add_message(HumanMessage(content=user_input))
    
    system_msg = SystemMessage(content="Você é um assistente prestativo.")
    all_messages = [system_msg] + history.messages
    
    # Cortar para contagem máxima de tokens
    trimmed_messages = trim_messages(
        all_messages,
        max_tokens=max_tokens,
        token_counter=token_counter,
        include_system=True,
    )
    
    response = llm.invoke(trimmed_messages)
    history.add_message(response)
    
    return response.content
 
# Exemplo de uso
print(chat_with_trimming("Estou planejando uma viagem ao Japão"))
print(chat_with_trimming("O que devo visitar em Tóquio?"))
print(chat_with_trimming("Quantos dias devo passar lá?"))
# Mesmo à medida que o histórico cresce, apenas conversação recente dentro da contagem máxima de tokens é enviada ao LLM

Próximo Passo: Limitações do Gerenciamento de Estado de Conversação e Soluções (Capítulo 9: RAG)

Fornecer histórico de conversação ao modelo ajuda a manter o contexto da conversação. No entanto, isso sozinho não é suficiente em alguns casos. Por exemplo:

  • Quando você precisa encontrar informações em documentos ou manuais da empresa
  • Quando você precisa referenciar histórico de conversação antigo que foi empurrado para fora da janela deslizante

É aqui que RAG (Retrieval-Augmented Generation) é necessário. RAG funciona da seguinte forma:

  1. Armazenamento: Armazena informações em um banco de dados vetorial para busca semântica
  2. Recuperação: Consulta informações com significado similar ao que você está procurando

Se usar RAG para complementar as limitações do padrão de janela deslizante:

  • Janela deslizante: Manter últimas 20 mensagens (contexto recente)
  • RAG: Buscar e recuperar conteúdo relevante de mensagens empurradas para fora da janela

Em vez de apenas lembrar conversação recente, RAG permite que você crie um sistema de memória de longo prazo.

RAG permite que agentes aproveitem conhecimento mais amplo e contexto mais longo através de utilização de conhecimento externo e recuperação de conversação passada.

O Capítulo 9 abordará como implementar RAG em detalhes.


Resumo do Capítulo:

Neste capítulo, você aprendeu:

  1. Por que LLMs esquecem: Modelos são sem estado—memória é uma ilusão criada ao reenviar histórico de conversação
  2. Tipos de mensagem: SystemMessage (instruções), HumanMessage (entrada do usuário), AIMessage (respostas do modelo)
  3. Gerenciamento de estado: Gerenciando histórico de conversação com InMemoryChatMessageHistory
  4. Gerenciamento de comprimento de conversação: Por que histórico ilimitado causa problemas de custo e janela de contexto
  5. Janelas deslizantes: Um padrão que mantém apenas mensagens recentes para prevenir que o histórico cresça infinitamente
  6. Corte baseado em tokens: Implementando janelas deslizantes usando trim_messages() e contagem de tokens