Criando uma aplicativo em java para conversar com Gemini-1.5-flash
- #Java
- #Inteligência Artificial (IA)
Nos dias de hoje, o uso crescente de Large Language Models (LLMs) tem impulsionado a necessidade de incorporá-los em nossos projetos. No entanto, encontrar exemplos específicos de como integrar esses modelos em aplicativos Java pode ser um desafio. Neste contexto, a criação de um aplicativo Java para conversar com o Gemini 1.5 Flash pode ser uma excelente oportunidade para explorar essa tecnologia e aprender como introduzir LLMs em nossos próprios projetos.
O aplicativo é um Chat usando o console, de maneira que o código ficasse o mais simples possível. Essa abordagem evitou o uso de dependências externas e com isso o código ficou com 78 linhas.
O código basicamente tem 4 métodos:
- Método main(): Este é o ponto de entrada da aplicação. Ele cria um Scanner para receber a entrada do usuário em um loop infinito que simula um chat. Quando o usuário digita "quit", o programa é encerrado.
- Método sendRequest(): é responsável por enviar a solicitação à API da Google. Primeiro, ele constrói o prompt completo, combinando o histórico de conversa e a pergunta atual. Em seguida, chama o método conversationHistory() para atualizar o histórico. Depois, constrói o corpo da solicitação HTTP no formato JSON e cria um HttpClient e um HttpRequest com a URL, cabeçalhos e corpo da solicitação. O HttpClient envia a solicitação e recebe a resposta, que é processada pelo método processResponse().
- Método processResponse(): lida com a resposta recebida da API da Google. Ele verifica o código de status da resposta e, se necessário, imprime um erro. Além disso, utiliza um padrão regular para extrair o texto da resposta e constrói a resposta final a partir do texto extraído.
- Método conversationHistory(): têm a função de manter um registro das interações em um sistema de conversação. Sua principal responsabilidade é gerenciar o histórico dessas conversas, garantindo que ele não cresça indefinidamente e que as informações mais recentes estejam sempre acessíveis.
Regex ao invés de APIs Json
O Método processResponse() usa uma expressão regular para “desmontar” a resposta json. O uso dessa estratégia foi simplesmente para diminuir o número de linhas de código. Após muita pesquisa cheguei nessa expressão: "\"text\"\\s:\\s\"([^\"]+)\"
Explicação de cada parte da expressão:
- "text": Procura literalmente a palavra "text" no texto.
- \\s: Corresponde a qualquer caractere de espaço em branco, como espaço, tabulação ou quebra de linha.
- :: Procura literalmente o caractere dois pontos.
- \"([^\"]+)\":
- \": Procura literalmente uma aspa dupla.
- ([^\"])+:
- ( e ): Agrupam a expressão dentro dos parênteses.
- [^"]: Corresponde a qualquer caractere que não seja uma aspa dupla.
- +: Indica que o caractere anterior (qualquer caractere que não seja aspa dupla) pode ocorrer uma ou mais vezes.
- \": Procura literalmente uma aspa dupla novamente.
Em um código de produção, talvez não fosse uma boa estratégia usar expressões regulares por sua complexidade e dificuldade de entendimento.
Ao criar esse aplicativo, demonstramos o potencial dos LLMs em transformar a maneira como programamos. A integração de LLMs em projetos reais exige um profundo conhecimento das tecnologias envolvidas e muitos testes. Este trabalho pode servir como um ponto de partida para que outros desenvolvedores possam explorar essas novas ferramentas e construir aplicações inovadoras.
Quem quiser dar uma olhada, o projeto está em
https://github.com/gazolla/GeminiChat
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class App {
private static final String API_KEY = "<<Adicione sua chave aqui>>";
private static final String URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:streamGenerateContent";
private static final ArrayList<String> history = new ArrayList<String>();
private static final int CAPACITY = 100;
public static void main(String[] args) throws IOException, InterruptedException {
try(var scanner = new Scanner(System.in)) {
while(true) {
System.out.println("you:");
var ask = scanner.nextLine();
if (ask.equals("quit")) {
System.out.println("answer: bye");
System.exit(0);
}
sendRequest(ask);
}
}
}
private static void sendRequest(String prompt) throws IOException, InterruptedException {
var context = history.stream().collect(Collectors.joining("\n"));
var fullPrompt = context + "return the result as plain text without any formatting, special characters, or backticks. Question:" + prompt;
conversationHistory(prompt);
var jsonRequest = "{\"contents\":[{\"parts\":[{\"text\":\"" + fullPrompt + "\"}],\"role\":\"user\"}]}";
var httpClient = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create(URL + "?alt=sse&key=" + API_KEY))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonRequest))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
processResponse(response);
}
private static void processResponse(HttpResponse<String> response) throws IOException {
if (response.statusCode() != 200) {
System.out.println("Error: " + response.statusCode());
return;
}
var pattern = Pattern.compile("\"text\"\\s*:\\s*\"([^\"]+)\"");
var answer = new StringBuilder();
String line;
try(var reader = new BufferedReader(new StringReader(response.body()))){
while ((line = reader.readLine()) != null) {
if(line.isEmpty()) continue;
Matcher matcher = pattern.matcher(line.substring(5));
if(matcher.find()) {
answer.append(matcher.group(1)).append(" ");
}
}
}
System.out.println("answer: " + answer.toString());
}
private static String conversationHistory(String prompt) {
if(history.size() == CAPACITY) {
history.remove(0);
}
history.add(prompt);
var result = history.stream().collect(Collectors.joining("\n"));
return result;
}
}