Por que os agentes de IA mudaram em 2026
Até recentemente, construir um agente de IA que chamasse APIs, lembrasse de conversas anteriores e tomasse decisões em cadeia exigia semanas de desenvolvimento e centenas de linhas de código customizado. Hoje, com frameworks maduros como o LangGraph, você constrói o mesmo fluxo em uma tarde — e com visibilidade total sobre cada decisão que o modelo toma.
O que mudou? Em 2024, a maioria dos agentes era single-turn: recebe uma pergunta, chama o modelo, devolve a resposta. Os problemas reais — consultar bancos de dados, manter contexto entre conversas, auditar decisões — exigiam infraestrutura customizada que quebrava a cada novo caso de uso. O LangGraph resolve isso representando o agente como um grafo direcionado, onde cada nó é uma unidade de trabalho e as arestas definem o fluxo de execução.
Neste tutorial, você vai construir um agente completo do zero: de uma única chamada de modelo até um sistema com ferramentas, memória persistente e visibilidade total da cadeia de raciocínio.
O que você ganha (e o que não ganha)
✅ Vantagens
- Estado compartilhado explícito: cada nó lê e escreve no mesmo estado. Nada passa entre nós de outra forma. Isso torna o fluxo previsível e fácil de debugar.
- Memória de conversa automática: o
MessagesStateacumula o histórico completo de mensagens com um reducer dedicado. Sem stitching manual. - Troca de modelos sem reescrever o grafo: trocar OpenAI por Anthropic ou um modelo local via Ollama é mudar uma linha de import.
- Ferramentas com two-way routing: o modelo decide quando usar uma ferramenta; o grafo executa e devolve o resultado automaticamente para o modelo interpretar.
- Persistência plug-and-play: um checkpointer transforma um agente stateless em stateful com duas linhas de código.
- Rastreabilidade completa: cada passo de raciocínio, chamada de ferramenta e resposta fica registrado no estado do grafo.
⚠️ Limitações
- Cada uso de ferramenta custa duas chamadas de modelo: uma para decidir o que consultar, outra para interpretar o resultado. Isso impacta latência e custo conforme você adiciona ferramentas.
- Curva de aprendizado dos primitivos: State, Node, Edge e reducers formam uma abstração poderosa, mas exigem compreensão sólida antes de escalar.
- InMemorySstore não é para produção: o checkpointer em memória some quando o processo reinicia. Produção exige PostgreSQL, Redis ou outro backend durável.
Pré-requisitos
| Componente | Mínimo | Recomendado | Ideal |
|---|---|---|---|
| Python | 3.10 | 3.11 | 3.12 |
| RAM | 4 GB | 8 GB | 16 GB |
| Chave de API | OpenAI | OpenAI + Anthropic | Multi-provedor |
| Conhecimento prévio | Python básico | Conceitos de LLM | Experiência com LangChain |
| Tempo estimado | 45 min | 30 min | 20 min |
| Sistema operacional | Qualquer com Python | macOS / Linux | Linux (Docker) |
Passo 1: Instalação e configuração do ambiente
Comece criando um ambiente virtual e instalando as dependências:
# Criar e ativar ambiente virtual (Linux/macOS)
python3 -m venv .venv
source .venv/bin/activate
# Windows
# python -m venv .venv
# .venv\Scripts\activate
# Instalar pacotes necessários
pip install langgraph langchain-openai python-dotenvCrie um arquivo .env na raiz do projeto com sua chave da OpenAI:
OPENAI_API_KEY=sk-seu-token-aquiCarregue as variáveis de ambiente no início do seu script Python, antes de qualquer import do LangChain ou LangGraph:
from dotenv import load_dotenv
load_dotenv() # Lê o .env e define as variáveis de ambientePasso 2: Entendendo os três primitivos fundamentais
Todo grafo no LangGraph é construído com apenas três conceitos. Dominá-los evita confusão quando o grafo ficar mais complexo.
State (Estado)
Um TypedDict que funciona como memória compartilhada do grafo inteiro. Cada nó lê dele e devolve um dicionário parcial com os campos que quer modificar. Campos não mencionados permanecem inalterados.
Nodes (Nós)
Funções Python puras. Recebem o estado atual como argumento e retornam um dicionário com as atualizações. Registrar uma função com add_node é o que a torna parte do grafo — sem decorators especiais ou classes base.
Edges (Arestas)
Definem a ordem de execução. add_edge(A, B) significa: depois do nó A, execute o nó B. add_conditional_edges significa: depois do nó A, chame uma função de roteamento e vá para onde ela apontar.
Para campos que devem acumular em vez de substituir (como histórico de mensagens), use um reducer. O operator.add em uma lista faz append em vez de replace:
from typing import TypedDict
import operator
class State(TypedDict):
messages: list # Reducer padrão: replace
log: list # Vai acumular com operator.addPasso 3: Gerenciando histórico de conversa com MessagesState
Para um agente conversacional, o estado precisa carregar o histórico completo — entradas do usuário, respostas do modelo, resultados de ferramentas. O LangGraph fornece MessagesState pronto para isso:
from langgraph.graph import MessagesState
# MessagesState é um TypedDict com um único campo 'messages'
# que usa o reducer add_messages (append automático)O reducer add_messages é mais inteligente que um simples append: ele lida com deduplicação e ordenação de objetos de mensagem. Você pode estender o MessagesState com campos adicionais como customer_id ou priority, mas messages já vem pronto para acumular.
Passo 4: Chamando o modelo dentro de um nó
Com o estado definido, o nó central de qualquer agente LangGraph é uma função que passa a lista de mensagens atual para o modelo e anexa a resposta:
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage
model = ChatOpenAI(model="gpt-4o")
def call_model(state: MessagesState):
# SystemMessage define o papel do modelo sem poluir o estado
system_msg = SystemMessage(content="Você é um assistente útil.")
response = model.invoke([system_msg] + state["messages"])
# Retornar a resposta como AIMessage a adiciona ao estado
return {"messages": [response]}Por que funciona: o modelo recebe todo o histórico (state["messages"]), gera uma resposta (AIMessage), e o reducer add_messages a anexa automaticamente. Trocar de provedor significa mudar o import e a string do modelo; o resto do nó permanece idêntico.
Passo 5: Registrando ferramentas e roteando chamadas
Até aqui o modelo responde com conhecimento de treinamento. Para acessar dados específicos — contas, assinaturas, histórico de tickets — você precisa de ferramentas:
from langchain_core.tools import tool
@tool
def get_customer_tier(customer_id: str) -> str:
"""Retorna o tier de assinatura (free, pro, enterprise)
para um determinado customer_id."""
# Simula consulta a banco de dados
tiers = {"cust_1001": "pro", "cust_1002": "enterprise"}
return tiers.get(customer_id, "free")A docstring da função é o que o modelo lê para decidir se deve usar a ferramenta. Seja preciso: docstrings vagas levam a chamadas perdidas ou argumentos malformados.
Agora vincule a ferramenta ao modelo e configure o nó de execução:
from langgraph.prebuilt import ToolNode, tools_condition
model_with_tools = model.bind_tools([get_customer_tier])
def call_model(state: MessagesState):
response = model_with_tools.invoke(state["messages"])
return {"messages": [response]}
# Construir o grafo
from langgraph.graph import StateGraph, START
builder = StateGraph(MessagesState)
builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode([get_customer_tier]))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")
graph = builder.compile()O que acontece no loop: O modelo recebe a mensagem do usuário. Se decidir usar uma ferramenta, retorna AIMessage com tool_calls preenchido em vez de content. O tools_condition detecta isso e roteia para ToolNode, que executa a função com os argumentos que o modelo especificou. A aresta de "tools" de volta para "agent" fecha o loop: o resultado da ferramenta volta ao modelo para ele produzir a resposta final.
Para confirmar que funcionou: execute graph.invoke({"messages": [{"role": "user", "content": "Qual é o tier do cliente cust_1001?"}]}) e verifique que o último AIMessage contém a resposta em content.
Passo 6: Rastreando a cadeia de raciocínio
Entender o que acontece dentro do grafo é essencial para debugging. No loop ReAct (Reasoning + Acting), cada uso de ferramenta produz 4 mensagens no estado:
- HumanMessage: a pergunta original do usuário
- AIMessage com tool_calls: o modelo sinalizando “quero consultar X” (content vazio)
- ToolMessage: o resultado da execução da ferramenta
- AIMessage final: o modelo interpretando o resultado e respondendo (content preenchido)
Custo implícito: cada uso de ferramenta consome duas chamadas de modelo — uma para decidir o que consultar, outra para interpretar o resultado. Isso é relevante ao planejar latência e custo conforme você adiciona mais ferramentas ao agente.
Passo 7: Persistindo conversas entre chamadas com Checkpointer
Sem persistência, cada graph.invoke() começa com estado zerado. Para manter contexto entre mensagens:
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
# Mesmo thread_id = mesma conversa
config = {"configurable": {"thread_id": "conversa-1"}}
result1 = graph.invoke({"messages": [{"role": "user", "content": "Meu nome é João"}]}, config)
result2 = graph.invoke({"messages": [{"role": "user", "content": "Qual é o meu nome?"}]}, config)
# O modelo lembra: "Seu nome é João"O checkpointer restaura o estado do thread_id antes da execução e salva o estado atualizado depois. Threads diferentes têm estados independentes. Para produção, substitua InMemorySaver por um backend persistente como PostgreSQL ou Redis — o resto do código permanece idêntico.
Comparação: LangGraph vs alternativas
| Framework | Modelo mental | Melhor para | Curva de aprendizado |
|---|---|---|---|
| LangGraph | Grafo direcionado com estado | Agentes complexos, multi-step, auditáveis | Média |
| CrewAI | Agentes com papéis e tarefas | Prototipagem rápida multi-agente | Baixa |
| AutoGen | Conversa entre agentes | Sistemas multi-agente conversacionais | Média |
| OpenAI Swarm | Handoff entre agentes | Roteamento simples de agentes | Baixa |
| Raw API calls | Requisições HTTP com loop manual | Máximo controle, mínima dependência | Alta |
Casos de uso reais
- Suporte ao cliente com acesso a banco de dados: agente que consulta tier de assinatura, histórico de pedidos e status de ticket antes de responder, com auditoria completa de cada decisão.
- Assistente de pesquisa jurídica: busca em múltiplas bases de jurisprudência, cruza resultados e gera parecer com citações rastreáveis.
- Pipeline de dados com aprovação humana: agente que processa dados, identifica anomalias e pausa para revisão humana antes de publicar relatórios.
- Onboarding de desenvolvedores: agente que acessa a documentação interna, configura repositórios e gera boilerplate personalizado para novos membros do time.
- Monitoramento de infraestrutura: agente que consulta métricas de cloud, detecta anomalias, escala recursos automaticamente e notifica o time com diagnóstico.
Troubleshooting: 5 erros comuns (e como resolver)
- ❌ TypeError: messages field expects a list
- Causa: o nó retornou um dicionário com
"messages"como string ou objeto único, não como lista.
Solução: sempre retorne{"messages": [response]}com a resposta dentro de uma lista. - ❌ O modelo nunca chama a ferramenta
- Causa: docstring vaga ou ausente na função
@tool. O modelo não entende quando ou por que usá-la.
Solução: escreva uma docstring precisa explicando o que a ferramenta faz e quais argumentos espera. - ❌ ImportError: cannot import name ‘MessagesState’
- Causa: versão antiga do LangGraph (anterior à 0.2.0).
Solução: atualize compip install --upgrade langgraph. - ❌ Graph recusion limit reached
- Causa: loop infinito no grafo — as arestas condicionais nunca levam a
END.
Solução: verifique quetools_conditionroteia para"__end__"quando não há tool calls. Adicionebuilder.add_edge("agent", END)como fallback. - ❌ Memória some entre execuções
- Causa: usando
InMemorySaverem produção ou esquecendo de passar o mesmothread_idno config.
Solução: em desenvolvimento, confirme que othread_idé consistente entre chamadas. Em produção, migre paraSqliteSaverouPostgresSaver.
FAQ: Perguntas que você faria depois de implementar
- Posso usar modelos locais como Llama ou Mistral?
- Sim. Substitua
ChatOpenAIporChatOllamaouChatHuggingFace. O grafo permanece idêntico — apenas o nó do modelo muda. - Quantas ferramentas um agente pode ter?
- Não há limite técnico, mas cada ferramenta adicional ocupa espaço no context window do modelo. Acima de 10-15 ferramentas, considere agrupar por domínio ou usar um roteador.
- Qual a diferença entre Checkpointer e Store?
- Checkpointer persiste o estado do grafo por thread (conversa). Store persiste dados de aplicação compartilhados entre threads, como perfis de usuário ou preferências globais.
- Funciona com streaming de tokens?
- Sim. Use
graph.stream()em vez degraph.invoke()para receber atualizações parciais conforme o modelo gera tokens. - Como implementar human-in-the-loop?
- Adicione um
interrupt_beforeouinterrupt_afterao compilar o grafo. A execução pausa no nó especificado e só continua após aprovação viagraph.update_state().
O futuro dos agentes: para onde estamos indo
O LangGraph estabeleceu um padrão que está sendo adotado por toda a indústria: agentes como grafos auditáveis, com estado explícito e ferramentas plugáveis. Em 2027, a tendência é que esse modelo se torne o padrão para qualquer sistema de IA que precise tomar decisões em cadeia — de chatbots de suporte a pipelines autônomos de descoberta de medicamentos como o que a Bristol Myers Squibb está construindo com a NVIDIA.
O próximo salto será a integração nativa entre agentes e bancos de dados vetoriais, permitindo que o estado do grafo inclua não apenas mensagens, mas embeddings, documentos e conhecimento institucional recuperado em tempo real. Se você domina os primitivos que construímos hoje — state, nodes, edges, tools, checkpointers — está pronto para o que vier.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



