Voltar para o Blog
Integrating AI and Machine Learning in Java Applications
14 min de leitura
JavaAIMachine LearningIntegration
IA não é apenas para Python! Descubra como integrar capacidades de inteligência artificial e machine learning em suas aplicações Java usando frameworks populares e APIs modernas.
Por Que IA em Java?
Java é amplamente usado em aplicações enterprise onde performance, escalabilidade e confiabilidade são críticas. Integrar IA em aplicações Java existentes permite aproveitar modelos de ML sem reescrever toda a stack.
OpenAI API com Spring Boot
Configuração Básica
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'com.fasterxml.jackson.core:jackson-databind'
}
// application.yml
openai:
api:
key: ${OPENAI_API_KEY}
url: https://api.openai.com/v1
// Configuration
@Configuration
public class OpenAIConfig {
@Value("${openai.api.key}")
private String apiKey;
@Value("${openai.api.url}")
private String apiUrl;
@Bean
public WebClient openAIWebClient() {
return WebClient.builder()
.baseUrl(apiUrl)
.defaultHeader("Authorization", "Bearer " + apiKey)
.defaultHeader("Content-Type", "application/json")
.build();
}
}Chat Completion Service
@Service
public class OpenAIService {
@Autowired
private WebClient openAIWebClient;
public Mono<String> generateChatCompletion(String prompt) {
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model("gpt-4")
.messages(List.of(
new Message("system", "You are a helpful assistant."),
new Message("user", prompt)
))
.temperature(0.7)
.maxTokens(500)
.build();
return openAIWebClient.post()
.uri("/chat/completions")
.bodyValue(request)
.retrieve()
.bodyToMono(ChatCompletionResponse.class)
.map(response -> response.getChoices().get(0).getMessage().getContent());
}
public Flux<String> generateStreamingCompletion(String prompt) {
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model("gpt-4")
.messages(List.of(new Message("user", prompt)))
.stream(true)
.build();
return openAIWebClient.post()
.uri("/chat/completions")
.bodyValue(request)
.retrieve()
.bodyToFlux(String.class)
.map(this::extractContent);
}
}
// DTOs
@Data
@Builder
public class ChatCompletionRequest {
private String model;
private List<Message> messages;
private Double temperature;
private Integer maxTokens;
private Boolean stream;
}
@Data
@AllArgsConstructor
public class Message {
private String role;
private String content;
}
@Data
public class ChatCompletionResponse {
private List<Choice> choices;
@Data
public static class Choice {
private Message message;
private String finishReason;
}
}REST Controller para Chat
@RestController
@RequestMapping("/api/ai")
public class AIController {
@Autowired
private OpenAIService openAIService;
@PostMapping("/chat")
public Mono<ResponseEntity<ChatResponse>> chat(
@RequestBody ChatRequest request) {
return openAIService.generateChatCompletion(request.getMessage())
.map(response -> ResponseEntity.ok(new ChatResponse(response)));
}
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String message) {
return openAIService.generateStreamingCompletion(message);
}
}
@Data
public class ChatRequest {
@NotBlank
private String message;
}
@Data
@AllArgsConstructor
public class ChatResponse {
private String reply;
}Deep Learning com DL4J (DeepLearning4J)
Configuração e Modelo Simples
// build.gradle
dependencies {
implementation 'org.deeplearning4j:deeplearning4j-core:1.0.0-M2.1'
implementation 'org.nd4j:nd4j-native-platform:1.0.0-M2.1'
}
@Service
public class MLModelService {
private MultiLayerNetwork model;
@PostConstruct
public void initModel() {
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(123)
.weightInit(WeightInit.XAVIER)
.updater(new Adam(0.001))
.list()
.layer(new DenseLayer.Builder()
.nIn(784) // 28x28 pixels
.nOut(250)
.activation(Activation.RELU)
.build())
.layer(new DenseLayer.Builder()
.nIn(250)
.nOut(100)
.activation(Activation.RELU)
.build())
.layer(new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nIn(100)
.nOut(10) // 10 classes (digits 0-9)
.activation(Activation.SOFTMAX)
.build())
.build();
model = new MultiLayerNetwork(conf);
model.init();
}
public void trainModel(INDArray features, INDArray labels, int epochs) {
DataSet dataSet = new DataSet(features, labels);
for (int i = 0; i < epochs; i++) {
model.fit(dataSet);
log.info("Epoch {} completed", i);
}
}
public int predict(INDArray input) {
INDArray output = model.output(input);
return Nd4j.argMax(output, 1).getInt(0);
}
public void saveModel(String path) throws IOException {
ModelSerializer.writeModel(model, new File(path), true);
}
public void loadModel(String path) throws IOException {
model = ModelSerializer.restoreMultiLayerNetwork(new File(path));
}
}Apache Spark MLlib para Big Data
Pipeline de Machine Learning
// build.gradle
dependencies {
implementation 'org.apache.spark:spark-core_2.12:3.5.0'
implementation 'org.apache.spark:spark-mllib_2.12:3.5.0'
}
@Service
public class SparkMLService {
private SparkSession spark;
@PostConstruct
public void initSpark() {
spark = SparkSession.builder()
.appName("ML Service")
.master("local[*]")
.config("spark.sql.warehouse.dir", "/tmp/spark-warehouse")
.getOrCreate();
}
public PipelineModel trainClassificationModel(String dataPath) {
// Load data
Dataset<Row> data = spark.read()
.format("csv")
.option("header", "true")
.option("inferSchema", "true")
.load(dataPath);
// Split data
Dataset<Row>[] splits = data.randomSplit(new double[]{0.8, 0.2}, 123);
Dataset<Row> trainingData = splits[0];
Dataset<Row> testData = splits[1];
// Create pipeline
VectorAssembler assembler = new VectorAssembler()
.setInputCols(new String[]{"feature1", "feature2", "feature3"})
.setOutputCol("features");
RandomForestClassifier rf = new RandomForestClassifier()
.setLabelCol("label")
.setFeaturesCol("features")
.setNumTrees(100);
Pipeline pipeline = new Pipeline()
.setStages(new PipelineStage[]{assembler, rf});
// Train model
PipelineModel model = pipeline.fit(trainingData);
// Evaluate
Dataset<Row> predictions = model.transform(testData);
MulticlassClassificationEvaluator evaluator =
new MulticlassClassificationEvaluator()
.setLabelCol("label")
.setPredictionCol("prediction")
.setMetricName("accuracy");
double accuracy = evaluator.evaluate(predictions);
log.info("Test Accuracy: {}", accuracy);
return model;
}
public double predict(PipelineModel model, Map<String, Object> features) {
// Create DataFrame from features
Row row = RowFactory.create(
features.get("feature1"),
features.get("feature2"),
features.get("feature3")
);
Dataset<Row> input = spark.createDataFrame(
Collections.singletonList(row),
createSchema()
);
Dataset<Row> prediction = model.transform(input);
return prediction.select("prediction").first().getDouble(0);
}
@PreDestroy
public void cleanup() {
if (spark != null) {
spark.stop();
}
}
}Hugging Face Transformers
Usando Modelos Pre-treinados
// Usando API HTTP da Hugging Face
@Service
public class HuggingFaceService {
@Value("${huggingface.api.key}")
private String apiKey;
private final WebClient webClient;
public HuggingFaceService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("https://api-inference.huggingface.co/models")
.defaultHeader("Authorization", "Bearer " + apiKey)
.build();
}
public Mono<String> analyzeSentiment(String text) {
return webClient.post()
.uri("/distilbert-base-uncased-finetuned-sst-2-english")
.bodyValue(Map.of("inputs", text))
.retrieve()
.bodyToMono(String.class);
}
public Mono<String> generateText(String prompt) {
return webClient.post()
.uri("/gpt2")
.bodyValue(Map.of(
"inputs", prompt,
"parameters", Map.of(
"max_length", 100,
"temperature", 0.7
)
))
.retrieve()
.bodyToMono(String.class);
}
public Mono<String> classifyText(String text, List<String> labels) {
return webClient.post()
.uri("/facebook/bart-large-mnli")
.bodyValue(Map.of(
"inputs", text,
"parameters", Map.of("candidate_labels", labels)
))
.retrieve()
.bodyToMono(String.class);
}
}Processamento de Linguagem Natural com OpenNLP
Análise de Texto
// build.gradle
dependencies {
implementation 'org.apache.opennlp:opennlp-tools:2.3.0'
}
@Service
public class NLPService {
private TokenizerME tokenizer;
private POSTaggerME posTagger;
private NameFinderME nameFinder;
@PostConstruct
public void loadModels() throws IOException {
// Load tokenizer model
try (InputStream tokenStream = getClass()
.getResourceAsStream("/models/en-token.bin")) {
TokenizerModel tokenModel = new TokenizerModel(tokenStream);
tokenizer = new TokenizerME(tokenModel);
}
// Load POS tagger model
try (InputStream posStream = getClass()
.getResourceAsStream("/models/en-pos-maxent.bin")) {
POSModel posModel = new POSModel(posStream);
posTagger = new POSTaggerME(posModel);
}
// Load name finder model
try (InputStream nameStream = getClass()
.getResourceAsStream("/models/en-ner-person.bin")) {
TokenNameFinderModel nameModel = new TokenNameFinderModel(nameStream);
nameFinder = new NameFinderME(nameModel);
}
}
public TextAnalysisResult analyzeText(String text) {
// Tokenize
String[] tokens = tokenizer.tokenize(text);
// POS tagging
String[] tags = posTagger.tag(tokens);
// Named entity recognition
Span[] nameSpans = nameFinder.find(tokens);
String[] names = Span.spansToStrings(nameSpans, tokens);
return TextAnalysisResult.builder()
.tokens(Arrays.asList(tokens))
.posTags(Arrays.asList(tags))
.entities(Arrays.asList(names))
.build();
}
public SentimentScore analyzeSentiment(String text) {
// Simple sentiment analysis based on keywords
String lowerText = text.toLowerCase();
List<String> positiveWords = Arrays.asList(
"good", "great", "excellent", "amazing", "wonderful"
);
List<String> negativeWords = Arrays.asList(
"bad", "terrible", "awful", "horrible", "poor"
);
long positive = positiveWords.stream()
.filter(lowerText::contains)
.count();
long negative = negativeWords.stream()
.filter(lowerText::contains)
.count();
double score = (positive - negative) / (double) (positive + negative + 1);
return new SentimentScore(score,
score > 0.3 ? "positive" : (score < -0.3 ? "negative" : "neutral"));
}
}
@Data
@Builder
public class TextAnalysisResult {
private List<String> tokens;
private List<String> posTags;
private List<String> entities;
}
@Data
@AllArgsConstructor
public class SentimentScore {
private double score;
private String sentiment;
}Embeddings e Busca Semântica
Vector Database com Pinecone
@Service
public class EmbeddingService {
@Autowired
private OpenAIService openAIService;
@Autowired
private WebClient pineconeClient;
public Mono<List<Double>> generateEmbedding(String text) {
return openAIService.createEmbedding(text)
.map(EmbeddingResponse::getData)
.map(data -> data.get(0).getEmbedding());
}
public Mono<Void> storeEmbedding(String id, String text, Map<String, String> metadata) {
return generateEmbedding(text)
.flatMap(embedding -> {
VectorUpsertRequest request = VectorUpsertRequest.builder()
.vectors(List.of(Vector.builder()
.id(id)
.values(embedding)
.metadata(metadata)
.build()))
.build();
return pineconeClient.post()
.uri("/vectors/upsert")
.bodyValue(request)
.retrieve()
.bodyToMono(Void.class);
});
}
public Mono<List<SearchResult>> semanticSearch(String query, int topK) {
return generateEmbedding(query)
.flatMap(embedding -> {
QueryRequest request = QueryRequest.builder()
.vector(embedding)
.topK(topK)
.includeMetadata(true)
.build();
return pineconeClient.post()
.uri("/query")
.bodyValue(request)
.retrieve()
.bodyToMono(QueryResponse.class);
})
.map(QueryResponse::getMatches);
}
}
@Data
@Builder
public class Vector {
private String id;
private List<Double> values;
private Map<String, String> metadata;
}
@Data
public class SearchResult {
private String id;
private double score;
private Map<String, String> metadata;
}Casos de Uso Práticos
1. Chatbot Inteligente
@Service
public class ChatbotService {
@Autowired
private OpenAIService openAIService;
@Autowired
private EmbeddingService embeddingService;
private final Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();
public Mono<String> chat(String userId, String message) {
// Get conversation history
List<Message> history = conversationHistory
.computeIfAbsent(userId, k -> new ArrayList<>());
// Add user message
history.add(new Message("user", message));
// Search relevant context
return embeddingService.semanticSearch(message, 3)
.flatMap(results -> {
String context = results.stream()
.map(r -> r.getMetadata().get("content"))
.collect(Collectors.joining("\n"));
// Build prompt with context
List<Message> messages = new ArrayList<>();
messages.add(new Message("system",
"You are a helpful assistant. Use this context: " + context));
messages.addAll(history);
return openAIService.generateChatCompletion(messages);
})
.doOnNext(reply -> {
// Add assistant reply to history
history.add(new Message("assistant", reply));
// Keep only last 10 messages
if (history.size() > 10) {
history.subList(0, history.size() - 10).clear();
}
});
}
}2. Sistema de Recomendação
@Service
public class RecommendationService {
@Autowired
private SparkMLService sparkMLService;
public List<ProductRecommendation> getRecommendations(
Long userId, int count) {
// Get user preferences
UserPreferences prefs = getUserPreferences(userId);
// Generate features
Map<String, Object> features = Map.of(
"age", prefs.getAge(),
"avgPurchaseAmount", prefs.getAvgPurchaseAmount(),
"categoryPreference", prefs.getCategoryPreference()
);
// Get predictions from ML model
double score = sparkMLService.predict(model, features);
// Query similar products
return productRepository.findSimilarProducts(score, count);
}
}Melhores Práticas
- Cache resultados: APIs de IA são caras - cache quando possível
- Implemente rate limiting: Proteja contra uso excessivo
- Use async/reactive: Requisições AI são lentas - não bloqueie threads
- Monitor custos: APIs pagas podem acumular custos rapidamente
- Fallback strategies: Tenha planos B quando IA falha
- Valide outputs: IA pode gerar respostas inesperadas
- Fine-tune quando necessário: Modelos gerais nem sempre são ideais
- Segurança: Nunca exponha API keys, use secrets management
Conclusão
Integrar IA em aplicações Java não é mais um desafio técnico - é uma questão de escolher as ferramentas certas e aplicá-las nos casos de uso apropriados.
De chatbots a sistemas de recomendação, as possibilidades são infinitas. Comece pequeno, experimente, e gradualmente construa soluções mais sofisticadas.