Guia Técnico — Março 2026

LangChain & LangGraph
Guia Completo

TypeScript / JS LangChain 0.3 LangGraph 1.0+ Audience: CPTO
TL;DR — O Que Você Precisa Saber
LCEL
API moderna do LangChain. prompt.pipe(model).pipe(parser)
Use para pipelines fixos & RAG
Depreciado
AgentExecutor foi depreciado no v0.2. Não use em código novo.
Migre para LangGraph
StateGraph
Coração do LangGraph. Grafo dirigido com estado explícito e persistente.
Use para agentes complexos
interrupt()
Human-in-the-loop nativo. Pausa execução e aguarda aprovação.
Requer checkpointer
Send()
Paralelismo dinâmico tipo map-reduce. Processa N itens em paralelo.
Pattern mais poderoso
3 vars
Para ativar LangSmith: LANGCHAIN_TRACING_V2, LANGCHAIN_API_KEY, LANGCHAIN_PROJECT
Observabilidade automática
01

LangChain: O Que É e De Onde Veio

Origem

LangChain foi criado por Harrison Chase em outubro de 2022 como side project na Robust Intelligence. A ideia: eliminar o boilerplate de conectar LLMs com ferramentas externas, memória e pipelines de dados. O projeto viralizou no GitHub, Harrison largou o emprego e fundou a LangChain, Inc. em 2023 — US$25M seed (Benchmark) + US$35M Série A.

Evolução de Versões

VersãoDataMarco Principal
0.0.xOut 2022 – Jan 2024Fase experimental, API instável
0.1.0Jan 2024Primeira versão "estável", início de deprecações
0.2.xMai 2024LangGraph promovido como padrão, AgentExecutor depreciado
0.3.xOut 2024Remove Pydantic v1, callbacks assíncronos por padrão
1.0.x2025/2026Python: migração completa; JS: em andamento

O Ecossistema

┌─────────────────────────────────────────────────────────┐
│                    ECOSSISTEMA LANGCHAIN                 │
├──────────────┬──────────────┬──────────────┬────────────┤
│  LangChain   │  LangGraph   │  LangSmith   │ LangServe  │
│  (framework) │ (orquestrador│(observabilid.│(depreciado)│
│  chains,LCEL │  de agentes) │  e evals)    │            │
└──────────────┴──────────────┴──────────────┴────────────┘
02

Arquitetura Core do LangChain

┌─────────────────────────────────────┐
│              SEU CÓDIGO             │
├─────────────────────────────────────┤
│       LCEL / Chains / Agents        │
├──────────┬──────────┬───────────────┤
│  Model   │ Prompts  │  Tools/Memory │
│   I/O    │          │               │
├──────────┴──────────┴───────────────┤
│      @langchain/core (Runnable API) │
├──────────┬──────────┬───────────────┤
│  OpenAI  │Anthropic │  ...outros    │
└──────────┴──────────┴───────────────┘

Os 6 Componentes Core

1. Model I/O

Interface padronizada para qualquer LLM. Troca de OpenAI para Anthropic sem mudar o resto do código.

import { ChatOpenAI } from "@langchain/openai";
import { ChatAnthropic } from "@langchain/anthropic";

// Mesma interface, provider diferente
const model = new ChatOpenAI({ model: "gpt-4o" });
// const model = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022" });

const response = await model.invoke("Olá, mundo!");
2. Prompts — Templates Parametrizados
import { ChatPromptTemplate } from "@langchain/core/prompts";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "Você é um especialista em {dominio}."],
  ["human", "{pergunta}"],
]);

const formatted = await prompt.format({
  dominio: "marketing digital",
  pergunta: "Como calcular CAC?",
});

Tipos: PromptTemplate (legado), ChatPromptTemplate (padrão), FewShotPromptTemplate, PipelinePromptTemplate

3. Output Parsers — Saída Tipada
import { StructuredOutputParser } from "@langchain/core/output_parsers";
import { z } from "zod";

const parser = StructuredOutputParser.fromZodSchema(
  z.object({
    nome: z.string(),
    score: z.number().min(0).max(10),
    justificativa: z.string(),
  })
);

const formatInstructions = parser.getFormatInstructions();
4. Memory Legado
Atenção: Os componentes de Memory do LangChain core são legados. LangGraph assumiu essa responsabilidade com checkpointing.

Tipos legados: BufferMemory, BufferWindowMemory, SummaryMemory, VectorStoreRetrieverMemory

5. Tools — Funções Chamáveis
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const buscarProdutos = tool(
  async ({ categoria, limite }) => {
    const produtos = await db.produtos.findMany({
      where: { categoria }, take: limite
    });
    return JSON.stringify(produtos);
  },
  {
    name: "buscar_produtos",
    description: "Busca produtos por categoria no catálogo",
    schema: z.object({
      categoria: z.string().describe("Categoria do produto"),
      limite: z.number().default(10),
    }),
  }
);
6. Agents Depreciado
AgentExecutor está depreciado desde LangChain 0.2. Use LangGraph em vez disso (Seções 9-18).

Implementava o loop ReAct: Pergunta → LLM decide → Executa tool → LLM processa → ... — tudo como caixa preta sem controle de fluxo.

03

Como Funciona Por Dentro

Loop de Execução LCEL

invoke(input)
    │
    ▼
[Runnable 1].invoke(input)    → output_1
    │
    ▼
[Runnable 2].invoke(output_1) → output_2
    │
    ▼
