LangChain4j + Quarkus: como colocar IA em produção no seu backend Java

A maioria dos times Java não precisa migrar para Python para integrar IA nas aplicações. Se sua stack já usa Spring Boot, Quarkus ou Micronaut, existe um caminho claro para adicionar capacidades de IA sem reescrever serviços. O ecossistema amadureceu a ponto de você tratar integração com modelos como mais uma dependência de backend.

O desafio real não é escolher um modelo. O desafio real é fazer a IA se comportar como um serviço confiável: lidar com latência, gerenciar falhas, medir custo e integrar com seu stack atual de segurança e observabilidade. É aí que LangChain4j com Quarkus entrega valor.

Neste artigo, você vai ver como integrar LangChain4j em aplicações Quarkus, o que a versão 1.12.1 adiciona para times enterprise e padrões práticos para workloads de produção.

O problema de integração que a maioria dos times enfrenta

Quando times adicionam IA em aplicações Java, normalmente começam com um cliente HTTP simples chamando endpoint da OpenAI ou Anthropic. Em protótipo funciona. Em produção, começa a doer.

Os problemas aparecem em três frentes:

  • Primeiro, complexidade de orquestração. Uma chamada de modelo é fácil. Múltiplas chamadas, pipelines de recuperação, invocação de ferramentas e cadeias de fallback exigem padrões de código estruturados. Clientes HTTP ad hoc viram dívida técnica rápido.
  • Segundo, comportamento de streaming. Inferência de LLM é lenta comparada com chamadas REST típicas. Uma requisição síncrona simples bloqueia threads enquanto o modelo gera centenas de tokens. Sob carga, esse padrão esgota pool de conexões e degrada o serviço inteiro.
  • Terceiro, observabilidade e controle. Você precisa de métricas de uso de token, rastreamento de latência, atribuição de custo e capacidade de cancelar requisições em andamento. Cliente HTTP cru não entrega isso.

LangChain4j resolve a camada de orquestração. Quarkus entrega o runtime. Juntos, eles entregam um caminho de produção para IA sem abandonar o ecossistema Java.

LangChain4j 1.12.1: o que mudou para times de produção

A release 1.12.1 do LangChain4j, publicada em março de 2026, inclui recursos que importam para deploys de produção.

Integração Hibernate EmbeddingStore

A adição mais relevante para times que usam Hibernate ORM é o HibernateEmbeddingStore. Essa integração permite armazenar e recuperar embeddings vetoriais direto no seu banco atual, usando a mesma sessão e o mesmo gerenciamento de transação do Hibernate que você já usa com dados relacionais.

Segundo as notas de release, a integração funciona com Hibernate ORM 7.1+ e 7.2+. Isso importa porque você não precisa adotar um banco vetorial dedicado para começar com retrieval-augmented generation. Você pode persistir embeddings no seu PostgreSQL ou MySQL atual usando o módulo hibernate-vector.

A implicação prática é simples: times com mapeamentos Hibernate consolidados conseguem adicionar armazenamento de embeddings sem novas dependências de infraestrutura. Tabelas de embeddings seguem os mesmos padrões de tabelas de entidades. As consultas usam a mesma Session API.

Observabilidade com Micrometer

A release adiciona integração com Micrometer para coleta de métricas. O MicrometerChatModelListener fornece contadores e timers para rastreamento de latência no nível de modelo.

Para times rodando Quarkus, isso é valioso porque Quarkus tem suporte nativo a Micrometer. Você pode emitir métricas de IA para o mesmo pipeline de observabilidade que já monitora requisições HTTP, queries de banco e cache hits. Contagem de tokens, duração de requisições e taxa de erro ficam visíveis em dashboard sem instrumentação customizada.

A camada de métricas inclui contadores para requisições bem-sucedidas, timers para medição de latência e gauges para conexões de streaming ativas. Isso dá visibilidade para configurar alertas de latência anômala ou picos de erro.

Suporte a agentes e busca de ferramentas

A versão 1.12.1 também introduz skills de agentes e tool search. Esses recursos suportam padrões mais complexos de agentes, em que o modelo pode descobrir e selecionar ferramentas dinamicamente, em vez de trabalhar com ferramentas codificadas manualmente.

