Olha só o problema: 15 minutos de conversa pra fechar um agendamento

Imagine a cena: você manda mensagem pra uma empresa de limpeza. Eles perguntam o tamanho do sofá, o material, pedem foto, seu endereço. Aí você pergunta sobre limpeza de apartamento e começa OUTRA rodada de perguntas. Preço, negociação, horário… quando você vê, passaram 15 minutos.

Isso é o tipo de fluxo que grita por automação. Mas um chatbot comum não resolve — ele não lembra do contexto entre mensagens, não extrai dados estruturados e não orquestra etapas. É aí que entra o LangGraph: um grafo com estado compartilhado, onde cada nó lê e atualiza o estado, e arestas condicionais decidem o próximo passo.

Neste tutorial a gente vai construir um agente que:

  • Entende a query em linguagem natural e extrai os dados do agendamento.
  • Calcula o preço a partir dos dados estruturados.
  • Lida com aceite ou recusa do cliente.
  • Propõe horários e confirma o agendamento.

E claro: vamos plugar o Langfuse pra ter observabilidade total. Porque em produção, se você não enxerga o que o agente está fazendo, você tá no escuro. Quer ver como function calling on-device está evoluindo? Dá uma olhada no nosso post sobre Google AI Edge e function calling cross-platform.

O código-fonte completo tá no GitHub — clona lá e testa! 🚀

AI chatbot interface handling a multi-step cleaning service booking conversation with state management Developer Related Image

Mão na massa: estado, nós e roteamento condicional

O coração do agente é um TypedDict de estado que flui pelo grafo. Cada nó retorna uma atualização parcial, e funções de roteamento inspecionam o estado pra decidir o próximo nó.

from typing import TypedDict, Optional, List
from langgraph.graph import StateGraph, END

class BookingDetails(TypedDict, total=False):
    service_type: str          # ex: "sofá", "apartamento"
    size: Optional[str]
    material: Optional[str]
    address: Optional[str]
    preferred_days: Optional[List[str]]
    price: Optional[float]
    confirmed: Optional[bool]

class AgentState(TypedDict):
    messages: List[dict]
    booking: BookingDetails
    next_action: Optional[str]

# --- Nó: extrai detalhes da conversa ---
def extract_details(state: AgentState) -> dict:
    # Na prática, chame um LLM com structured output aqui
    # Pra demo, simulamos a extração
    last_msg = state["messages"][-1]["content"].lower()
    booking = state.get("booking", {})
    if "sofá" in last_msg or "sofa" in last_msg:
        booking["service_type"] = "sofá"
    if "apartamento" in last_msg:
        booking["service_type"] = "apartamento"
    return {"booking": booking}

# --- Nó: calcula preço a partir dos dados estruturados ---
def calculate_price(state: AgentState) -> dict:
    booking = state["booking"]
    base = 250.0 if booking.get("service_type") == "sofá" else 600.0  # R$
    if booking.get("material") == "couro":
        base *= 1.2
    return {"booking": {**booking, "price": base}}

# --- Roteamento: temos info suficiente pra precificar? ---
def route_after_extract(state: AgentState) -> str:
    b = state["booking"]
    required = ["service_type", "address"]
    if all(b.get(k) for k in required):
        return "calculate_price"
    return END  # espera a próxima mensagem do cliente

# --- Monta o grafo ---
builder = StateGraph(AgentState)
builder.add_node("extract_details", extract_details)
builder.add_node("calculate_price", calculate_price)
builder.set_entry_point("extract_details")
builder.add_conditional_edges("extract_details", route_after_extract, {
    "calculate_price": "calculate_price",
    END: END,
})
builder.add_edge("calculate_price", END)

# Compila com checkpointer pra persistir entre turnos
from langgraph.checkpoint.memory import MemorySaver
graph = builder.compile(checkpointer=MemorySaver())

Pontos-chave:

  • O checkpointer é o que torna o agente stateful entre mensagens — essencial pra um fluxo que dura minutos ou horas.
  • A função de roteamento é onde você codifica a regra de negócio: "só pergunta de novo se faltar algo".
  • Nós são funções puras de estado → estado parcial, fáceis de testar e raciocinar.