[Runnable 3].invoke(output_2) → final_output

Como Tool Calling Funciona

1. Você envia: mensagens + definições de tools (schemas JSON)
   │
   ▼
2. LLM retorna: AIMessage com tool_calls[] (não texto)
   { tool_calls: [{ name: "buscar_produtos", args: {...} }] }
   │
   ▼
3. Seu código executa a tool e captura o resultado
   │
   ▼
4. Você envia: ToolMessage com o resultado
   │
   ▼
5. LLM processa e gera resposta final (ou chama mais tools)

Streaming

const chain = prompt.pipe(model).pipe(parser);

for await (const chunk of await chain.stream({ pergunta: "O que é CAC?" })) {
  process.stdout.write(chunk); // imprime token por token
}

Callbacks

const handler = {
  handleLLMStart: async (llm, messages) => {
    console.log("LLM starting with", messages);
  },
  handleLLMEnd: async (output) => {
    console.log("LLM finished:", output);
  },
  handleToolStart: async (tool, input) => {
    console.log(`Tool ${tool.name} called with:`, input);
  },
};

await chain.invoke(input, { callbacks: [handler] });
Mudança v0.3: Callbacks são agora assíncronos e não-bloqueantes por padrão. Em ambientes serverless (Lambda, Vercel Functions), faça await explícito para garantir que os traces sejam enviados ao LangSmith antes da função terminar.
04

LangChain Expression Language (LCEL)

Introduzido em agosto 2023. Substitui as classes pesadas (LLMChain, SequentialChain, RetrievalQA) por um sistema de pipe operator. Ideia central: qualquer dois componentes que implementem Runnable podem ser conectados com .pipe().

Pipe Syntax

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";

const prompt = ChatPromptTemplate.fromTemplate(
  "Explique {conceito} em uma frase para um CEO"
);
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const parser = new StringOutputParser();

const chain = prompt.pipe(model).pipe(parser);
const resultado = await chain.invoke({ conceito: "NRR" });

A Interface Runnable

interface Runnable<Input, Output> {
  invoke(input: Input, config?: RunnableConfig): Promise<Output>;
  stream(input: Input, config?: RunnableConfig): AsyncGenerator<Output>;
  batch(inputs: Input[], config?: RunnableConfig): Promise<Output[]>;
  pipe<NewOutput>(next: Runnable<Output, NewOutput>): Runnable<Input, NewOutput>;
}

Componentes Utilitários

RunnableLambda — funções simples como Runnables
import { RunnableLambda } from "@langchain/core/runnables";

const formataContexto = RunnableLambda.from(async (docs: Document[]) => {
  return docs.map(d => d.pageContent).join("\n\n");
});

const chain = retriever.pipe(formataContexto).pipe(prompt).pipe(model);
RunnablePassthrough — preserva o input original (essencial para RAG)
import { RunnablePassthrough } from "@langchain/core/runnables";

const chain = RunnablePassthrough.assign({
  context: retriever.pipe(formatDocs),
}).pipe(prompt).pipe(model).pipe(parser);

// Input: { question: "O que é CAC?" }
// Antes do prompt: { question: "...", context: "...docs..." }
RunnableParallel — execução paralela
import { RunnableParallel } from "@langchain/core/runnables";

const parallelChain = new RunnableParallel({
  resumo: chainDeResumo,
  sentimento: chainDeSentimento,
  topicos: chainDeTopicos,
});

const resultado = await parallelChain.invoke(texto);
// → { resumo: "...", sentimento: "positivo", topicos: [...] }

Quando Usar vs. Não Usar LCEL

Use LCEL quando...NÃO use LCEL quando...
Pipelines lineares simplesLógica condicional complexa
RAG com retrieval + geraçãoFluxos cíclicos
Precisa de streaming built-inHuman-in-the-loop
Batch processing automáticoEstado persistente entre chamadas
Compatibilidade LangSmithMúltiplos agentes cooperando
Para lógica condicional, ciclos, HITL ou multi-agent: use LangGraph (Seções 9+).

Exemplo Completo: RAG com LCEL

import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnablePassthrough } from "@langchain/core/runnables";
import { MemoryVectorStore } from "langchain/vectorstores/memory";

const embeddings = new OpenAIEmbeddings();
const vectorStore = await MemoryVectorStore.fromTexts(
  ["CAC é o Custo de Aquisição de Cliente...", "NRR mede retenção..."],
  [{}, {}],
  embeddings
);
const retriever = vectorStore.asRetriever();

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "Use o contexto abaixo para responder.\nContexto:\n{context}"],
  ["human", "{question}"],
]);

const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const parser = new StringOutputParser();

const ragChain = RunnablePassthrough.assign({
  context: ({ question }: { question: string }) =>
    retriever.invoke(question).then(docs => docs.map(d => d.pageContent).join("\n")),
}).pipe(prompt).pipe(model).pipe(parser);

const resposta = await ragChain.invoke({ question: "O que é CAC?" });

// Streaming automático
for await (const token of await ragChain.stream({ question: "O que é NRR?" })) {
  process.stdout.write(token);
}
05

Módulos Principais em Detalhe

Model I/O — Chat Models e Utilitários
import { ChatOpenAI } from "@langchain/openai";
import { ChatAnthropic } from "@langchain/anthropic";
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";

// Todos têm a mesma interface
const openai = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });
const anthropic = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022" });
const gemini = new ChatGoogleGenerativeAI({ model: "gemini-1.5-pro" });