Tool search permite que um agente encontre ferramentas relevantes com base na consulta do usuário, reduzindo consumo de janela de contexto com descrições de ferramentas. Agent skills oferecem uma forma de definir grupos reutilizáveis de capacidades que podem ser anexados aos agentes.

Para a maioria dos times, esses recursos importam se você está construindo sistemas multiagente ou uso autônomo de ferramentas. Para casos mais simples de chat e retrieval, são opcionais.

Integração com Quarkus: setup prático

A extensão Quarkus para LangChain4j tem interface compatível com OpenAI. Isso significa que você consegue conectar OpenAI, Azure OpenAI, Ollama ou qualquer provedor que implemente a especificação de API da OpenAI. A Anthropic também expõe endpoint compatível com OpenAI, mas a própria documentação classifica esse endpoint como voltado a teste e avaliação, não produção. Para produção com modelos Anthropic, prefira a extensão dedicada quarkus-langchain4j-anthropic, que mapeia para a API nativa da Anthropic.

Um setup básico envolve três dependências:

<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-openai</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-micrometer</artifactId>
</dependency>

A extensão quarkus-langchain4j-openai faz parte do ecossistema Quarkiverse (io.quarkiverse.langchain4j), mantido separadamente do core do Quarkus. As extensões quarkus-rest e quarkus-micrometer pertencem ao core do Quarkus (io.quarkus). Essa distinção importa: misturar groupIds de forma incorreta no POM gera falha de build.

A extensão autoconfigura um bean ChatModel ou StreamingChatModel com base nas propriedades da aplicação. Configuração no application.properties:

quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY}
quarkus.langchain4j.openai.base-url=https://api.openai.com/v1
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o
quarkus.langchain4j.openai.log-requests=true
quarkus.langchain4j.openai.log-responses=true

Para modelos locais, basta trocar a base URL. Se você usa Ollama para rodar modelos localmente, o endpoint vira:

quarkus.langchain4j.openai.base-url=http://localhost:11434/v1
quarkus.langchain4j.openai.api-key=ollama

O Ollama não valida API keys. O valor ollama é a convenção canônica usada na documentação do Ollama e atende à inicialização do cliente sem exigir chave real.

Streaming: por que importa e como implementar

Chamadas síncronas de chat bloqueiam a thread da requisição até o modelo terminar de gerar. Para modelos pequenos em hardware rápido, isso pode ser aceitável. Para modelos maiores, especialmente rodando localmente ou chamando APIs em nuvem com cold start, uma única requisição pode levar dez segundos ou mais.

Streaming muda o modelo de interação. Em vez de esperar a resposta completa, a aplicação recebe tokens conforme são gerados. O usuário vê saída imediata. A latência percebida cai muito, mesmo quando o tempo total de geração é o mesmo.

LangChain4j fornece StreamingChatModel e StreamingChatResponseHandler para esse padrão. A interface de handler recebe callbacks para cada token, chunk de raciocínio, tool call e evento de conclusão.

No Quarkus, a primitiva reativa idiomática é Multi<String> do Mutiny, não Flux do Project Reactor. O exemplo abaixo conecta o handler baseado em callback do LangChain4j com a API de emitter do Mutiny, que integra direto com SSE no Quarkus REST:

import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.StreamingChatResponseHandler;
import io.smallrye.mutiny.Multi;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;

@Path("/chat")
public class ChatResource {

    @Inject
    StreamingChatModel streamingChatModel;

    @GET
    @Path("/stream")
    @Produces(MediaType.SERVER_SENT_EVENTS)
    public Multi<String> streamChat(@QueryParam("message") String userMessage) {
        return Multi.createFrom().emitter(emitter ->
            streamingChatModel.chat(userMessage, new StreamingChatResponseHandler() {
                @Override
                public void onPartialResponse(String partialResponse) {
                    emitter.emit(partialResponse);
                }

                @Override
                public void onCompleteResponse(ChatResponse completeResponse) {
                    emitter.complete();
                }

                @Override
                public void onError(Throwable error) {
                    emitter.fail(error);
                }
            })
        );
    }
}

