Spring AI openAI

Spring AI

【Larry】林鈺涵 2025/01/17 15:26:56
38

本文章以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問題的應用,此處以一個食譜產生器為範本,

當客戶輸入需要的材料(EXOlives,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

【Larry】林鈺涵