iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
Build on Google AI

單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?系列 第 7

Day 7 |「教練,我想變強!」在後端注入靈魂,實作 Gemini API 核心串接

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20260816/201650436yXZDAnu1S.png

前言:為後端注入 AI 靈魂

昨天我們成功啟動了 Java 21 + Spring Boot 3 的伺服器,今天我們終於要變得更強!將 Day 5 申請的 Gemini API 整合進後端,建立一個專屬的 HTTP Client 了!

我們不打算手動複製金鑰到 IDE 的環境變數裡。今天我們要挑戰的是:使用 Spring Boot 3 的最新技術,結合 Google Cloud Secret Manager 與 ADC (Application Default Credentials),優雅且極度安全地完成這次串接。同時,在正式發送請求前,我們也要釐清 API 的計費層級,確保你的錢包跟程式碼一樣安全!

觀念解說 1:發送請求前必看!Free Tier vs Tier 1

在把金鑰寫進專案前,我們必須先了解你的 Google Cloud 帳號層級帶來的影響:

評估維度 Free Tier (免費試用層級) Tier 1 (Pay-as-you-go 付費層級)
開通方式 註冊 Google AI Studio 即可使用 綁定 Google Cloud 帳單與信用卡後升級
速率限制 (Rate Limit) 限制較嚴格 (如 15 RPM) 額度大幅提升,適合正式環境
資料隱私 資料可能被用於改進模型 承諾資料隱私保護,不被訓練
計費方式 一定量完全免費 依百萬 Token 數精準計費

如果只是個人本地端測試,Free Tier 相當夠用;但當我們準備將 API 部署上線時,升級至 Tier 1 才能確保高併發流量下不被限流,同時保障企業級的資料隱私,由於我的帳號目前是Tier 1(帳號被默默地升上去,開KAKERU專案後怎麼選都沒辦法改成免費的😭),所以先以Tier 1來說明。

觀念解說 2:使用 RestClient?

在 Spring Boot 3.2 以前,我們發送 HTTP 請求通常有兩個選擇:

  1. RestTemplate:雖然經典,但寫法稍嫌冗長。
  2. WebClient:寫法優雅,但為了一個簡單的請求,卻必須引入整套 WebFlux (響應式編程) 生態系,猶如牛刀割雞。

而全新引入的 RestClient 完美解決了這個痛點!它擁有流暢的 Fluent API,底層依然保持同步呼叫的輕量,寫起來卻像 WebClient 一樣優雅,絕對是目前串接 RESTful API 的最佳選擇。

觀念解說 3:金鑰零落地!實作 application.yml 與 ADC

為了達成「本機不寫死金鑰、程式碼不見機密」的最高資安原則,我們透過以下兩步驟來實現:

1. 本機驗證 (ADC):
請在你的終端機執行以下指令:
gcloud auth application-default login
執行後,系統會生成 ADC。這讓 Spring Boot 在本地開發時,能直接「代表你的權限」連線至 Google Cloud Secret Manager,完全不需要手動複製貼上金鑰。

2. 引入依賴與配置導入:
在實作前,請務必確認你的 pom.xml 已經引入了 Google Cloud 專屬套件,但你也可以透過下 Prompt的方式來達成。
你可以透過下Prompt的方式請AI幫你產生:

請給我幫達成Spring Boot 3,將 GCP Secret Manager 動態注入 Gemini API Key 的方法。

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-Google Cloud-starter-secretmanager</artifactId>
</dependency>

接著,在 application.yml 中可以看到此設定:

spring:
  config:
    import: "sm://" # 核心:告知 Spring Boot 啟動時加載 Google Cloud Secret Manager 屬性源
  cloud:
    Google Cloud:
      project-id: kakeru-ai # 指定你的 Google Cloud 專案 ID

