Pular para conteúdo

LangGraph API

Execução de grafos LangGraph via HTTP — orquestração de fluxos com estado, ramificação condicional e retentativa. Roda no IBM Code Engine, escalando a zero quando ninguém usa.

Endpoints

Recurso URL
API https://langgraph-api.2d3xg120feyh.br-sao.codeengine.appdomain.cloud
Documentação interativa (Swagger UI) /docs

Autenticação

Todas as chamadas exigem o header X-Api-Key (exceto /health e /docs). Sem a chave a API responde 401.

X-Api-Key: <chave>

Onde está a chave?

A chave é armazenada em segurança pela equipe de desenvolvimento. Solicite acesso ao responsável pelo serviço.

Rotas

Rota O que faz
GET /health Liveness (sem auth) — lista os grafos carregados
GET /graphs Grafos disponíveis, com exemplo de entrada de cada um
POST /graphs/{nome}/invoke Executa o grafo até o fim e devolve o estado final
POST /graphs/{nome}/stream Executa emitindo um evento SSE por nó concluído
GET /graphs/{nome}/state?thread_id= Estado salvo de uma thread

Grafos disponíveis

doc_triage — extração de documento com correção automática de OCR

Converte um documento para Markdown usando o Docling OCR, detecta camada de texto quebrada e, se encontrar, reprocessa automaticamente com force_ocr=true.

flowchart LR
    A([início]) --> B["extract<br>converte via docling-serve"]
    B --> C{"inspect<br>texto embaralhado?"}
    C -->|"sim, e ainda não tentou OCR"| D["retry_force_ocr<br>liga force_ocr"]
    D --> B
    C -->|"não, ou já tentou"| E["finalize<br>gera recomendação"]
    E --> F([fim])

Resolve na origem o problema descrito em Qualidade da extração: quem chama não precisa saber de antemão se o PDF tem camada de texto confiável — o grafo descobre e corrige.

Uso

curl -X POST \
  -H "X-Api-Key: <chave>" \
  -H "Content-Type: application/json" \
  -d '{"input":{"url":"https://exemplo.gov.br/deliberacao.pdf"}}' \
  "https://langgraph-api.2d3xg120feyh.br-sao.codeengine.appdomain.cloud/graphs/doc_triage/invoke"

Também aceita o documento embutido, em vez de URL:

{"input": {"base64_content": "<base64>", "filename": "deliberacao.pdf"}}

Resposta

{
  "graph": "doc_triage",
  "thread_id": "ephemeral-3148e9bd4d5a",
  "output": {
    "markdown": "# TÍTULO: POLÍTICA CORPORATIVA...",
    "attempts": 2,
    "force_ocr": true,
    "garbled_lines": [],
    "stats": {
      "docling_status": "success",
      "elapsed_s": 50.4,
      "warmup_s": 19.4,
      "queue_wait_s": 30.8,
      "chars": 34220,
      "headings": 22,
      "garbled_count": 0,
      "force_ocr_used": true,
      "recommendation": "camada de texto estava quebrada; markdown final veio de OCR forçado"
    },
    "trace": ["extract#1: ...", "inspect: ...", "retry: ...", "extract#2: ...", "finalize: ..."]
  }
}

O trace mostra o caminho percorrido no grafo e o stats diz onde o tempo foi gasto — útil porque o processing_time do docling cobre só a conversão, não o cold start nem a espera na fila.

Como a detecção funciona

Uma linha é considerada suspeita quando o comprimento médio dos tokens fica abaixo de 3,5 caracteres e ao menos 40% dos tokens têm 1–2 caracteres. É a assinatura do texto picado:

suspeita:  ## TIT PA D   D D DPIO DPO AIS
correta:   ## TÍTULO: POLÍTICA CORPORATIVA DE PRIVACIDADE E PROTEÇÃO DE DADOS PESSOAIS

Os dois sinais são exigidos juntos porque a proporção de tokens curtos, isolada, acusaria frases normais em português — cheias de "a", "de", "os".

echo

