Spring Boot e ChatGPT: Utilize IA para Chamar Métodos Java e Enriquecer Seu Chatbot

Por Gaspar Barancelli Junior em 17 de janeiro de 2025
Imagem ilustrativa sobre o post Spring Boot e ChatGPT: Utilize IA para Chamar Métodos Java e Enriquecer Seu Chatbot

Introdução

Fala galera!

Neste post, não vou enrolar muito e vou direto ao ponto: vamos ver como a IA pode ser utilizada em conjunto com Spring Boot e ChatGPT para chamar funções Java e enriquecer as respostas do chat com dados externos, seja de um banco de dados, de alguma API ou qualquer fonte de informação.

O que vamos ver hoje?

  1. Configuração básica do projeto Spring Boot para habilitar o uso do Spring AI.

  2. Controlador REST que recebe uma mensagem do usuário e interage com o modelo de linguagem do ChatGPT.

  3. Funções Java para buscar posts de um blog e simular um E-commerce (busca de produtos, preços e avaliações).

  4. Exemplos de uso com cURL para você testar.

  5. Importância das anotações @Description e @JsonClassDescription.

  6. Funções encadeadas: como o modelo pode chamar mais de uma função em sequência para resolver a mesma pergunta.

O código completo está disponível no GitHub: github.com/gasparbarancelli/demo-spring-boot-ai-function

Bora começar!!!

1. Classe Principal da Aplicação

package com.gasparbarancelli;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringAiFunctionApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringAiFunctionApplication.class, args);
    }

}

O que faz:

  • Inicia o contexto do Spring Boot.

  • Ao executar esse main, sua aplicação estará disponível para receber requisições REST na porta 8080.

2. Controlador REST (ChatbotService)

package com.gasparbarancelli;

import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class ChatbotService {

    private final OpenAiChatModel openAiChatClient;

    public ChatbotService(OpenAiChatModel openAiChatClient) {
        this.openAiChatClient = openAiChatClient;
    }

    @GetMapping
    public String hello(@RequestParam("message") String message) {
        UserMessage userMessage = new UserMessage(message);
        ChatResponse response = openAiChatClient.call(new Prompt(userMessage));
        return response.getResult().getOutput().getContent();
    }

}

O que faz:

  • Exponde um endpoint GET /?message=…​.

  • Recebe a mensagem do usuário (message), cria um UserMessage e envia para o OpenAiChatModel.

  • Se o modelo precisar de dados externos, chamará as funções Java (via Function Calling) que veremos abaixo.

  • Retorna o texto final produzido pelo modelo como resposta ao usuário.

Tip

O Spring Boot AI cuida de instanciar e configurar o OpenAiChatModel com base em suas propriedades (application.properties ou application.yml). Basta você injetar este Bean no construtor do seu controlador, como mostrado.

3. Função para Buscar Posts do Blog (FunctionSearchBlogPosts)

package com.gasparbarancelli;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Function;

@Service
@Description("Busca posts relacionados a um tópico no blog gasparbarancelli.com")
public class FunctionSearchBlogPosts implements Function<FunctionSearchBlogPosts.Request, FunctionSearchBlogPosts.Response> {

    @JsonClassDescription("Obtem os posts do blog relacionados a um determinado assunto")
    public record Request(String topic) {}
    public record BlogPost(String title, String url) {}
    public record Response(List<BlogPost> posts) {}

    @Override
    public Response apply(Request request) {
        List<BlogPost> allPosts = List.of(
            new BlogPost("Aprendendo Spring Boot", "https://gasparbarancelli.com/posts/spring-boot"),
            new BlogPost("Dicas de Java 17", "https://gasparbarancelli.com/posts/java-17"),
            new BlogPost("Testes com JUnit", "https://gasparbarancelli.com/posts/junit-tests")
        );

        List<BlogPost> filtered = allPosts.stream()
            .filter(post -> post.title().toLowerCase().contains(request.topic().toLowerCase()))
            .toList();

        return new Response(filtered);
    }

}

O que faz:

  • Recebe como parâmetro o nome de um tópico (ex: “Java”, “Spring Boot”).

  • Filtra a lista de posts (mocks) para retornar somente os relevantes ao tema solicitado.

  • Pode ser facilmente adaptado para buscar em um banco de dados real.

