· Hernán Pérez Rodal · Engineering  · 6 min read

Agentic Compliance System com LangGraph: patterns que funcionam em produção

Nem todos os multi-agent patterns servem em domínios regulados. Contamos qual arquitetura de agentes usamos na Darwin, por quê, e quais anti-patterns evitamos.

Nem todos os multi-agent patterns servem em domínios regulados. Contamos qual arquitetura de agentes usamos na Darwin, por quê, e quais anti-patterns evitamos.

TL;DR — Os tutoriais de agents falam de “autonomous AI” como se fosse um superpoder mágico. Em produção regulada, autonomia sem guardrails é um pesadelo jurídico. Na Darwin rodamos um sistema multi-agent com LangGraph, e o valor não está na autonomia — está na orquestração determinística de componentes LLM-powered.

O problema

A Darwin processa milhares de casos de compliance por dia sobre dados de rastreabilidade alimentar. Cada caso requer:

  1. Interpretar uma pergunta regulatória (texto livre do usuário ou sistema)
  2. Decompô-la em sub-consultas (regulatória, operativa, de evidência)
  3. Delegar cada sub-consulta ao agente adequado
  4. Sintetizar os resultados em uma resposta com gap analysis + risk scoring
  5. Validar antes de apresentar — especialmente dados numéricos

Tentar que um único LLM faça tudo end-to-end com um prompt gigante falha em produção. Os modelos se confundem, alucinam dados, perdem contexto intermediário. Quando o output vai para uma auditoria FDA, “errou um pouquinho” não é aceitável.

O pattern que usamos: supervisor + workers especializados

A arquitetura é:

           [Supervisor]

   ┌──────────┼──────────┬──────────┐
   │          │          │          │
 [Research] [Validation] [Analytics] [Reporting]
  • Supervisor — decide quais agentes invocar, em que ordem, e quando parar
  • Research Agent — busca contexto regulatório + eventos de rastreabilidade (usa o RAG híbrido que descrevi aqui)
  • Validation Agent — cruza regras de negócio determinísticas com achados do Research
  • Analytics Agent — executa queries estruturadas + anomaly detection sobre eventos
  • Reporting Agent — sintetiza tudo em um output consumível (relatório PDF, API JSON, UI view)

O supervisor é a peça crítica. É ele que decide, não os workers.

Por que LangGraph e não outra coisa

Avaliamos várias opções antes de escolher LangGraph:

FrameworkPor que não / por que sim
LangChain Agents (AgentExecutor)Muito mágico. Difícil de debugar em produção.
CrewAIBom para protótipos. Abstração alta demais para patterns específicos.
AutoGenForte em conversas multi-agent. Menos ideal para fluxos determinísticos.
Custom state machineConsiderado. Para nosso caso, LangGraph entrega 80% com 20% do código.
LangGraphState machine explícita + checkpoints + streaming. Debugável, production-grade.

O chave: LangGraph não é “um framework de agents”. É uma state machine com superpoderes para LLM calls. Essa diferença mental muda como você o usa.

Patterns que funcionam

1. Supervisor determinístico, não autônomo

Nosso supervisor NÃO pergunta ao LLM “o que eu faço agora?” a cada step. Isso é autonomia mágica e é um anti-pattern.

O que SIM faz:

def supervisor_node(state: ComplianceState) -> str:
    """Decide next node based on state — deterministic rules first, LLM second."""
    if not state.query_classified:
        return "classify_query"
    if state.classification == "regulatory" and not state.regulatory_context:
        return "research"
    if state.has_numerical_claims and not state.validated:
        return "validate"
    if state.ready_for_report:
        return "report"
    return "END"

O LLM entra só nos nodes individuais (classify, research, validate). O routing é código. Evita que um LLM “decida” tirar do circuito um step de validação crítico.

2. Checkpointing agressivo

Cada step emite um checkpoint. Se algo falha, não reexecutamos tudo — retomamos a partir do último estado bom. Crítico quando um caso toca 5-10 nodes e cada LLM call custa tempo+$.

from langgraph.checkpoint.postgres import PostgresSaver

graph = builder.compile(
    checkpointer=PostgresSaver(conn_string=DATABASE_URL),
    interrupt_before=["report"],  # Pausa antes do relatório final
)