// Utilitários de mensagens (novo no 0.3)
import { trimMessages, filterMessages } from "@langchain/core/messages";

const mensagensOtimizadas = trimMessages(historico, {
  maxTokens: 4096,
  tokenCounter: model.getNumTokens.bind(model),
  strategy: "last", // mantém as mais recentes
});
Retrieval (RAG) — Pipeline Completo
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "@langchain/openai";
import { PineconeStore } from "@langchain/pinecone";
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";

// 1. Load
const loader = new PDFLoader("relatorio-vendas.pdf");
const docs = await loader.load();

// 2. Split
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000, chunkOverlap: 200,
});
const chunks = await splitter.splitDocuments(docs);

// 3. Embed & Store
const vectorStore = await PineconeStore.fromDocuments(
  chunks,
  new OpenAIEmbeddings(),
  { pineconeIndex }
);

// 4. Retrieve com MMR (evita repetição)
const retriever = vectorStore.asRetriever({
  k: 5,
  searchType: "mmr",
});

Tipos de retriever: VectorStoreRetriever, MultiQueryRetriever, ContextualCompressionRetriever, ParentDocumentRetriever, EnsembleRetriever

Chains Legadas → Equivalentes LCEL
Chain (legado)Equivalente LCEL moderno
LLMChainprompt.pipe(model).pipe(parser)
SequentialChainchain1.pipe(chain2).pipe(chain3)
RetrievalQAChainRAG com RunnablePassthrough.assign()
ConversationChainLangGraph com checkpointing
MapReduceDocumentsChainLangGraph com Send()
06

LangChain em TypeScript

Estrutura de Pacotes

# Core — obrigatório sempre
npm install @langchain/core

# Providers — instale só os que usar
npm install @langchain/openai
npm install @langchain/anthropic
npm install @langchain/google-genai

# Utilitários, loaders, splitters
npm install langchain

# LangGraph — para agentes
npm install @langchain/langgraph

# LangSmith — tracing
npm install langsmith
Mudança v0.3: @langchain/core passou a ser peer dependency. Instale explicitamente para evitar conflitos de tipo.

Python vs. TypeScript

AspectoPythonTypeScript/JS
Pipe operatorprompt | model | parserprompt.pipe(model).pipe(parser)
TypingPydantic (runtime)Zod + TypeScript (compile time)
State schemaTypedDict / PydanticAnnotation.Root / Zod
Tool decorator@tool decoratortool() helper function

Integração com Fastify (Streaming)

import Fastify from "fastify";
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

const fastify = Fastify();

// Instancia uma vez — modelos são stateless, seguro compartilhar
const model = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 });

const analisarCampanha = ChatPromptTemplate.fromMessages([
  ["system", "Você é especialista em marketing. Analise campanhas com metodologia TOC."],
  ["human", "Analise: {dados_campanha}"],
]).pipe(model).pipe(new StringOutputParser());

fastify.post("/analisar-campanha", async (request, reply) => {
  const { dadosCampanha } = request.body as { dadosCampanha: string };

  reply.raw.setHeader("Content-Type", "text/event-stream");
  reply.raw.setHeader("Cache-Control", "no-cache");

  for await (const token of await analisarCampanha.stream({
    dados_campanha: dadosCampanha,
  })) {
    reply.raw.write(`data: ${JSON.stringify({ token })}\n\n`);
  }

  reply.raw.end();
});

Variáveis de Ambiente

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

# LangSmith (tracing automático)
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__...
LANGCHAIN_PROJECT=v4mos-ai-prod

# LangGraph Cloud (se usar)
LANGGRAPH_API_KEY=...
07

Quando LangChain Ajuda vs. Quando Atrapalha

Casos onde LangChain genuinamente ajuda
  • Troca de providers sem reescrever código: A interface unificada de ChatModel é genuinamente útil — trocar OpenAI por Anthropic é uma linha.
  • RAG padronizado: O pipeline loader → splitter → embeddings → vector store está bem testado e documentado.
  • Integração com LangSmith: Tracing automático de todas as chamadas sem instrumentação manual.
  • Prototipagem rápida: Para POCs, LCEL é expressivo e rápido de escrever.
  • Equipe que já conhece o framework: Reduz curva de aprendizado em onboarding.
Casos onde LangChain atrapalha

1. Abstrações Leaky

O team da OctoClaw reportou em 2024:

"Passamos mais tempo entendendo e debugando o LangChain do que construindo features. Quando precisávamos de comportamento de baixo nível, o framework intencionalmente abstrai tantos detalhes que frequentemente não era possível escrever o código de baixo nível necessário."

2. Latência das Abstrações

O stack de callbacks pode adicionar 100–500ms por chamada. Para pipelines de produção com SLA apertado, isso é relevante.

3. Breaking Changes Frequentes

De 0.0 para 0.1 para 0.2 para 0.3: breaking changes significativas a cada versão. Equipes sem dependências pinadas tiveram surpresas desagradáveis.

4. Documentação Fragmentada

Com tantos pacotes separados e versões diferentes, encontrar a documentação correta para a versão em uso é trabalhoso.

Guia de Decisão Rápida

Tarefa simples (prompt + resposta)?
    → OpenAI SDK direto ou LCEL simples

RAG com pipeline padronizado?
    → LangChain (LCEL + Retrieval)

Agente com tools e estado?
    → LangGraph (não AgentExecutor)

