It's impossible to spend a day in tech without hearing the words "Artificial Intelligence". In this presentation we will embark on a journey into the world of Artificial Intelligence (AI) specifically designed for beginners. We'll start by introducing the fundamental concepts of AI, demystifying its jargon, and exploring its potential impact on our everyday lives.
Next, we will explore Spring AI. Its goal is to simplify the development of applications that incorporate artificial intelligence functionality, without introducing unnecessary complexity. We will cover the basics of setting up a Spring AI project, how to integrate it with existing Spring Boot applications, and how to use its various components to implement common AI tasks.
Whether you want to add chatbots to your app, generate recommendations, or analyze sentiments in text, Spring AI provides a streamlined and efficient approach to integrating these features. By the end of this talk, you will have a solid grasp of AI basics and how to incorporate them into your Spring applications using Spring AI.
- Getting Started Demo
- Prompts Demo
- SimplePrompt
- DadJokesController
- YouTube
- OutputParser
- Bring You Own Data
- Stuff the Prompt
- RAG Demo
- Function
In this demo you will create a simple ChatController
that can send a message to OpenAI.
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/api/generate")
public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a Dad Joke") String message) {
return Map.of("generation",chatClient.call(message));
}
}
Request
http :8080/api/generate
Response
HTTP/1.1 200
Connection: keep-alive
Content-Type: application/json
Date: Wed, 27 Mar 2024 20:43:20 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked
{
"generation": "Why don't scientists trust atoms?\n\nBecause they make up everything!"
}
Walk through the Prompt class and its different constructors.
@RestController
public class SimplePrompt {
private final ChatClient chatClient;
public SimplePrompt(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/api/simple-prompt")
public String simple() {
return chatClient.call(
new Prompt("How long has The Java Programming language been around?"))
.getResult().getOutput().getContent();
}
}
Show the different types of messages
@RestController
public class DadJokeController {
private final ChatClient chatClient;
public DadJokeController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/api/jokes")
public String jokes() {
var system = new SystemMessage("You primary function is to tell Dad Jokes. If someone asks you for any other type of joke please tell them you only know Dad Jokes");
var user = new UserMessage("Tell me a joke");
// var user = new UserMessage("Tell me a very serious joke about the earth");
Prompt prompt = new Prompt(List.of(system, user));
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
Show off PromptTemplate
by using a simple string
@GetMapping("/popular-step-one")
public String findPopularYouTubersStepOne(@RequestParam(value = "genre", defaultValue = "tech") String genre) {
String message = """
List 10 of the most popular YouTubers in {genre} along with their current subscriber counts. If you don't know
the answer , just say "I don't know".
""";
PromptTemplate promptTemplate = new PromptTemplate(message);
Prompt prompt = promptTemplate.create(Map.of("genre",genre));
return chatClient.call(prompt).getResult().getOutput().getContent();
}
And then externalizing that to a classpath resource
@GetMapping("/popular")
public String findPopularYouTubers(@RequestParam(value = "genre", defaultValue = "tech") String genre) {
PromptTemplate promptTemplate = new PromptTemplate(ytPromptResource);
Prompt prompt = promptTemplate.create(Map.of("genre", genre));
return chatClient.call(prompt).getResult().getOutput().getContent();
}
If you make a call with the following prompt and ask for the content you will get it back as a String
@GetMapping("/ken")
public Generation getBooksByKen() {
String promptMessage = """
Generate a list of books written by the author {author}.
""";
PromptTemplate promptTemplate = new PromptTemplate(promptMessage, Map.of("author","Ken Kousen"));
Prompt prompt = promptTemplate.create();
return chatClient.call(prompt).getResult().getOutput().getContent();
}
Request
http :8080/api/books/craig
Response
- "Spring in Action"
- "Spring Boot in Action"
- "Modular Java: Creating Flexible Applications with OSGi and Spring"
- "XDoclet in Action"
- "Spring Microservices in Action"
- "Getting started with Spring Framework: a hands-on guide to begin developing applications using Spring Framework"
- "Spring in Action, Fifth Edition"
- "Spring in Action, Fourth Edition"
- "Spring Boot in Action, Second Edition"
You can ask for a JSON formatted String:
String promptMessage = """
Generate a list of books written by the author {author}. Please return it to me in JSON format.
""";
And you will get this back, but then you still need to convert this raw JSON into an object.
[
{
"author": "Craig Walls",
"title": "Spring in Action",
"year": "2014"
},
{
"author": "Craig Walls",
"title": "Spring Boot in Action",
"year": "2015"
},
{
"author": "Craig Walls",
"title": "Spring in Action, Fifth Edition",
"year": "2018"
},
{
"author": "Craig Walls",
"title": "Modular Java: Creating Flexible Applications with Osgi and Spring",
"year": "2009"
},
{
"author": "Craig Walls",
"title": "XDoclet in Action",
"year": "2003"
}
]
In the final demo we use the BeanOutputParser
@GetMapping("/by-author")
public Author getBooksByAuthor(@RequestParam(value = "author", defaultValue = "Ken Kousen") String author) {
var outputParser = new BeanOutputParser<>(Author.class);
String format = outputParser.getFormat();
System.out.println("format = " + format);
String promptMessage = """
Generate a list of books written by the author {author}.
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(promptMessage, Map.of("author",author,"format", format));
Prompt prompt = promptTemplate.create();
Generation generation = chatClient.call(prompt).getResult();
Author authorResult = outputParser.parse(generation.getOutput().getContent());
return authorResult;
}
How to use your own data in AI applications
Request
http :8080/olympics/2024
Response
HTTP/1.1 200
Connection: keep-alive
Content-Length: 46
Content-Type: text/plain;charset=UTF-8
Date: Wed, 27 Mar 2024 20:52:41 GMT
Keep-Alive: timeout=60
I'm sorry but I don't know the answer to that.
Adding context to the prompt:
@GetMapping("/2024")
public String get2024OlympicSports(
@RequestParam(value = "message", defaultValue = "What sports are being included in the 2024 Summer Olympics?") String message,
@RequestParam(value = "stuffit", defaultValue = "false") boolean stuffit
) {
PromptTemplate promptTemplate = new PromptTemplate(olympicSportsResource);
Map<String,Object> map = new HashMap<>();
map.put("question",message);
if(stuffit) {
map.put("context", docsToStuffResource);
} else {
map.put("context", "");
}
Prompt prompt = promptTemplate.create(map);
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
Response
~ 🚀 http :8080/olympics/2024 stuffit==true
HTTP/1.1 200
Connection: keep-alive
Content-Length: 557
Content-Type: text/plain;charset=UTF-8
Date: Thu, 28 Mar 2024 02:00:34 GMT
Keep-Alive: timeout=60
Archery, athletics, badminton, basketball , basketball 3×3, boxing, canoe slalom, canoe sprint, road cycling, cycling track, mountain biking, BMX freestyle, BMX racing, equestrian, fencing, football, golf, artistic gymnastics, rhythmic gymnastics, trampoline, handball, hockey, judo, modern pentathlon, rowing, rugby, sailing, shooting, table tennis, taekwondo, tennis, triathlon, volleyball, beach volleyball, diving, marathon swimming, artistic swimming, swimming, water polo, weightlifting,wrestling,breaking, sport climbing, skateboarding, and surfing.
The RAG Demo uses a SimpleVectorStore
to store some FAQs about the upcoming Summer Olympics in Paris.
@Bean
SimpleVectorStore simpleVectorStore(EmbeddingClient embeddingClient) {
var simpleVectorStore = new SimpleVectorStore(embeddingClient);
var vectorStoreFile = new File(vectorStorePath);
if (vectorStoreFile.exists()) {
log.info("Vector Store File Exists,");
simpleVectorStore.load(vectorStoreFile);
} else {
log.info("Vector Store File Does Not Exist, load documents");
TextReader textReader = new TextReader(faq);
textReader.getCustomMetadata().put("filename", "olympic-faq.txt");
List<Document> documents = textReader.get();
TextSplitter textSplitter = new TokenTextSplitter();
List<Document> splitDocuments = textSplitter.apply(documents);
simpleVectorStore.add(splitDocuments);
simpleVectorStore.save(vectorStoreFile);
}
return simpleVectorStore;
}
Request
http :8080/faq message=="Where are the next 3 summer games?"
Response
HTTP/1.1 200
Connection: keep-alive
Content-Length: 133
Content-Type: text/plain;charset=UTF-8
Date: Thu, 28 Mar 2024 12:18:27 GMT
Keep-Alive: timeout=60
The next three Summer Olympic Games will be held in Paris, France in 2024, Los Angeles, USA in 2028, and Brisbane, Australia in 2032.
The code related to this demo is in the functions
package. In this demo there is a CityController
with a
/cities
endpoint that will answer questions about cities around the world.
Request
http :8080/cities message=="What is the largest city in Ohio"
Response
HTTP/1.1 200
Connection: keep-alive
Content-Length: 51
Content-Type: text/plain;charset=UTF-8
Date: Thu, 25 Apr 2024 21:38:46 GMT
Keep-Alive: timeout=60
The largest city in Ohio by population is Columbus.
If you were to ask it what the current weather is like in Columbus it wouldn't know the answer to that.
Request
http :8080/cities message=="What is current weather in Columbus"
Response
HTTP/1.1 200
Connection: keep-alive
Content-Length: 185
Content-Type: text/plain;charset=UTF-8
Date: Thu, 25 Apr 2024 21:40:54 GMT
Keep-Alive: timeout=60
Sorry, as an AI, I'm unable to provide real-time information such as current weather updates. I recommend checking a reliable weather website or app for the most up-to-date information.
We can define a function by declaring a bean and the weather service will be responsible for determining the current weather.
@Bean
@Description("Get the current weather conditions for the given city.")
public Function<WeatherService.Request,WeatherService.Response> currentWeatherFunction() {
return new WeatherService(props);
}