Quer ver mais sobre features modernas de web platform que combinam com esses backends de agente? Confere nosso apanhado sobre CSS Quake, Gap Decorations e o futuro das features de web platform.

Python code editor showing LangGraph state schema and node definitions for a booking agent System Abstract Visual

Observabilidade com Langfuse (e os perrengues que ninguém te conta)

Com o agente rodando, a pergunta muda de "funciona?" pra "o que ele tá fazendo de verdade?". O Langfuse é uma plataforma open-source de engenharia de LLM pra tracing, monitoramento e avaliação. O plano free é mais que suficiente pra protótipo.

import os
from langfuse.callback import CallbackHandler

# Coloque no seu .env:
# LANGFUSE_PUBLIC_KEY=pk-lf-...
# LANGFUSE_SECRET_KEY=sk-lf-...
# LANGFUSE_HOST=https://cloud.langfuse.com

langfuse_handler = CallbackHandler(
    public_key=os.getenv("LANGFUSE_PUBLIC_KEY"),
    secret_key=os.getenv("LANGFUSE_SECRET_KEY"),
    host=os.getenv("LANGFUSE_HOST"),
)

# Anexa em qualquer invocação LangChain/LangGraph
result = graph.invoke(
    {"messages": [{"role": "user", "content": "Preciso limpar um sofá de couro, 3 lugares, na Rua X, 123."}]},
    config={"callbacks": [langfuse_handler], "configurable": {"thread_id": "cliente-42"}},
)

No dashboard do Langfuse você vê cada prompt, input, output, contagem de tokens e custo por trace. Esse último item importa mais do que a galera admite — um agente descontrolado pode queimar seu orçamento de API sem você perceber. 💸

⚠️ Limitações e armadilhas

  • Extração estruturada é frágil. LLMs às vezes esquecem campos ou alucinam. Sempre valide com schemas Pydantic antes de mandar pra lógica de preço.
  • Drift no schema de estado. Conforme você adiciona features (ex: agendamentos recorrentes), o TypedDict cresce. Versione ou migre com cuidado — threads antigas com schema velho quebram.
  • Preço determinístico ≠ agente determinístico. Mantenha a função de preço pura e separada do LLM. Nunca deixe o modelo "inventar" preço.
  • Checkpointer importa. MemorySaver serve pra demo, mas produção precisa de store persistente (Redis, Postgres) pra sobreviver a restarts.
  • Tracking de custo não é opcional. Sem Langfuse (ou equivalente), você descobre a fatura de tokens do jeito difícil.

🧭 Próximos passos

  1. Adicione uma UI real — Streamlit ou um frontend React leve pra substituir o CLI.
  2. Persista o estado no Postgres — troque MemorySaver por um checkpointer de produção.
  3. Integre calendários — conecte com Google Calendar ou Calendly pra propor horários reais.
  4. Human-in-the-loop — pra casos exóticos (pedidos fora do padrão), escale pra um humano via interrupt do LangGraph.
  5. Avalie com datasets do Langfuse — monte uma suíte de regressão com conversas de exemplo pra pegar regressões de prompt.

Langfuse observability dashboard displaying token usage and trace logs for an AI agent Coding Session Visual

Fechando a conta

Você viu como construir um agente stateful e multi-etapa que vai muito além de um chatbot. LangGraph cuidando do estado e da orquestração + Langfuse cuidando da observabilidade = uma base que você consegue realmente colocar em produção, não só fazer demo.

A ideia central é simples: deixe o LLM cuidar da linguagem, deixe o código determinístico cuidar das regras de negócio, e deixe o grafo cuidar da orquestração. Essa separação é o que torna agentes confiáveis o suficiente pra produção.

Comece pelo CLI de demo, instrumente com Langfuse, e itere pra uma UI real com storage persistente. A ligação de 15 minutos não tem a menor chance. 💪

Leitura complementar:

Fonte: Towards Data Science — Replacing a 15-Minute Booking Process with a LangGraph AI Agent

Este conteúdo foi elaborado com o auxílio de ferramentas de IA, com base em fontes confiáveis, e revisado pela nossa equipe editorial antes da publicação. Não substitui o aconselhamento de um profissional especializado.