3. Human-in-the-loop configurável

Alguns casos são auto-approve (rotinas), outros require review (high-stakes). Usamos interrupt_before no LangGraph para pausar antes dos nodes que decidimos exigirem intervenção humana.

O operador revisa, aprova ou edita, e o graph continua desde o checkpoint. Sem perder trabalho.

4. Tool use com validação estruturada

Quando um agent chama uma tool (query DB, buscar documento, computar métrica), todo output é validado contra um schema Pydantic antes de voltar ao LLM. Se a tool retorna algo inesperado, o erro é tratado explicitamente — não passamos para o LLM que inventará.

class TraceabilityQueryOutput(BaseModel):
    events: list[CriticalTrackingEvent]
    total_count: int
    date_range: DateRange

# Tool response validated. If fails → retry or escalate, NO LLM hallucination.

5. Observabilidade end-to-end

Cada node emite tracing com OpenTelemetry:

  • Input state
  • LLM prompt + response
  • Token usage + cost
  • Latency
  • Tool calls (com inputs/outputs)

Sem isso, debugar um agent system é impossível. Com isso, você encontra por que um caso deu errado em minutos.

Anti-patterns que evitamos

❌ Autonomia total do supervisor. “O LLM decide quando parar” = runaway loops + custos inesperados. Nosso supervisor tem max iterations por node e timeout por caso.

❌ Conversas agent-to-agent longas. Os tutoriais mostram N agents “discutindo”. Em produção, cada troca é $$$ e latência. Usamos mensagem única por agent, não conversa.

❌ Shared memory global sem estrutura. Todos os agents escrevendo em um dict compartilhado → race conditions + alucinação sobre qual é o estado atual. Nós usamos state pydantic-typed com mutations explícitas.

❌ LLM-as-everything. Muitos tutoriais usam LLM para coisas que uma regra determinística resolve. Regra geral: se você pode escrever como código, escreva como código. O LLM só para o que realmente requer raciocínio em linguagem natural.

O que não funcionou

V0: um único agent com tool use — testamos com um único Claude agent com acesso a todas as tools. Falhou porque o agent às vezes esquecia validações críticas quando o prompt ficava longo. Refatoramos para supervisor + workers.

Streaming contínuo na UI — quisemos streamar as decisões do supervisor para a UI em tempo real. Ficou confuso para o usuário (via “research → validate → research → validate” sem saber por quê). Mudamos para mostrar só os milestones principais e esconder a orquestração interna.

O que sim funcionou

Supervisor determinístico — zero runaway loops desde que implementamos.

Checkpointing em PostgreSQL — podemos retomar cases que ficaram pela metade por queda de infra ou cotas de API.

Validation agent com regras + LLM — combinação de regras hardcoded + LLM-as-judge para os edge cases dá melhor precisão que qualquer um separadamente.

Metrics por agent — sabemos exatamente qual agent é o gargalo em latência ou custo. Nos permitiu otimizar seletivamente (ex: trocar o Reporting Agent por um modelo menor sem tocar em Research).

Lessons learned

  1. Agentic ≠ autonomous — os melhores patterns são determinísticos com LLM calls seletivos
  2. LangGraph é uma state machine, não um framework de agents — pense assim e tudo simplifica
  3. Checkpointing agressivo desde o dia 0 — o custo de adicioná-lo depois é alto
  4. Valide tool outputs com schemas — nunca passe ao LLM algo sem validar
  5. Tracing + cost metrics por node — sem isso não há iteração real

E agora?

Estamos explorando sub-graphs reutilizáveis — nosso Validation Agent é genérico o suficiente para usar em outros fluxos (não só compliance). LangGraph suporta composabilidade de graphs, e isso abre a porta para um catálogo interno de patterns que podemos remixar.

Se você está fazendo build de AI agents em produção e te surpreende que seu sistema é mais lento/mais caro/mais instável que sua demo — provavelmente é falta de orquestração determinística. Comece por aí.


Construindo algo parecido? Vamos conversar — nos interessa compartilhar arquitetura e patterns.

Compartir:
Back to Blog

Related Posts

View All Posts »