Importância de @Description e @JsonClassDescription

  • A anotação @Description (no nível da classe) ajuda o modelo de IA a entender quando essa função deve ser chamada.

  • A anotação @JsonClassDescription (no record Request) adiciona metadados para o JSON Schema que o Spring Boot AI gera. Isso orienta o modelo sobre como montar os argumentos JSON para chamar essa função.

4. Função para Buscar Produtos (FunctionSearchProducts)

package com.gasparbarancelli;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Function;

@Service
@Description("Busca de produtos comercializados")
public class FunctionSearchProducts implements Function<FunctionSearchProducts.Request, FunctionSearchProducts.Response> {

    @JsonClassDescription("Obtem uma lista de produtos comercializados filtrando pelo seu nome ou categoria")
    public record Request(String query) {}
    public record Product(String name, String category, String description) {}
    public record Response(List<Product> products) {}

    @Override
    public Response apply(Request request) {
        List<Product> allProducts = List.of(
            new Product("Notebook Dell", "informática", "Notebook para uso profissional"),
            new Product("Cadeira Gamer", "escritório", "Cadeira ergonômica para escritório"),
            new Product("Mouse Sem Fio", "informática", "Mouse sem fio com alta precisão")
        );

        List<Product> filtered = allProducts.stream()
            .filter(p -> p.name().toLowerCase().contains(request.query().toLowerCase())
                      || p.category().toLowerCase().contains(request.query().toLowerCase()))
            .toList();

        return new Response(filtered);
    }

}

O que faz:

  • Recebe o termo de busca (query), por exemplo: “informática” ou “cadeira”.

  • Retorna produtos que contenham aquele termo no nome ou na categoria.

  • Simula um catálogo de produtos de uma loja virtual.

5. Função de Preço do Produto (FunctionProductPricing)

package com.gasparbarancelli;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.function.Function;

@Service
@Description("Obtem o preço/valor do produto")
public class FunctionProductPricing implements Function<FunctionProductPricing.Request, FunctionProductPricing.Response> {

    @JsonClassDescription("Encontra o produto por seu nome e retorna o preço/valor atualizado")
    public record Request(String productName) {}
    public record Response(String productName, BigDecimal price, String currency) {}

    @Override
    public Response apply(Request request) {
        BigDecimal price;
        if (request.productName().toLowerCase().contains("notebook")) {
            price = BigDecimal.valueOf(3_500.00);
        } else if (request.productName().toLowerCase().contains("cadeira")) {
            price = BigDecimal.valueOf(999.99);
        } else {
            price = BigDecimal.valueOf(150.99);
        }
        return new Response(request.productName(), price, "BRL");
    }

}

O que faz:

  • Recebe o nome do produto (ex.: “Notebook Dell”).

  • Calcula (neste caso, simula) um preço em Reais (BRL).

  • Retorna o valor do produto para ser exibido ao usuário no chat.

6. Função para Avaliações do Produto (FunctionProductReviews)

package com.gasparbarancelli;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Function;

@Service
@Description("Obtem as avaliações de determinado produto")
public class FunctionProductReviews implements Function<FunctionProductReviews.Request, FunctionProductReviews.Response> {

    @JsonClassDescription("Recupera o produto por seu nome e retorna as avaliações feitas por outros compradores")
    public record Request(String productName) {}
    public record Review(String user, String comment, int rating) {}
    public record Response(String productName, List<Review> reviews) {}

    @Override
    public Response apply(Request request) {
        List<Review> reviews;
        if (request.productName().toLowerCase().contains("notebook")) {
            reviews = List.of(
                new Review("Gaspar", "Ótimo produto!", 5),
                new Review("Laura", "Atende bem, mas poderia ter mais memória", 4)
            );
        } else {
            reviews = List.of(
                new Review("Cecília", "Bom custo-benefício.", 4)
            );
        }
        return new Response(request.productName(), reviews);
    }

}

O que faz:

  • Recebe o nome do produto.

  • Retorna uma lista de “reviews” pré-definidas, podendo ser positivas ou “neutras”.

  • Em um cenário real, poderia buscar avaliações em um banco de dados ou serviço externo.

7. Configuração no application.properties

spring.application.name=spring-ai-function

spring.ai.openai.api-key=${API_KEY}
spring.ai.openai.chat.options.model=gpt-4o
spring.ai.openai.chat.options.functions=functionSearchBlogPosts,functionSearchProducts,functionProductPricing,functionProductReviews