Multi-agentes ou workflows complexos?
    → LangGraph

Precisa de observabilidade completa?
    → LangSmith + LangGraph

Microserviço com latência crítica?
    → Considere SDK direto + instrumentação manual
08

Use Cases com Código

8.1 RAG Chatbot com Histórico
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnablePassthrough } from "@langchain/core/runnables";
import { HumanMessage, AIMessage } from "@langchain/core/messages";

const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const retriever = vectorStore.asRetriever({ k: 4 });

const contextualizeQPrompt = ChatPromptTemplate.fromMessages([
  ["system", `Dado o histórico de chat, reformule a pergunta para ser standalone.
   Se já for standalone, retorne como está.`],
  new MessagesPlaceholder("chat_history"),
  ["human", "{input}"],
]);

const historyAwareRetriever = contextualizeQPrompt
  .pipe(model)
  .pipe(new StringOutputParser())
  .pipe(retriever);

const qaPrompt = ChatPromptTemplate.fromMessages([
  ["system", "Responda usando o contexto abaixo.\nContexto: {context}"],
  new MessagesPlaceholder("chat_history"),
  ["human", "{input}"],
]);

const ragChain = RunnablePassthrough.assign({
  context: ({ input, chat_history }: any) =>
    historyAwareRetriever
      .invoke({ input, chat_history })
      .then(docs => docs.map((d: any) => d.pageContent).join("\n")),
}).pipe(qaPrompt).pipe(model).pipe(new StringOutputParser());

// Uso com histórico
const chatHistory: (HumanMessage | AIMessage)[] = [];
const resposta = await ragChain.invoke({
  input: "O que é a trava de ICP?",
  chat_history: chatHistory,
});
8.2 Extração Estruturada com Zod
import { ChatAnthropic } from "@langchain/anthropic";
import { z } from "zod";

const model = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022" });

const LeadSchema = z.object({
  nome: z.string().describe("Nome da empresa"),
  setor: z.enum(["tech", "varejo", "saúde", "educação", "outro"]),
  funcionarios: z.number().nullable(),
  problemas: z.array(z.string()).describe("Problemas mencionados"),
  urgencia: z.enum(["alta", "media", "baixa"]),
  proximo_passo: z.string(),
});

// withStructuredOutput força output no schema
const extractor = model.withStructuredOutput(LeadSchema, {
  name: "extrair_lead",
});

const lead = await extractor.invoke([{
  role: "user",
  content: `Extraia as informações: TechCorp, e-commerce, ~200 pessoas,
  dificuldade séria em medir ROAS, quer resolver no próximo trimestre...`,
}]);
// → { nome: "TechCorp", setor: "tech", funcionarios: 200, urgencia: "alta", ... }
8.3 Agente com Tools (loop manual — casos simples)
const calcularROAS = tool(
  async ({ receita, gasto }) => {
    return `ROAS: ${(receita/gasto).toFixed(2)}x`;
  },
  {
    name: "calcular_roas",
    description: "Calcula o ROAS (Return on Ad Spend)",
    schema: z.object({
      receita: z.number(),
      gasto: z.number(),
    }),
  }
);

const modelWithTools = model.bindTools([calcularROAS]);
const messages = [new HumanMessage(
  "Qual o ROAS com R$50k de receita e R$15k de gasto?"
)];

let response = await modelWithTools.invoke(messages);

while (response.tool_calls?.length > 0) {
  messages.push(response);
  for (const toolCall of response.tool_calls) {
    const result = await calcularROAS.invoke(toolCall.args);
    messages.push({ role: "tool", content: result, tool_call_id: toolCall.id } as any);
  }
  response = await modelWithTools.invoke(messages);
}
// → "O ROAS é de 3.33x..."
Para agentes mais complexos com estado persistente, HITL ou multi-agent, use LangGraph (próximas seções).
09

LangGraph: O Que É e Por Que Foi Criado

O Problema que o LangGraph Resolve

LangGraph NÃO é parte do LangChain. É um framework independente (MIT) que pode usar componentes LangChain (ChatModels, tools), mas não requer. Tornou-se o padrão recomendado desde LangChain 0.2.

A Ideia Central

Agentes são grafos dirigidos onde:

Empresas usando em Produção

KlarnaVantaRippling LyftHarveyAbridge CloudflareHome DepotWorkday CiscoElasticLinkedIn CoinbaseServiceNow
10

Conceitos Fundamentais do LangGraph

StateGraph — O Coração

import { StateGraph, START, END } from "@langchain/langgraph";

const grafo = new StateGraph(MeuSchema)
  .addNode("processar", funcaoProcessar)
  .addNode("validar", funcaoValidar)
  .addEdge(START, "processar")
  .addEdge("processar", "validar")
  .addEdge("validar", END)
  .compile();

const resultado = await grafo.invoke({ input: "dados" });

Nodes — O Trabalho Real

// Assinatura de um node
async function meuNode(
  state: typeof MeuSchema.State,
  config?: RunnableConfig
): Promise<Partial<typeof MeuSchema.State>> {
  const resultado = await model.invoke(state.messages);
  // Retorna apenas as keys que mudaram
  return { messages: [resultado] };
}
Regra fundamental: nodes retornam atualizações parciais, não o estado completo. O LangGraph faz merge automático via reducers.

Edges — Tipos

Edge Normal, Conditional Edge e Send
// Edge normal — transição fixa
graph.addEdge("nodeA", "nodeB");

