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

- Introdução
- O que vamos ver hoje?
- 1. Classe Principal da Aplicação
- 2. Controlador REST (ChatbotService)
- 3. Função para Buscar Posts do Blog (FunctionSearchBlogPosts)
- 4. Função para Buscar Produtos (FunctionSearchProducts)
- 5. Função de Preço do Produto (FunctionProductPricing)
- 6. Função para Avaliações do Produto (FunctionProductReviews)
- 7. Configuração no application.properties
- 8. Configuração do Maven (pom.xml - trecho)
- Exemplos de Teste com cURL
- Chamadas Encadeadas de Funções
- Conclusão
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?
-
Configuração básica do projeto Spring Boot para habilitar o uso do Spring AI.
-
Controlador REST que recebe uma mensagem do usuário e interage com o modelo de linguagem do ChatGPT.
-
Funções Java para buscar posts de um blog e simular um E-commerce (busca de produtos, preços e avaliações).
-
Exemplos de uso com cURL para você testar.
-
Importância das anotações
@Description
e@JsonClassDescription
. -
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 porta8080
.
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 umUserMessage
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 |
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 recordRequest
) 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!