Esse padrão usa a ponte de emitter do Mutiny. A factory Multi.createFrom().emitter() assina de forma lazy: a chamada de streaming para o modelo só começa quando existe subscriber downstream conectado. O endpoint declara @Produces(MediaType.SERVER_SENT_EVENTS), que é a constante JAX-RS para SSE no Quarkus REST. O Quarkus gerencia backpressure e ciclo de vida da conexão automaticamente para endpoints que retornam Multi.

Rodando modelos locais com Ollama

Para desenvolvimento e testes, rodar modelos localmente evita custo de API e latência de rede. O Ollama simplifica isso ao oferecer um servidor local de modelos com API compatível com OpenAI por padrão.

Depois de instalar o Ollama, faça pull e rode um modelo:

ollama run llama3.2

Esse comando baixa o modelo e inicia o servidor em http://localhost:11434. O endpoint compatível com OpenAI fica disponível em http://localhost:11434/v1. Sua aplicação Quarkus aponta para esse endpoint sem mudança de código.

Para trocar de modelo:

ollama pull gemma3
ollama run gemma3

Ollama suporta uma biblioteca ampla de modelos. Você pode listar modelos locais com ollama list e baixar qualquer modelo do registry com ollama pull <nome-do-modelo>.

No macOS, o Ollama usa aceleração de GPU com Metal automaticamente ao rodar nativo. Não é necessária configuração adicional. No Linux com GPU NVIDIA, o Ollama detecta CUDA automaticamente. Em ambientes CPU-only, a inferência fica significativamente mais lenta, então escolha de modelo pesa mais nesse contexto. Prefira modelos menores quantizados, como llama3.2:3b ou qwen2.5:3b, para uma experiência de desenvolvimento utilizável.

Extensão nativa do Ollama

A abordagem acima usa quarkus-langchain4j-openai com base URL customizada, que funciona porque o Ollama expõe endpoint compatível com OpenAI. Para integração mais profunda, o ecossistema Quarkiverse também fornece uma extensão dedicada:

<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-ollama</artifactId>
</dependency>

Essa extensão usa a API nativa do Ollama em vez do subconjunto compatível com OpenAI, expõe configuração específica em quarkus.langchain4j.ollama.* e integra com Quarkus Dev Services. Na prática, o Quarkus pode subir automaticamente um container Ollama durante quarkus dev, sem setup manual. Se o Ollama é seu alvo principal de inferência local, e não só um drop-in replacement para provider compatível com OpenAI, a extensão dedicada é a melhor escolha.

Padrões de produção: resiliência e controle de custo

Streaming não resolve todos os problemas de produção. Você ainda precisa de limites de timeout, tratamento de falhas e visibilidade de custo.

Timeouts e cancelamento

LangChain4j suporta cancelamento no meio do stream via PartialResponseContext. Quando você precisa interromper a geração (por exemplo, se o usuário clica em stop ou se a aplicação detecta violação de política):

@Override
public void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context) {
    if (shouldCancel()) {
        context.streamingHandle().cancel();
    }
}

Chamar cancel() fecha a conexão com o provedor de modelo e interrompe o stream de forma limpa.

Limites de tamanho de prompt

Modelos têm janelas de contexto fixas. Prompts longos consomem tokens e aumentam latência. Serviços de produção devem validar tamanho de prompt antes de enviar ao modelo.

Uma abordagem prática é definir limite de caracteres ou tokens na camada de API e rejeitar requisições que excedam esse valor. Isso evita que um prompt grande degrade o serviço para outros usuários.

Rastreamento de uso de tokens

Contagem de tokens só fica disponível em onCompleteResponse. O método ChatResponse.getMetadata().getTokenUsage() retorna tokens de entrada, saída e total da requisição.

Para atribuição de custo, registre esses valores com timestamp e contexto do usuário. Agregar isso no sistema de observabilidade dá visibilidade de custo por usuário ou por endpoint.

Fallback de modelos

Se sua aplicação usa múltiplos provedores, considere uma cadeia de fallback. Se OpenAI retornar erro, roteie para Anthropic. Se ambos falharem, retorne resposta em cache ou mensagem de degradação graciosa.

Esse padrão exige templates de prompt agnósticos a provedor e abstração no nível de cliente de modelo. As interfaces de modelo do LangChain4j suportam isso ao abstrair APIs específicas de provedores em padrões comuns.

Quarkus 3.31 e 3.32: melhorias relevantes para IA