// Conditional edge — decide com base no estado
function roteador(state: typeof MeuSchema.State): string {
  if (state.precisaAprovacao) return "solicitar_aprovacao";
  if (state.erros.length > 0) return "corrigir_erros";
  return "finalizar";
}
graph.addConditionalEdges("processar", roteador);

// Send — paralelismo dinâmico (map-reduce)
import { Send } from "@langchain/langgraph";

function distribuirTarefas(state: typeof GraphState.State) {
  return state.itens.map(item =>
    new Send("processar_item", { item })
  );
}
graph.addConditionalEdges("gerar_itens", distribuirTarefas, ["processar_item"]);
11

Como Funciona o Estado no LangGraph

O estado é composto de channels. Cada channel tem um tipo TypeScript, um reducer (define como novos valores se fundem) e um valor padrão.

Definindo Estado com Annotation.Root (padrão atual)

import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";

const EstadoAgente = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (x, y) => x.concat(y), // APPEND — cada node adiciona ao array
    default: () => [],
  }),
  etapa: Annotation<string>({
    reducer: (_, y) => y, // LAST WRITE WINS — substitui
    default: () => "inicio",
  }),
  aprovado: Annotation<boolean>({
    reducer: (_, y) => y,
    default: () => false,
  }),
});

Forma Moderna com StateSchema + Zod

import { StateSchema, MessagesValue, ReducedValue } from "@langchain/langgraph";
import { z } from "zod";

const EstadoCampanha = new StateSchema({
  messages: MessagesValue, // reducer built-in para mensagens

  errosEncontrados: new ReducedValue(z.array(z.string()), {
    reducer: (atual, novo) => [...atual, ...novo], // append
    default: () => [],
  }),

  etapaAtual: z.string().default("inicio"),  // last write wins
  aprovado: z.boolean().default(false),
});

Como o Estado Flui

Node A executa → retorna { messages: [nova_msg] }
    │
    ▼
Reducer: estado.messages = concat(atual, [nova_msg])
    │
    ▼
Node B executa → retorna { etapa: "processando" }
    │
    ▼
Reducer: estado.etapa = "processando"  (last write wins)
    │
    ▼
... e assim por diante

Super-Steps (Modelo Pregel)

LangGraph usa o modelo Pregel do Google. Cada "super-step" é uma rodada onde múltiplos nós podem executar em paralelo. Após o super-step, o estado é consolidado via reducers — isso habilita o paralelismo com Send().

12

Patterns Essenciais do LangGraph

Pattern 1: Single Agent com Tools
import { StateGraph, START, END, MessagesAnnotation } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { AIMessage } from "@langchain/core/messages";

const tools = [buscarMetricas];
const model = new ChatOpenAI({ model: "gpt-4o" }).bindTools(tools);

async function agente(state: typeof MessagesAnnotation.State) {
  const resposta = await model.invoke(state.messages);
  return { messages: [resposta] };
}

function deveContiunar(state: typeof MessagesAnnotation.State) {
  const ultimaMensagem = state.messages[state.messages.length - 1] as AIMessage;
  if (ultimaMensagem.tool_calls?.length > 0) return "tools";
  return END;
}

const grafo = new StateGraph(MessagesAnnotation)
  .addNode("agente", agente)
  .addNode("tools", new ToolNode(tools))
  .addEdge(START, "agente")
  .addConditionalEdges("agente", deveContiunar)
  .addEdge("tools", "agente") // após tools, volta pro agente
  .compile();
Pattern 2: Human-in-the-Loop com interrupt()
import { MemorySaver, interrupt, Command } from "@langchain/langgraph";

async function solicitarAprovacao(state: typeof EstadoCampanha.State) {
  // interrupt() pausa a execução e retorna ao chamador
  const resposta = await interrupt({
    mensagem: `Campanha gerada:\n${JSON.stringify(state.campanha)}\n\nAprova?`,
    tipo: "aprovacao_campanha",
  });
  return { aprovado: resposta === "sim" };
}

// Checkpointer é OBRIGATÓRIO para HITL
const checkpointer = new MemorySaver();
const grafo = new StateGraph(EstadoCampanha)
  .addNode("gerar_campanha", gerarCampanha)
  .addNode("solicitar_aprovacao", solicitarAprovacao)
  .addNode("publicar", publicarCampanha)
  .addEdge(START, "gerar_campanha")
  .addEdge("gerar_campanha", "solicitar_aprovacao")
  .addConditionalEdges("solicitar_aprovacao",
    (state) => state.aprovado ? "publicar" : END
  )
  .compile({ checkpointer });

const config = { configurable: { thread_id: "campanha-123" } };

// Passo 1: executa até o interrupt
const resultado1 = await grafo.invoke(
  { messages: [{ role: "user", content: "Crie campanha para produto X" }] },
  config
);

// Passo 2: após aprovação humana, retoma
const resultado2 = await grafo.invoke(
  new Command({ resume: "sim" }),
  config
);
Pattern 3: Multi-Agent com Supervisor
import { createSupervisor } from "@langchain/langgraph-supervisor";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

const model = new ChatOpenAI({ model: "gpt-4o" });

// Agentes especialistas
const agenteAnalise = createReactAgent({
  llm: model, tools: [buscarMetricas, gerarGrafico],
  name: "agente_analise",
  prompt: "Você é especialista em análise de dados de marketing.",
});

