Este projeto implementa um sistema RAG (Retrieval-Augmented Generation) utilizando Quarkus, LangChain4j e Ollama para criar um chatbot inteligente que pode responder perguntas baseadas em documentos ingeridos.
O diretório src/main/resources/rag contém documentos de exemplo para
ingestão e teste do sistema. Você pode adicionar seus próprios documentos neste
diretório para expandir o conhecimento do chatbot.
- Java 21 - Linguagem de programação
- Quarkus 3.26.2 - Framework para aplicações Java na nuvem
- LangChain4j - Framework para integração com IA
- Ollama - Plataforma para executar modelos de IA localmente
- Chroma - Banco de dados vetorial para embeddings
- Redis - Cache e gerenciamento de memória
- Maven - Gerenciamento de dependências
- Java 21 ou superior
- Maven 3.8+
- Ollama instalado e configurado
- Docker e Docker Compose
# macOS
brew install ollama
# Linux
curl -fsSL https://ollama.com/install.sh | sh
# Windows
# Baixe o instalador em https://ollama.com/download/windows# Modelo para chat
ollama pull gemma3:1b
# Modelo para embeddings
ollama pull all-minilm:33m# Inicie o Ollama
ollama serve
# Clone o repositório
git clone https://github.com/rodrigoprestesmachado/rag.git
cd ragExecute a aplicação em modo de desenvolvimento com live reload:
./mvnw quarkus:devNota: o Chroma e o Redis serão iniciados automaticamente via Dev Services do Quarkus. O Dev Services é uma funcionalidade do Quarkus que facilita o desenvolvimento local, iniciando automaticamente serviços como bancos de dados, filas de mensagens, caches, entre outros, sem a necessidade de configuração. Porém, é necessário ter o Docker instalado para que o Quarkus possa criar e gerenciar esses containers.
Se você quiser testar a interface do chat, basta pressionar a tecla w no
terminal quando a aplicação estiver em execução que o Quarkus irá abrir a
interface web no endereço e porta: http://localhost:8080/.
Empacote e execute a aplicação:
# Compilar
./mvnw package
# Executar JAR
java -jar target/quarkus-app/quarkus-run.jar
# Ou criar e executar uber-jar
./mvnw package -Dquarkus.package.jar.type=uber-jar
java -jar target/*-runner.jar# Build da aplicação
./mvnw package
# Build da imagem Docker
docker build -f src/main/docker/Dockerfile.jvm -t rag:jvm .
# Executar container
docker run -i --rm -p 8080:8080 \
-e QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL=http://host.docker.internal:11434/ \
rag:jvm# Build nativo
./mvnw package -Dnative -Dquarkus.native.container-build=true
# Build da imagem Docker
docker build -f src/main/docker/Dockerfile.native -t rag:native .
# Executar container
docker run -i --rm -p 8080:8080 \
-e QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL=http://host.docker.internal:11434/ \
rag:nativeCrie um arquivo docker-compose.yml:
version: '3.8'
services:
rag:
build:
context: .
dockerfile: src/main/docker/Dockerfile.jvm
ports:
- "8080:8080"
environment:
- QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL=http://host.docker.internal:11434/
depends_on:
- redis
- chroma
redis:
image: redis:7-alpine
ports:
- "6379:6379"
chroma:
image: chromadb/chroma:latest
ports:
- "8000:8000"Execute com:
docker-compose up -dO projeto segue os princípios da Arquitetura Hexagonal (Clean Architecture), organizando o código em camadas bem definidas:
src/main/java/dev/rpmhub/
├── domain/ # Núcleo da aplicação
│ ├── model/ # Entidades de domínio
│ ├── port/ # Interfaces/Contratos
│ └── usecase/ # Casos de uso/Regras de negócio
├── application/ # Camada de aplicação
│ ├── adapter/ # Adaptadores de aplicação
│ └── rest/ # Controladores REST
└── infrastructure/ # Camada de infraestrutura
├── adapter/ # Adaptadores externos
├── config/ # Configurações
├── repository/ # Implementações de repositório
└── service/ # Serviços de infraestrutura
A Arquitetura Hexagonal (também conhecida como Ports and Adapters) é um padrão arquitetural que promove a separação de responsabilidades e o baixo acoplamento entre as camadas da aplicação. Este projeto implementa os seguintes conceitos:
O núcleo da aplicação, contendo a lógica de negócio pura e independente de frameworks externos:
- Entidades de Domínio: Classes como
ChatMessage,RagQuery,RagResponse,ConversationMemory - Casos de Uso: Orquestram a lógica de negócio (
ChatbotUseCase,AskQuestionUseCase) - Portas (Interfaces): Contratos que definem como o domínio se comunica com o mundo externo
domain/
├── model/
│ ├── AIRequest.java # Requisição para IA
│ ├── ChatMessage.java # Mensagem de chat
│ ├── ConversationMemory.java # Memória da conversa
│ ├── RagQuery.java # Query RAG
│ └── RagResponse.java # Resposta RAG
├── port/
│ ├── AIService.java # Interface para serviços de IA
│ ├── EmbeddingRepository.java # Interface para repositório de embeddings
│ └── MemoryService.java # Interface para serviços de memória
└── usecase/
├── AskQuestionUseCase.java # Caso de uso: perguntas
├── ChatbotUseCase.java # Caso de uso: chatbot
└── IngestDocumentsUseCase.java # Caso de uso: ingestão
Coordena a interação entre o domínio e o mundo externo:
- Controladores REST: Criam os endpoints da API (
RagController)
Implementa os detalhes técnicos e integrações externas:
- Adaptadores: Implementações concretas das portas
(
AIServiceAdapter,LangChainAIService) - Repositórios: Implementa a persistência de dados. Nesta aplicação, os
repositórios são responsáveis por armazenar e recuperar informações de
embeddings e também o histórico de uma conversa (
EmbeddingRepositoryImpl,MemoryServiceImpl) - Serviços: Implementação de serviços externo da aplicação. O sistema
necessita do Apache PDFBox (
PDFExtractorService) para fazer a extração correta de PDFs.
- Testabilidade: O domínio pode ser testado isoladamente através de mocks das portas
- Flexibilidade: Fácil troca de provedores de IA (Ollama → OpenAI → Azure)
- Manutenibilidade: Alterações em frameworks não afetam a lógica de negócio
- Independência: O core da aplicação não depende de bibliotecas externas
HTTP Request → REST Controller → Use Case → Domain Logic → Port Interface → Adapter → External Service
↓
HTTP Response ← REST Controller ← Use Case ← Domain Logic ← Port Interface ← Adapter ← External Service
A arquitetura hexagonal é especialmente valiosa em sistemas RAG devido à natureza evolutiva e experimental da IA:
- Experimentação com Modelos: Facilita testes com diferentes LLMs (Ollama, OpenAI, Claude) sem alterar a lógica de negócio
- Múltiplas Estratégias de Embedding: Permite comparar diferentes algoritmos de vetorização (sentence-transformers, OpenAI embeddings, etc.)
- Bancos Vetoriais Intercambiáveis: Suporte fácil para Chroma, Pinecone, Weaviate ou Qdrant
- Estratégias de Chunking: Implementação de diferentes abordagens para divisão de documentos
- Memória Adaptável: Alternância entre Redis, banco relacional ou memória em processo
- Processamento de Documentos: Extensibilidade para PDF, Word, HTML, etc.
Sistema RAG Hexagonal:
┌─────────────────────────┐
│ REST Controllers │ ← Camada de Interface
├─────────────────────────┤
│ Use Cases │ ← Orquestração RAG
│ • ChatbotUseCase │
│ • AskQuestionUseCase │
│ • IngestUseCase │
├─────────────────────────┤
│ Ports │ ← Contratos
│ • AIService │
│ • EmbeddingRepository │
│ • MemoryService │
├─────────────────────────┤
│ Adapters │ ← Implementações
│ • OllamaAdapter │
│ • ChromaAdapter │
│ • RedisAdapter │
└─────────────────────────┘
- ChatbotUseCase: Implementa conversas com memória de contexto
- AskQuestionUseCase: Responde perguntas baseadas em documentos
- IngestDocumentsUseCase: Processa e indexa documentos
// Domínio define o contrato (Port)
public interface AIService {
Multi<String> generateResponse(String prompt, List<ChatMessage> context);
}
// Infraestrutura implementa diferentes adaptadores
@ApplicationScoped
public class OllamaAdapter implements AIService { ... }
@ApplicationScoped
public class OpenAIAdapter implements AIService { ... }
// Caso de uso permanece inalterado
@ApplicationScoped
public class ChatbotUseCase {
@Inject AIService aiService; // Injeção por interface
}// Teste unitário usando mock da porta
@Test
void shouldGenerateResponseWithMemory() {
// Given
AIService mockAI = Mockito.mock(AIService.class);
MemoryService mockMemory = Mockito.mock(MemoryService.class);
ChatbotUseCase useCase = new ChatbotUseCase(null, mockAI, mockMemory);
// When & Then - testa apenas lógica de negócio
// sem dependências externas
}// Nova funcionalidade: adicionar suporte a múltiplos embeddings
public interface EmbeddingRepository {
// Método existente
List<Document> findSimilar(String query, int limit);
// Novo método - não quebra implementações existentes
List<Document> findSimilarWithMetadata(String query, int limit, Map<String, Object> filters);
}# Conversa com contexto mantido por sessão
curl "http://localhost:8080/ai/chatbot?session=user123&prompt=Olá, como você pode me ajudar?"# Consulta baseada em documentos ingeridos
curl "http://localhost:8080/ai/ask?session=user123&prompt=O que é Vue.js?"# Obter histórico da conversa
curl "http://localhost:8080/ai/memory?session=user123"// JavaScript/Frontend
const response = await fetch(
'http://localhost:8080/ai/chatbot?session=user123&prompt=Explique IA generativa'
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
console.log(chunk); // Resposta em streaming
}# Porta da aplicação
quarkus.http.port=8080
# Configuração do Ollama
quarkus.langchain4j.ollama.base-url=http://localhost:11434/
quarkus.langchain4j.ollama.chat-model.model-id=gemma3:1b
quarkus.langchain4j.ollama.embedding-model.model-id=all-minilm:33m
quarkus.langchain4j.ollama.devservices.enabled=false
# Configuração RAG
rag.location=src/main/resources/rag
rag.context=Vue.js
quarkus.langchain4j.embedding-model.provider=ollama
# Chroma (Banco Vetorial)
quarkus.langchain4j.chroma.collection-name=chatbot
quarkus.langchain4j.chroma.timeout=30000
# Redis (Cache/Memória)
quarkus.redis.devservices.enabled=true
# Gerenciamento de Memória
memory.default.max-messages=100
memory.ttl.hours=48# Executar todos os testes
./mvnw test
# Testes de integração
./mvnw verify -Dskip.integration.tests=false
# Testes com perfil nativo
./mvnw verify -Dnative- Faça um fork do projeto
- Crie uma branch para sua feature (
git checkout -b feature/nova-feature) - Commit suas mudanças (
git commit -m 'Adiciona nova feature') - Push para a branch (
git push origin feature/nova-feature) - Abra um Pull Request
Este projeto contém informações confidenciais e proprietárias. Cópia, distribuição ou uso não autorizado deste arquivo ou seu conteúdo é estritamente proibido.
© 2025 Rodrigo Prestes Machado. Todos os direitos reservados.