gemini:
  api:
    # 核心:透過 sm:// 協議自動注入 Google Cloud Secret Manager 中的最新金鑰
    key: ${sm://projects/kakeru-ai/secrets/GEMINI_API_KEY/versions/latest}
    model: gemini-3.5-flash-lite
    url: https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash-lite:generateContent

動手實作 1:RestClient 核心發送

叫 Gemini API 時,最大的痛點是它的 JSON 結構層級較深(contents -> parts -> text)。

我們直接把需求拋給 AI:

我想用 Spring Boot 3 的 RestClient 寫一個 GeminiService 來發送文字請求,請幫我寫 Service 實作。

AI 立刻運用建構子注入與 API 寫出了乾淨的實作:

@Service
public class GeminiServiceImpl implements GeminiService {

    private final RestClient restClient;
    private final String apiKey;
    private final String apiUrl;

    // 透過建構子注入 (Constructor Injection)
    public GeminiServiceImpl(
            RestClient.Builder restClientBuilder,
            @Value("${gemini.api.key}") String apiKey,
            @Value("${gemini.api.url}") String apiUrl) {
        this.restClient = restClientBuilder.build();
        this.apiKey = apiKey;
        this.apiUrl = apiUrl;
    }

    @Override
    public String generateText(String prompt) {
        // 透過 RestClient 發送 POST 請求並帶入由 Secret Manager 注入的金鑰
        GeminiResponse response = restClient.post()
                .uri(apiUrl + "?key=" + apiKey)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new GeminiRequest(prompt))
                .retrieve()
                .body(GeminiResponse.class);

        return response != null ? response.extractText() : "";
    }
}

【重點解析】

透過@Value("${gemini.api.key}"),Spring Boot 會自動幫我們完成金鑰的拉取。發送請求時只要一句.post().uri(...).body(...).retrieve(),就完成了原本繁瑣的 HTTP 傳輸。程式碼乾淨俐落!

動手實作 2:打造秒級驗證端點 (GeminiController)

很多端點通常透過 POST 傳遞複雜物件,但在開發與除錯初期,每次改動都要開 Postman 或組裝 Payload 很麻煩,因此可以下以下Prompt:

請在 Controller 加一個 GET /test 端點,讓我可以直接在瀏覽器打網址測試 Gemini API 連線正不正常。

AI 便很貼心地幫我們加上了預設 prompt 與簡明的 JSON 回傳結構:

@RestController
@RequestMapping("/gemini")
public class GeminiController {

    private final GeminiService geminiService;

    public GeminiController(GeminiService geminiService) {
        this.geminiService = geminiService;
    }

    /**
     * 開發與測試專用的 GET 端點(方便直接在瀏覽器網址列或終端機驗證連線)
     */
    @GetMapping("/test")
    public ResponseEntity<Map<String, Object>> testGemini(
            @RequestParam(defaultValue = "請用正體中文向使用者打個招呼!") String prompt) {
        String result = geminiService.generateText(prompt);
        return ResponseEntity.ok(Map.of(
                "status", "success",
                "prompt", prompt,
                "response", result
        ));
    }
}

【端點設計亮點】

  1. 預設 Prompt (defaultValue):即使不帶任何參數,也能直接回傳問候語,快速驗證「應用程式 → Secret Manager → Gemini API」整條呼叫鏈是否暢通。
  2. 免工具即時測試
  • 瀏覽器直接輸入http://localhost:8080/gemini/test?prompt=早安
curl -G "http://localhost:8080/gemini/test" \
  --data-urlencode "prompt=早安"

踩坑與避雷指南

  • Timeout 超時設定:AI 生成文字耗時較長(尤其是長篇大論時)。建議在建構 RestClient 時,將底層的 ReadTimeout 設定為 30 ~ 60 秒,避免請求因為等太久而自動中斷崩潰。
  • 模型名稱動態適配:雲端平台的 AI 模型疊代迅速,舊有型號會隨時間升級或轉移。將模型名稱設定解耦至 application.yml(例如指定 gemini-3.5-flash-lite),未來不論官方推展到什麼新版本,都能在不修改任何 Java 程式碼的前提下完成無痛切換。
  • 中文 URL 編碼陷阱:在測試 GET 端點並於 Query Parameter 帶入中文時,務必使用 --data-urlencode(或瀏覽器自動轉換),避免 URL 特殊字元導致 400 Bad Request。

今日總結與明日預告

今天我們釐清了計費層級,透過最簡單的 Prompt 與 AI 協同開發,利用 RestClient 與 Secret Manager 成功打通了後端連線,並實作了極速驗證的 /gemini/test 端點。但目前的對話是「單次(Stateless)」的,AI 講完就忘了我們剛才聊過什麼。

明天(Day 8),我們將進入實戰應用的關鍵環節:設計資料庫來儲存歷史對話,打造具備上下文記憶 (Context) 的 AI 助手!我們明天見!


上一篇
Day 6 | 怕踩破薄冰?那把冰層加厚!建立穩健的 RESTful API 專案
下一篇
Day 8 | 賦予 AI 記憶力:Spring Boot 實作多輪對話的上下文管理
系列文
單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言