const agenteCopy = createReactAgent({
  llm: model, tools: [buscarBestPractices, gerarCopy],
  name: "agente_copy",
  prompt: "Você é copywriter especializado em marketing digital.",
});

const agenteMidia = createReactAgent({
  llm: model, tools: [configurarBid, selecionarAudiencia],
  name: "agente_midia",
  prompt: "Você é especialista em mídia paga.",
});

const supervisorWorkflow = createSupervisor({
  agents: [agenteAnalise, agenteCopy, agenteMidia],
  llm: model,
  prompt: `Coordene uma equipe de especialistas em marketing.
  - Performance → agente_analise
  - Conteúdo → agente_copy
  - Mídia → agente_midia`,
  outputMode: "last_message",
});

const app = supervisorWorkflow.compile({ checkpointer: new MemorySaver() });
Pattern 4: Subgraphs — Módulos Reutilizáveis
// Sub-grafo encapsulado
const subgrafoEnriquecimento = new StateGraph(EnriquecimentoState)
  .addNode("buscar_linkedin", buscarLinkedin)
  .addNode("buscar_cnpj", buscarCNPJ)
  .addNode("score_lead", calcularScore)
  .addEdge(START, "buscar_linkedin")
  .addEdge("buscar_linkedin", "buscar_cnpj")
  .addEdge("buscar_cnpj", "score_lead")
  .addEdge("score_lead", END)
  .compile();

// Grafo principal usa subgrafo como nó
// + Send() para processar N leads em paralelo
function distribuirLeads(state: any) {
  return state.leads.map((lead: any) =>
    new Send("enriquecer_lead", { lead })
  );
}

const grafoPrincipal = new StateGraph(GrafoPrincipalState)
  .addNode("enriquecer_lead", enriquecerLead)
  .addConditionalEdges(START, distribuirLeads, ["enriquecer_lead"])
  .addEdge("enriquecer_lead", END)
  .compile();
Pattern 5: Map-Reduce Paralelo com Send()
const MapReduceState = Annotation.Root({
  topicos: Annotation<string[]>({ reducer: (_, y) => y, default: () => [] }),
  // Reducer append — agrega resultados de múltiplos nós paralelos
  analises: Annotation<string[]>({
    reducer: (x, y) => x.concat(y),
    default: () => [],
  }),
  relatorioFinal: Annotation<string>({ reducer: (_, y) => y, default: () => "" }),
});

// Executa para CADA tópico em paralelo
async function analisarTopico(state: { topico: string }) {
  const analise = await model.invoke([
    { role: "user", content: `Analise a trava de receita: ${state.topico}` },
  ]);
  return { analises: [analise.content as string] };
}

const grafoMapReduce = new StateGraph(MapReduceState)
  .addNode("analisar_topico", analisarTopico)
  .addNode("gerar_relatorio", gerarRelatorio)
  .addConditionalEdges(START,
    (state) => state.topicos.map(t => new Send("analisar_topico", { topico: t })),
    ["analisar_topico"]
  )
  .addEdge("analisar_topico", "gerar_relatorio")
  .addEdge("gerar_relatorio", END)
  .compile();

const resultado = await grafoMapReduce.invoke({
  topicos: ["ICP mal definido", "Funil sem métricas", "Follow-up inexistente"],
});
13

Checkpointing e Persistência

No LangGraph, checkpointing não é feature opcional — é a base de human-in-the-loop, resiliência, conversas multi-turn e time travel para debug.

Escolhendo o Checkpointer

CheckpointerQuando Usar
MemorySaverDev Desenvolvimento, testes, demos. Volátil — perde tudo ao reiniciar.
SqliteSaverProd Low Produção de baixa escala, instância única.
PostgresSaverProd Produção multi-instância, alta disponibilidade.
CustomRedis, DynamoDB, ou storage proprietário.
PostgresSaver — Produção em Escala
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import pg from "pg";

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
});

const checkpointer = new PostgresSaver(pool);
await checkpointer.setup(); // cria as tabelas necessárias

const grafo = workflow.compile({ checkpointer });
Thread Management e Time Travel
// Listar checkpoints de um thread (histórico completo)
const historico = grafo.getStateHistory(config);
for await (const checkpoint of historico) {
  console.log(checkpoint.metadata.step, checkpoint.values);
}

// Pegar o estado atual
const estadoAtual = await grafo.getState(config);

// "Time travel" — retomar de checkpoint anterior
const configAntigo = {
  configurable: {
    thread_id: "user-123",
    checkpoint_id: "checkpoint-abc123",
  },
};
const resultadoRetomado = await grafo.invoke(null, configAntigo);
14

LangGraph em TypeScript

Pacotes e Instalação

npm install @langchain/langgraph @langchain/core
npm install @langchain/openai @langchain/anthropic
npm install @langchain/langgraph-checkpoint-sqlite
npm install @langchain/langgraph-checkpoint-postgres

Streaming com LangGraph em Fastify

fastify.post("/chat/:threadId", async (request, reply) => {
  const { threadId } = request.params as { threadId: string };
  const { message } = request.body as { message: string };
  const config = { configurable: { thread_id: threadId } };

  reply.raw.setHeader("Content-Type", "text/event-stream");
  reply.raw.setHeader("Cache-Control", "no-cache");
  reply.raw.setHeader("Connection", "keep-alive");

  const stream = await grafo.streamEvents(
    { messages: [{ role: "user", content: message }] },
    { ...config, version: "v2" }
  );

  for await (const event of stream) {
    if (event.event === "on_chat_model_stream") {
      const chunk = event.data?.chunk;
      if (chunk?.content) {
        reply.raw.write(
          `data: ${JSON.stringify({ type: "token", content: chunk.content })}\n\n`
        );
      }
    }
    if (event.event === "on_tool_start") {
      reply.raw.write(
        `data: ${JSON.stringify({ type: "tool_start", tool: event.name })}\n\n`
      );
    }
  }

  reply.raw.write(`data: ${JSON.stringify({ type: "done" })}\n\n`);
  reply.raw.end();
});

