Estado de execução durável e recuperável no LangChain4j: o que muda para sistemas multi-agentes em Java

Um sistema multi-agente que roda por minutos ou horas tem um ponto fraco estrutural: se o processo cai no meio da execução, todo o estado intermediário some. O agente que já gerou um rascunho, o que já consultou a base de dados, o passo que estava parado esperando uma aprovação humana: na volta, nada disso existe mais, e o fluxo recomeça do zero. Para um exemplo curto isso é irrelevante. Para um pipeline de agentes que faz a triagem de um chamado, redige uma resposta e espera um humano confirmar antes de enviar, recomeçar do zero é caro e, em alguns casos, simplesmente errado (você reprocessa efeitos colaterais que já aconteceram).

O LangChain4j 1.13.0 (junto com o beta 1.13.0-beta23) ataca exatamente esse ponto. A mudança de destaque do release é tornar o estado de execução de um sistema agêntico persistível e recuperável, com duas classes novas no centro: RecoverabilityIT e PendingResponse. Vale entender o que isso significa na mecânica de execução, qual é a regra de decisão de onde você guarda esse estado, e por que o novo HibernateContentRetriever é a peça que liga um agente durável aos dados que sua empresa já tem.

O que é o “estado de execução” de um sistema agêntico

No módulo langchain4j-agentic, você compõe agentes em workflows: uma sequência, um loop, uma composição condicional. Esses agentes não conversam por variáveis soltas; eles compartilham um AgenticScope. Cada agente lê seus argumentos de entrada do scope e escreve sua saída de volta (pela outputKey que você declara no builder). Além disso, o loop de execução tem uma posição: qual passo do workflow está rodando agora.

Então o “estado de execução” tem dois componentes. É o conteúdo do AgenticScope (tudo que os agentes produziram até aqui) e a posição interna do planner (onde no workflow você está). Perder qualquer um dos dois quebra a retomada: sem o scope, você perde o trabalho; sem a posição, você não sabe de onde continuar.

O que a recuperabilidade adiciona, mecanicamente

Quando você configura um store de persistência, o comportamento muda de forma concreta: depois de cada invocação de agente, o AgenticScope atual é gravado (checkpoint) no store configurado, e o loop também salva a posição interna do planner. Numa recuperação após crash ou restart, o scope persistido é recarregado e o workflow retoma a partir do passo correto, em vez de reiniciar do começo.

É aí que entra a PendingResponse. Sistemas com etapa humana (human-in-the-loop) podem parar esperando uma resposta externa. Essa resposta pendente faz parte do que precisa sobreviver ao crash: na recuperação, você carrega o scope, fornece a resposta que faltava para completar a ação pendente, e re-invoca o workflow, que segue do ponto certo. RecoverabilityIT é o teste de integração que exercita esse caminho de crash, restart e retomada; tê-lo versionado no release é o sinal de que o comportamento é intencional e especificado.

O 1.13 ainda traz agentes opcionais, que parecem cosméticos mas reforçam a mesma história de robustez. Marcando um agente com .optional(true) no builder, o workflow não falha inteiro quando os argumentos de entrada daquele agente não estão disponíveis no AgenticScope: ele pula o agente e segue. Em um fluxo longo, isso é a diferença entre um passo ausente derrubar tudo e o pipeline tolerar estado parcial e continuar. Recuperabilidade e opcionalidade resolvem lados diferentes do mesmo problema: manter um fluxo de muitos passos de pé quando algo sai do esperado.

A regra de decisão: onde você persiste esse estado

A recuperabilidade não vem ligada por padrão a um banco específico, e isso é intencional. A persistência mora atrás de uma interface, AgenticScopeStore. Você implementa essa interface e a registra com AgenticScopePersister.setStore(seuStore). O scope em memória, default, é rápido e descartável; um store durável custa uma escrita por passo de agente, mas sobrevive a quedas. A pergunta de design é onde colocar esse store.

A regra de bolso que funciona em produção: persista o scope onde sua durabilidade operacional já vive. Se o seu system of record é um banco relacional com uma história de backup, restore e transação que sua operação já conhece, é ali que o checkpoint deveria estar. A razão é consistência. Quando o estado do agente está no mesmo banco dos dados de negócio, a recuperação enxerga um mundo coerente: o que o agente já fez e os dados sobre os quais ele agiu fazem parte da mesma fronteira transacional e do mesmo plano de recuperação. Um store paralelo (um cache volátil, um serviço à parte) significa mais uma coisa para operar, monitorar e, na pior hora, reconciliar manualmente depois de um incidente. A interface AgenticScopeStore existe justamente para que essa escolha seja sua, e a escolha sóbria costuma ser “junto do dado que já importa”.

HibernateContentRetriever: o agente durável lendo o dado corporativo

A outra novidade que conversa diretamente com isso é o HibernateContentRetriever, no pacote dev.langchain4j.rag.content.retriever.hibernate. Ele é um ContentRetriever (a abstração de RAG que entrega trechos de contexto para o modelo), mas com um mecanismo diferente do usual. Em vez de comparar embeddings num vector store, ele recebe uma SessionFactory e um ChatModel e, para uma pergunta em linguagem natural, gera e executa uma consulta HQL sobre o seu modelo de entidades existente, devolvendo uma lista ordenada de Content. O builder permite configurar maxRetries, o número de tentativas quando o HQL gerado não executa de primeira.

O ganho prático é direto: recuperação sobre o dado relacional que você já tem, sem montar e manter um vector store em paralelo, com toda a pipeline de embeddings, sincronização e reindexação que ele implica. Sua base de clientes, pedidos e contratos já está mapeada em entidades Hibernate; retrieve(new Query("...")) passa a ser uma forma de o agente perguntar a essa base em linguagem natural, com o ChatModel traduzindo a pergunta em HQL e o Hibernate executando contra o esquema real. Para um caso onde o dado corporativo já é relacional e bem modelado, isso é menos infraestrutura nova para sustentar a mesma capacidade.

Junte as duas peças e aparece um substrato único. O agente persiste o AgenticScope no seu banco relacional via um AgenticScopeStore, e lê o contexto de negócio através do Hibernate sobre esse mesmo banco. Um plano de dados, uma história de operação, uma fronteira transacional: tudo isso para um agente durável e os dados que ele consulta. Não é a única arquitetura possível, mas é a que pede menos componentes novos para um time que já roda Java com Hibernate em produção.

Ou seja

A engenharia difícil de sistemas agênticos em produção raramente está no prompt; está em durabilidade, recuperação e em decidir onde o estado vive de forma consistente com o resto da sua plataforma. O 1.13 dá as primitivas (AgenticScopeStore, recuperação por checkpoint, agentes opcionais, HibernateContentRetriever); a decisão de arquitetura continua sua.

É exatamente nesse recorte (colocar a durabilidade do agente e o acesso ao dado corporativo sobre a infraestrutura que você já opera, com disciplina antes de adicionar peças novas) que eu trabalho no Método Java AI Specialist: arquitetura primeiro, integração depois, sem abrir mão do que já existe em operação.

Os detalhes estão em eldermoraes.ai.