Spring AI
本文章以chat model,Image model, and template model為範例,介紹相關spring AI的基本應用,
chat model為詢問AI問題的基本操作,Image model則是實作一個AI圖片產生器,而template model則是可以對於chat問題做進一步的一些條件設定的範例.
▋環境建置
專案詳細程式碼可從文章末段『參考資料』的github取得完整測試程式碼,此處簡單說明首先需建立前後端專案稍微需留意的事項
Create Back-End project
Spring Initializr產生測試專案,依賴套件選擇OpenAI & Spring Web 這樣便可以產生一個簡單的Spring AI web專案
Create Fornt-End project
程式碼從參考資料取得即可,接下來只需在專案路徑 /spring-ai-demo-react 執行以下動作即可在http://localhost:3000/ 看到實作的結果
1.執行npm install 下載依賴module
2.執行npm start 啟動專案
Pricing
此專案使用OpenAI ChatGPT APIs,需要付費才能產生AI回應資料,否則執行API會出現以下錯誤
2025-01-16T15:24:45.671+08:00 ERROR 28353 --- [SpringAIDemo] [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.ai.retry.NonTransientAiException: 400 - {
"error": {
"code": "billing_hard_limit_reached",
"message": "Billing hard limit has been reached",
"param": null,
"type": "invalid_request_error"
}
}
] with root cause
付費說明: OpenAI
▋取得API Key
首先進入 Open AI 首頁,下滑到底下的分類選單 API login
登入帳號後選擇start building開始取得API key程序
接著需要輸入API key名稱以及Project name
按下產生API Key之後,即可看到唯一的一組API Key,接著將此key寫入application.properties
application.properties
將你取得的API KEY換掉${MY_API_KEY}即可
spring.application.name=SpringAIDemo
spring.ai.openai.api-key=${MY_API_KEY}
▋專案程式碼
Chat Model API
GenAIController
package com.ai.SpringAIDemo;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RequestMapping("/api")
@RestController
public class GenAIController {
@Autowired
ChatService chatService;
public GenAIController(ChatService chatService) {
this.chatService = chatService;
}
@GetMapping("/ask-ai")
public String getResponse(@RequestParam String prompt){
return chatService.getResponse(prompt);
}
@GetMapping("/ask-ai-options")
public String getResponseOptions(@RequestParam String prompt){
return chatService.getResponseOptions(prompt);
}
}
ChatService
package com.ai.SpringAIDemo;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.stereotype.Service;
@Service
public class ChatService {
private final ChatModel chatModel;
public ChatService(ChatModel chatModel) {
this.chatModel = chatModel;
}
public String getResponse(String prompt){
return chatModel.call(prompt);
}
public String getResponseOptions(String prompt){
ChatResponse response = chatModel
.call(new Prompt(
prompt,
OpenAiChatOptions.builder().withModel("GPT-4o")
.build()
));
return response.getResult().getOutput().getContent();
}
}
Image Model API
AI生成images
GenAIController
@GetMapping("/generate-single-Image")
public void generateSingleImages(HttpServletResponse response, @RequestParam String prompt) throws IOException {
ImageResponse imageResponse = imageService.generateImage(prompt);
// 單一image回傳單一url
String imageUrl = imageResponse.getResult().getOutput().getUrl();
//跳至外部網站
response.sendRedirect(imageUrl);
}
@GetMapping("/generate-Images")
public List<String> generateImages(HttpServletResponse response, @RequestParam String prompt) throws IOException {
ImageResponse imageResponse = imageService.generateImage(prompt);
// 多個images回傳多個url Streams to get Urls from ImageResponse
List<String> imageUrls = imageResponse.getResults().stream()
.map(result -> result.getOutput().getUrl()).collect(Collectors.toList());
return imageUrls;
}
ImageService
@Service
public class ImageService {
private final OpenAiImageModel openAiImageModel;
public ImageService(OpenAiImageModel openAiImageModel) {
this.openAiImageModel = openAiImageModel;
}
public ImageResponse generateImage(String prompt){
// ImageResponse imageResponse = openAiImageModel.call(
// new ImagePrompt(prompt)
// );
ImageResponse imageResponse = openAiImageModel.call(
new ImagePrompt(prompt,
OpenAiImageOptions.builder()
.withModel("dall-e-2")
.withQuality("hd")
.withN(3)
.withHeight(1024)
.withWidth(1024).build())
);
return imageResponse;
}
}
generate-Images生成三個URL
載網頁中執行api之後可以看到包含三個URL的陣列資料
將其中一個URL拿出來到分頁執行就會呈現圖片如下
PS:URL 圖片存活時間(life time limit)根據模型的不同,通常為幾小時(EX:1~3hrs)
因此你不能將URL存到DB資料,而是要另外將圖永久保留
Recipe Model API
我們可以建立一個Customer support bot 客服機器人,作為網站客戶Q&A問題的應用,此處以一個食譜產生器為範本,
當客戶輸入需要的材料(EX:Olives,photatoes...etc),食譜名稱(any: 任何食譜),飲食限制(EX:vegan 素食),即可提供客戶
客製化的食譜建議
GenAIController
@GetMapping("recipe-creator")
public String recipeCreator(@RequestParam String ingredients,
@RequestParam(defaultValue = "any") String cuisine,
@RequestParam(defaultValue = "") String dietaryRestriction) {
return recipeService.createRecipe(ingredients, cuisine, dietaryRestriction);
}
RecipeService
package com.ai.SpringAIDemo;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class RecipeService {
private final ChatModel chatModel;
public RecipeService(ChatModel chatModel) {
this.chatModel = chatModel;
}
public String createRecipe(String ingredients, String cuisine,String dietaryRestrictions ){
// Create template with dynamic variables in it
var template = """
I want to create a recipe using the following ingredients: {ingredients}.
The cuisine type I prefer is {cuisine}.
Please consider the following dietary restrictions: {dietaryRestrictions}.
Please provide me with a detailed recipe including title, list of ingredients, and cooking instructions
""";
PromptTemplate promptTemplate = new PromptTemplate(template);
// make use of map over here to replace those with values
Map<String, Object> params = Map.of(
"ingredients",ingredients,
"cuisine", cuisine,
"dietaryRestrictions", dietaryRestrictions
);
Prompt prompt = promptTemplate.create(params);
// fetching the result output and content over here
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
使用posman執行測試
輸出結果:
Ingredients:
- 1 lb potatoes, diced
- 1/2 cup olives, sliced
- 1/4 cup red onion, diced
- 1/4 cup fresh parsley, chopped
- 2 tablespoons o1]ve oil
- 1 tablespoon red wine vinegar
- Salt and pepper to taste
Instrustions:
...etc
▋參考資料
Spring AI Full Course with Projects – Build Smarter Spring Boot Applications
https://www.youtube.com/watch?v=9Crrhz0pm8s&t=4470s
https://github.com/EmbarkXOfficial/spring-ai-masters/tree/main/spring-ai-demo