Python vs TypeScript no LangGraph

AspectoPythonTypeScript
State schemaTypedDict / PydanticStateSchema + Zod / Annotation.Root
Interruptinterrupt() / NodeInterruptinterrupt()
Checkpointer asyncAsyncSqliteSaverSqliteSaver
Packagelanggraph@langchain/langgraph
15

LangGraph Cloud / Platform

LangGraph Platform resolve problemas de infraestrutura: escalabilidade horizontal, checkpoints gerenciados, filas para agentes long-running, Studio para debug visual e APIs REST automáticas.

LangGraph CLI

npm install -g @langchain/langgraph-cli

langgraph new meu-agente   # cria projeto
langgraph dev              # desenvolvimento com hot reload
langgraph build            # build para produção
langgraph deploy           # deploy para LangGraph Cloud

Self-Hosted com Docker

docker run -p 8123:8123 \
  -e DATABASE_URL=postgresql://... \
  -e LANGCHAIN_API_KEY=... \
  langchain/langgraph-api

LangGraph Studio

IDE visual (desktop app) para: visualizar o grafo, inspecionar estado a cada step, "time travel" (voltar checkpoints anteriores), editar estado manualmente e debugar com breakpoints visuais.

16

LangSmith: Observabilidade

Setup em 2 Minutos

# .env — com essas 3 vars, TODO código LangChain/LangGraph
# envia traces ao LangSmith automaticamente
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__seu_api_key_aqui
LANGCHAIN_PROJECT=v4mos-ai-producao
Com essas variáveis setadas, qualquer código LangChain ou LangGraph automaticamente envia traces ao LangSmith. Sem código adicional.
Tracing Manual (para código sem LangChain)
import { traceable } from "langsmith/traceable";
import { wrapOpenAI } from "langsmith/wrappers";
import OpenAI from "openai";

const openai = wrapOpenAI(new OpenAI()); // wrapa para tracing automático

const minhaFuncao = traceable(
  async (input: string) => {
    const response = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [{ role: "user", content: input }],
    });
    return response.choices[0].message.content;
  },
  { name: "minha_funcao", project_name: "v4mos-ai" }
);
Avaliação Automatizada com LangSmith
import { evaluate } from "langsmith/evaluation";

await evaluate(
  async (input) => ragChain.invoke({ question: input.question }),
  {
    data: "dataset-perguntas-marketing",
    evaluators: [
      {
        evaluatorType: "labeled_criteria",
        criteria: "relevance",
        feedbackKey: "relevancia",
      },
      async (run, example) => {
        const score = calcularSeuScore(
          run.outputs?.output, example.outputs?.answer
        );
        return { key: "accuracy", score };
      },
    ],
    experimentPrefix: "rag-v2",
  }
);
17

Use Cases Reais com LangGraph

17.1 Agente de Customer Support com Escalonamento
const SupportState = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (x, y) => x.concat(y), default: () => [],
  }),
  categoriaTicket: Annotation<string | null>({
    reducer: (_, y) => y, default: () => null,
  }),
  escalonado: Annotation<boolean>({
    reducer: (_, y) => y, default: () => false,
  }),
});

function decidirFluxo(state: typeof SupportState.State) {
  if (state.categoriaTicket === "reclamacao_grave") return "escalonar";
  if (state.categoriaTicket === "cancelamento") return "escalonar";
  return "responder";
}

const supportAgent = new StateGraph(SupportState)
  .addNode("classificar", classificarTicket)
  .addNode("responder", responderAutomaticamente)
  .addNode("escalonar", escalonarHumano)
  .addEdge(START, "classificar")
  .addConditionalEdges("classificar", decidirFluxo)
  .addEdge("responder", END)
  .addEdge("escalonar", END)
  .compile({ checkpointer: new PostgresSaver(pool) });
17.2 Agente de Pesquisa (Deep Research)
const ResearchState = Annotation.Root({
  pergunta: Annotation<string>({ reducer: (_, y) => y, default: () => "" }),
  subperguntas: Annotation<string[]>({ reducer: (_, y) => y, default: () => [] }),
  resultadosPesquisa: Annotation<Record<string, string>>({
    reducer: (x, y) => ({ ...x, ...y }),
    default: () => ({}),
  }),
  relatorio: Annotation<string>({ reducer: (_, y) => y, default: () => "" }),
});

const researchAgent = new StateGraph(ResearchState)
  .addNode("decompor", decomporPergunta)        // decompõe em sub-perguntas
  .addNode("pesquisar", pesquisarSubpergunta)   // pesquisa cada uma
  .addNode("sintetizar", sintetizarRelatorio)   // sintetiza tudo
  .addEdge(START, "decompor")
  .addConditionalEdges("decompor", distribuirPesquisas, ["pesquisar"])
  .addEdge("pesquisar", "sintetizar")
  .addEdge("sintetizar", END)
  .compile();