Grafo mínimo de dois nós, sem dependências externas. Serve para verificar se o serviço e a autenticação estão de pé.

curl -X POST -H "X-Api-Key: <chave>" -H "Content-Type: application/json" \
  -d '{"input":{"message":"olá","upper":true}}' \
  "https://langgraph-api.2d3xg120feyh.br-sao.codeengine.appdomain.cloud/graphs/echo/invoke"

Streaming (SSE)

/stream emite um evento por nó concluído — útil para acompanhar grafos longos:

curl -N -X POST -H "X-Api-Key: <chave>" -H "Content-Type: application/json" \
  -d '{"input":{"url":"https://exemplo.gov.br/doc.pdf"}}' \
  "https://langgraph-api.2d3xg120feyh.br-sao.codeengine.appdomain.cloud/graphs/doc_triage/stream"
data: {"extract": {"markdown": "...", "trace": ["extract#1: ..."]}}
data: {"inspect": {"garbled_lines": [], "trace": ["inspect: 0 linha(s) ..."]}}
data: {"finalize": {"stats": {...}}}
data: [DONE]

O thread_id usado vem no header X-Thread-Id da resposta.

Estado e threads

thread_id é opcional:

  • omitido — a API gera um efêmero e a execução não guarda nada
  • informado — o estado acumula entre chamadas e pode ser consultado em /graphs/{nome}/state

O estado é volátil

O checkpointer é em memória: o estado de uma thread existe apenas enquanto a instância estiver viva e desaparece quando o serviço escala a zero. Para fluxos que precisam retomar de onde pararam, o estado tem de ser guardado do lado do cliente.

Arquitetura

LangGraph aqui é usado como biblioteca, dentro de uma aplicação FastAPI própria — não é o LangGraph Platform. Na prática o serviço é um container HTTP comum, o que permite escalar a zero e dispensa banco de dados, licença e verificação externa.

flowchart LR
    A["Cliente<br>(app, script)"]

    subgraph CE["IBM Code Engine — br-sao"]
        B["langgraph-api<br>0,5 vCPU / 1 GB · escala 0 → 3<br>auth: X-Api-Key"]
        C["docling-serve<br>API de conversão"]
        D["docling-worker<br>conversão"]
    end

    A -->|"POST /graphs/doc_triage/invoke"| B
    B -->|"1. GET /health (warmup)"| C
    B -->|"2. POST convert async + polling"| C
    C <--> D
Componente Configuração
langgraph-api 0,5 vCPU / 1 GB RAM, escala 0 → 3, porta 8000, timeout de request 600 s

O trade-off assumido: o LangGraph Platform self-hosted traria /threads, /assistants, cron e o LangGraph Studio prontos, mas exige Postgres e Redis obrigatórios, chave de licença com verificação externa, e seus background runs não sobrevivem ao scale-to-zero — nada acorda o container para retomar a fila. Como biblioteca, o serviço fica mais simples e realmente serverless.

Comportamento

  • Cold start: poucos segundos (imagem leve, sem modelos)
  • doc_triage: ~50 s ponta a ponta para um PDF de 7–10 páginas, incluindo o cold start do docling; ~30 s se o docling já estiver quente
  • Timeout: o Code Engine encerra requests em 600 s. Documentos que passem disso devem ser quebrados em partes
  • Residência: processamento em br-sao (São Paulo) — o documento não sai do Brasil

Troubleshooting

Sintoma Causa provável Ação
401 Unauthorized Chave ausente ou inválida Conferir header X-Api-Key
503 com "LANGGRAPH_API_KEY não configurada" Serviço subiu sem a chave Conferir o secret vinculado ao app
404 grafo '...' não existe Nome errado GET /graphs lista os disponíveis
Execução demora minutos além do esperado Cold start do docling Ver warmup_s no stats — o serviço já aquece o docling antes de enfileirar
error com docling não concluiu em ...s Documento grande demais ou fila cheia Reenviar; documentos muito grandes devem ir direto no endpoint assíncrono do docling
Estado de uma thread desapareceu Instância escalou a zero Esperado — o checkpointer é em memória