Releases recentes do Quarkus incluem recursos que beneficiam workloads com IA. Os itens abaixo estão organizados pela versão que introduziu cada recurso.

Quarkus 3.31:

  • OIDC PAR (Pushed Authorization Requests): suporte a OAuth 2.0 PAR melhora segurança na fase de requisição de autorização. Relevante para serviços de IA expostos a clientes externos sob requisitos de compliance.
  • Suporte a diretório de trust-store TLS: agora você pode apontar quarkus.tls.trust-store.pem.certs para um diretório, não só para um arquivo. Em Kubernetes, rotação de certificados fica mais simples porque você não precisa reconstruir keystores manualmente.

Quarkus 3.32:

  • Serializadores Jackson sem reflexão: habilitado por padrão, reduz overhead de serialização e melhora compatibilidade nativa. Para endpoints de IA de alto throughput que retornam JSON, isso importa.
  • Empacotamento AOT-jar: novo tipo de empacotamento que desloca mais inicialização para build time, melhorando cold start sem precisar ir para native completo. Útil para workloads containerizados em que compilação nativa não é prática.
  • Providers de nonce para OIDC DPoP: providers customizados de nonce DPoP (Demonstrating Proof of Possession) fecham lacunas de compliance em aplicações que exigem semântica forte de proof-of-possession.

Essas melhorias são incrementais, mas relevantes para times que operam serviços de IA em escala.

Conclusão

No fim, é sobre integrar IA com previsibilidade dentro do seu backend Java: observabilidade, resiliência, custo sob controle e arquitetura sustentável no longo prazo. É aqui que LangChain4j com Quarkus deixa de ser buzzword e vira decisão de engenharia.

Demo bonita impressiona. Produção estável paga a conta. Estratégia real de IA não é “dar POST em endpoint de modelo”. É integrar IA na stack que sua empresa já roda, com segurança, métrica e evolução contínua, sem reescrever tudo do zero. Esse é o corte entre experimento e solução.

Se você é dev Java, pare de se vender como “atrasado para IA”. Você já traz vantagem competitiva: backend, arquitetura, performance, cloud e operação em produção. O próximo passo é direto: construir, testar, medir e publicar aprendizado. Porque não basta saber, o mercado precisa ver que você sabe. Quem aplica e comunica com clareza vira referência e fica difícil de ignorar.

Quer aprofundar essa jornada com foco prático em Java + IA, arquitetura e execução de produção? Acesse: https://eldermoraes.ai

Fontes

  • LangChain4j 1.12.1 Release Notes: https://github.com/langchain4j/langchain4j/releases/tag/1.12.1
  • LangChain4j HibernateEmbeddingStore PR: https://github.com/langchain4j/langchain4j/pull/4622
  • LangChain4j Micrometer Integration PR: https://github.com/langchain4j/langchain4j/pull/4556
  • Ollama Documentation: https://docs.ollama.com
  • Ollama OpenAI Compatibility: https://ollama.com/blog/openai-compatibility
  • Quarkus LangChain4j Extension (Quarkiverse): https://github.com/quarkiverse/quarkus-langchain4j
  • Quarkus LangChain4j Extension Documentation: https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html
  • Quarkus LangChain4j Ollama Extension: https://quarkus.io/extensions/io.quarkiverse.langchain4j/quarkus-langchain4j-ollama/
  • LangChain4j Streaming Response Guide: https://bootcamptoprod.com/langchain4j-streaming-response/
  • LangChain4j Response Streaming Docs: https://docs.langchain4j.dev/tutorials/response-streaming/
  • Quarkus 3.31/3.32 Hidden Features: https://www.the-main-thread.com/p/quarkus-3-31-3-32-hidden-features-java
  • Quarkus 3.31 Release Blog: https://quarkus.io/blog/quarkus-3-31-released/
  • Quarkus 3.32 Release Blog: https://quarkus.io/blog/quarkus-3-32-released/
  • Quarkus Reflection-Free Jackson PR: https://github.com/quarkusio/quarkus/pull/51802
  • Quarkus AOT-jar Packaging PR: https://github.com/quarkusio/quarkus/pull/52224
  • Anthropic OpenAI SDK Compatibility: https://docs.anthropic.com/en/api/openai-sdk