17.3 Agente de Campanha V4MOS.AI (caso real)
const marketingAgent = new StateGraph(CampaignState)
  .addNode("diagnosticar", diagnosticarTravas)     // identifica as 8 travas TOC
  .addNode("gerar_campanha", gerarCampanha)        // gera copy + audiência + bid
  .addNode("aguardar_aprovacao", aguardarAprovacao) // HITL: interrupt()
  .addNode("publicar", publicarCampanha)           // publica no Meta + Google
  .addEdge(START, "diagnosticar")
  .addEdge("diagnosticar", "gerar_campanha")
  .addEdge("gerar_campanha", "aguardar_aprovacao")
  .addConditionalEdges("aguardar_aprovacao",
    (state) => state.aprovada ? "publicar" : END
  )
  .addEdge("publicar", END)
  .compile({ checkpointer: new PostgresSaver(pool) });
Este é o blueprint direto para o agente central do V4MOS.AI: diagnóstico de travas TOC → geração de campanha → aprovação humana → publicação automática.
18

Migração: AgentExecutor → LangGraph

O LangGraph é "a surgical swap, not a risky rewrite". Benefícios: controle total do fluxo, persistência nativa, HITL sem gambiarras, observabilidade melhor no LangSmith, multi-agent nativo.

Antes vs Depois

Antes: AgentExecutor (legado)
import { AgentExecutor, createOpenAIToolsAgent } from "langchain/agents";

// Caixa preta — você não controla o loop
const agent = await createOpenAIToolsAgent({ llm: model, tools, prompt });
const agentExecutor = new AgentExecutor({ agent, tools });
const resultado = await agentExecutor.invoke({ input: "Analise o ROAS" });
Depois: LangGraph StateGraph (moderno)
import { StateGraph, START, END, MessagesAnnotation } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";

const model = new ChatOpenAI({ model: "gpt-4o" }).bindTools(tools);

async function chamarModelo(state: typeof MessagesAnnotation.State) {
  const resposta = await model.invoke(state.messages);
  return { messages: [resposta] };
}

function decidirProximoPasso(state: typeof MessagesAnnotation.State) {
  const ultima = state.messages[state.messages.length - 1] as AIMessage;
  return ultima.tool_calls?.length ? "tools" : END;
}

const agente = new StateGraph(MessagesAnnotation)
  .addNode("agente", chamarModelo)
  .addNode("tools", new ToolNode(tools))
  .addEdge(START, "agente")
  .addConditionalEdges("agente", decidirProximoPasso)
  .addEdge("tools", "agente")
  .compile({ checkpointer: new MemorySaver() }); // agora tem persistência!

Atalho: createReactAgent

import { createReactAgent } from "@langchain/langgraph/prebuilt";

// Drop-in replacement para AgentExecutor
const agente = createReactAgent({
  llm: model,
  tools: [buscarMetricas, calcularROAS],
  checkpointSaver: new MemorySaver(),
});

const resultado = await agente.invoke({
  messages: [{ role: "user", content: "Analise o ROAS" }],
});

Checklist de Migração

19

Tabela de Comparação Final

Aspecto LCEL / Chains AgentExecutor LangGraph
Status Ativo Depreciado v0.2 Ativo
Caso de uso idealPipelines fixos / RAGEvitarAgentes e workflows complexos
Fluxo de controleLinear / paralelo fixoLoop automático (caixa preta)Grafo dirigido — controle total
EstadoStateless por padrãoImplícitoExplícito e persistente
PersistênciaManualManual e limitadaNativa via checkpointing
Human-in-the-loopNão suportadoNão suportadoNativo com interrupt()
Multi-agentNãoNãoNativo (supervisor, swarm)
ParalelismoRunnableParallelNãoSend() para map-reduce
DebugDifícilMuito difícilLangGraph Studio (visual)
StreamingNativo stream()LimitadoNativo streamEvents()
TypeScript typingBoaLimitadaExcelente
Complexidade de códigoBaixa–MédiaBaixa (opaca)Média–Alta
SubgrafosNãoNãoSim

Guia de Decisão Final

Sua task:
    │
    ├── "Prompt + resposta simples"
    │   → LCEL: prompt.pipe(model).pipe(parser)
    │
    ├── "RAG com retrieval + geração"
    │   → LCEL com Retrieval
    │
    ├── "Extração de dados estruturados"
    │   → LCEL com model.withStructuredOutput()
    │
    ├── "Agente que decide quais tools chamar"
    │   ├── Simples, sem persistência?
    │   │   → createReactAgent (LangGraph prebuilt)
    │   └── Com estado, HITL, ou multi-turn?
    │       → StateGraph customizado
    │
    ├── "Múltiplos agentes cooperando"
    │   → LangGraph + createSupervisor
    │
    ├── "Pausar e esperar aprovação humana"
    │   → LangGraph com interrupt() + checkpointing
    │
    ├── "Processar 100 documentos em paralelo e agregar"
    │   → LangGraph com Send() (map-reduce)
    │
    └── "Tenho AgentExecutor legado"
        → Migrar: use createReactAgent como passo 1

Referências: Blog LangChain v0.3 · LangGraph JS Docs · DeepWiki LangGraph JS · Microsoft Educator Blog · Aurelio AI LCEL · OctoClaw/Octomind · Focused.io Migration Guide · DEV.to LangGraph Concepts · LangSmith Observability Docs

V4MOS.AI Research · Março 2026 · Versões: LangChain 0.3 / LangGraph 1.0+