O que faz:

  • Define a chave da API da OpenAI (${API_KEY} deve ser substituído pela sua).

  • Escolhe o modelo a ser usado (gpt-4o).

  • Especifica as funções que queremos habilitar no chat (separadas por vírgula). O Spring AI associará esses nomes aos beans correspondentes (functionSearchBlogPosts, functionSearchProducts, etc.).

8. Configuração do Maven (pom.xml - trecho)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>
    <!-- ... -->
</dependencies>

O que faz:

  • spring-boot-starter-web: disponibiliza as funções de servidor web (REST).

  • spring-ai-openai-spring-boot-starter: integra o Spring AI ao OpenAI, viabilizando o Function Calling.

Exemplos de Teste com cURL

Depois de subir a aplicação (porta 8080 por padrão), teste as requisições:

  • Buscar posts sobre Java

curl 'http://localhost:8080/?message=posts%20sobre%20java'

Resposta (exemplo)

Encontrei um post relacionado a Java no blog gasparbarancelli.com:

- [Dicas de Java 17](https://gasparbarancelli.com/posts/java-17)

Você pode acessar o link para ler mais sobre o assunto.
  • Buscar posts sobre Spring Boot

curl 'http://localhost:8080/?message=tutorial%20sobre%20spring%20boot'

Resposta (exemplo)

Encontrei um tutorial sobre Spring Boot que pode te ajudar. Confira o artigo
[Aprendendo Spring Boot](https://gasparbarancelli.com/posts/spring-boot) no blog gasparbarancelli.com
  • Consultar produtos de informática

curl 'http://localhost:8080/?message=voces%20vendem%20produtos%20de%20informatica'

Resposta (exemplo)

Sim, nós vendemos produtos de informática. Aqui estão algumas opções disponíveis:

1. **Notebook Dell**: Ideal para uso profissional.
2. **Mouse Sem Fio**: Oferece alta precisão para suas atividades.

Se precisar de mais informações ou estiver interessado em algum desses produtos, estou à disposição para ajudar!
  • Obter valor de um produto

curl 'http://localhost:8080/?message=qual%20o%20valor%20do%20notebook%20dell'

Resposta (exemplo)

O valor do Notebook Dell é R$ 3.500,00
  • Consultar avaliações do Notebook Dell

curl 'http://localhost:8080/?message=as%20avaliacoes%20do%20notebook%20dell%20sao%20possitivas%20ou%20negativas'

Resposta (exemplo)

As avaliações do notebook Dell são positivas. Um usuário comentou "Ótimo produto!"
com uma avaliação de 5 estrelas, enquanto outro comentou
"Atende bem, mas poderia ter mais memória" com uma avaliação de 4 estrelas.

Chamadas Encadeadas de Funções

Em muitos casos, o modelo pode decidir chamar mais de uma função em sequência para responder a mesma pergunta. Por exemplo:

  • O usuário pergunta: “Quanto custa o Notebook Dell?”

  • O modelo primeiro chama a função functionSearchProducts para confirmar que existe mesmo um produto chamado “Notebook Dell”.

  • Em seguida, chama functionProductPricing para obter o preço.

O Spring Boot AI abstrai toda essa complexidade: se o modelo retornar JSON dizendo que quer chamar a função A e depois a B, o framework converte esses JSONs em objetos de request, chama as funções e leva o resultado de volta ao modelo, tudo de forma transparente.

Esse padrão segue o fluxo explicado na documentação do Spring Boot AI, onde a IA retorna algo como:

{
  "name": "functionProductPricing",
  "arguments": {
    "productName": "Notebook Dell"
  }
}

E, caso necessário, ela pode gerar outro JSON para chamar outra função, num único fluxo de conversa.

Conclusão

O Spring AI cuida de toda a ponte entre o modelo de linguagem e suas funções Java, tornando mais fácil enriquecer as respostas do chat com dados reais de qualquer fonte (bancos de dados, APIs externas, etc.).

As anotações @Description e @JsonClassDescription ajudam o modelo a entender quando e como chamar cada função, e o Spring AI gera automaticamente o schema necessário para orientar o ChatGPT.

Fique ligado no blog porque sempre trago novidades sobre Java, Spring e Inteligência Artificial, essa união tem muito a oferecer para quem deseja construir aplicações inteligentes e dinâmicas!

// Compartilhe esse Post

💫
🔥 NOVO APP

Domine o Inglês em 30 dias!

Inteligência Artificial + Repetição Espaçada • Método cientificamente comprovado

✅ Grátis para começar 🚀 Resultados rápidos
×