Key Findings
  • Spring AI 提供統一的抽象層,讓你用同一套程式碼切換 OpenAI、Anthropic、Google Gemini、Ollama 等不同 AI 供應商——就像 Spring Data 對資料庫做的事一樣
  • ChatClient(高階 Fluent API)是日常開發的首選,而 ChatModel(底層介面)適合需要精細控制的進階場景——90% 的情況用 ChatClient 就夠了
  • 透過三種訊息類型(SystemMessage、UserMessage、AssistantMessage)的組合,你可以精準控制 AI 的角色、行為與回應風格
  • 理解 Token 計量機制是控制 AI 使用成本的關鍵——輸出 Token 的價格通常是輸入的 2~4 倍

為什麼選擇 Spring AI?

在 AI 應用開發快速演進的當下,Java/Kotlin 開發者面臨一個核心挑戰:每個 AI 供應商都有自己的 SDK、自己的 API 格式、自己的錯誤處理方式。今天用 OpenAI,明天想換 Anthropic Claude,後天可能要整合本地的 Ollama——每次切換都意味著大量程式碼重寫。

Spring AI[1] 正是為了解決這個問題而生。它在你的應用程式和 AI 供應商之間建立了一層統一抽象,讓切換模型就像切換資料庫一樣簡單:

痛點沒有 Spring AI有 Spring AI
切換供應商重寫 HTTP 呼叫、序列化、錯誤處理改一行設定
企業整合自己處理 Spring Security、Transaction原生整合 Spring 生態系
功能擴展自己實作 RAG、Memory、Tool Calling內建 Advisor API 框架
測試Mock 複雜的 HTTP 層標準 Spring Test 模式

Spring AI 1.0 GA:從實驗到生產

2025 年發佈的 Spring AI 1.0 GA 標誌著這個框架正式進入生產級。以下是對開發者最重要的核心功能[3]

  • ChatClient — 統一的高階 Fluent API,用鏈式呼叫完成所有 AI 互動
  • Advisor API — 類似 Spring AOP 的擴展機制,在不修改核心程式碼的情況下添加日誌、快取、過濾等功能
  • Tool Calling — 讓 AI 模型呼叫你的 Java/Kotlin 方法(本系列 Lesson 4 將深入介紹)
  • RAG 支援 — 內建向量資料庫整合,讓 AI 存取企業專屬知識
  • 多模態支援 — 處理文字、圖片、語音等多種輸入輸出
  • 結構化輸出 — AI 回應直接轉換為 Java/Kotlin 物件,不用自己解析 JSON

架構總覽:三層 API 設計

在開始寫程式碼之前,先理解 Spring AI 的三層架構,這會幫助你在後續課程中做出正確的設計決策:

┌─────────────────────────────────────────────────┐
│            你的應用程式 (Application)              │
├─────────────────────────────────────────────────┤
│   ChatClient(高階 Fluent API)← 日常開發推薦     │
│   ├─ .prompt().system().user().call()            │
│   └─ 支援 Advisor 擴展                            │
├─────────────────────────────────────────────────┤
│   ChatModel(底層模型介面)← 需要精細控制時使用     │
│   ├─ .call(Prompt)                               │
│   └─ 手動組裝 Message                             │
├─────────────────────────────────────────────────┤
│   HTTP Client(自動處理序列化、重試、錯誤處理)     │
│   └─ OpenAI / Anthropic / Ollama / Azure ...     │
└─────────────────────────────────────────────────┘
設計原則: 優先使用最上層的 ChatClient。只有當你需要自訂 Prompt 組裝邏輯、建構框架底層、或進行效能微調時,才需要降到 ChatModel 層。

Step 1:引入 Spring AI 依賴

在 Kotlin Notebook 中,我們使用 @file:DependsOn 來引入 Maven 依賴。開啟課程專案的 Lesson1/Lesson1_Hello_Spring_AI.ipynb,第一個 Cell 負責引入所有必要的函式庫:

@file:DependsOn("org.springframework.ai:spring-ai-openai:1.0.0")
@file:DependsOn("org.springframework.ai:spring-ai-client-chat:1.0.0")
@file:DependsOn("org.slf4j:slf4j-simple:2.0.16")

println("✓ Spring AI 依賴引入成功")

這三個依賴分別負責:

  • spring-ai-openai — OpenAI 模型的連接器(包含 API 呼叫、序列化等實作)
  • spring-ai-client-chat — ChatClient 與 ChatModel 的核心抽象層
  • slf4j-simple — 日誌輸出(Spring AI 內部使用 SLF4J 記錄除錯資訊)

Step 2:載入 API Key 並初始化模型

使用 Lesson 0 設定好的環境變數來初始化 OpenAI 連線:

import org.springframework.ai.openai.OpenAiChatModel
import org.springframework.ai.openai.api.OpenAiApi
import org.springframework.ai.chat.client.ChatClient

// 從環境變數讀取 API Key
val apiKey = System.getenv("OPENAI_API_KEY")
    ?: error("請先設定 OPENAI_API_KEY 環境變數(參考 Lesson 0)")

// 建立 OpenAI API 連線
val openAiApi = OpenAiApi.builder()
    .apiKey(apiKey)
    .build()

// 建立 ChatModel(底層介面)
val chatModel = OpenAiChatModel.builder()
    .openAiApi(openAiApi)
    .build()

// 建立 ChatClient(高階 Fluent API)
val chatClient = ChatClient.builder(chatModel).build()

println("✓ Spring AI 初始化完成,已連線 OpenAI")

注意初始化的層次關係:API Key → OpenAiApi → ChatModel → ChatClient。每一層都封裝了下一層的複雜度,讓你在最上層用最簡潔的方式操作。

Step 3:用 ChatModel 發出第一次 AI 呼叫

先從底層的 ChatModel 開始,理解最基礎的呼叫方式[2]

// 最簡單的呼叫方式:直接傳入字串
val response = chatModel.call("用一句話介紹什麼是 Spring AI")

println(response)

預期輸出(每次呼叫結果會略有不同):

Spring AI 是一個結合 Spring 框架的強大功能與人工智慧技術的開發工具集,
旨在簡化和加速 AI 應用的開發過程。

恭喜!你已經成功用 Spring AI 完成了第一次 AI 模型呼叫。但這只是最基礎的用法——接下來我們要學習如何透過訊息類型來精準控制 AI 的行為。

Step 4:理解三種訊息類型

AI 對話是由不同角色的訊息組成的。Spring AI 提供三種訊息類型,對應 OpenAI Chat API 的三種 role[2]

訊息類型角色用途範例
SystemMessage系統設定 AI 的角色與行為規則「你是一位 Spring 框架專家」
UserMessage使用者使用者的提問或指令「請問如何使用 Spring AI?」
AssistantMessageAI 助手AI 的回應(多輪對話時使用)(AI 的回覆文字)

使用 ChatModel 的 Prompt 物件來組合這些訊息:

import org.springframework.ai.chat.prompt.Prompt
import org.springframework.ai.chat.messages.SystemMessage
import org.springframework.ai.chat.messages.UserMessage

// 組裝多訊息 Prompt
val prompt = Prompt(listOf(
    SystemMessage("你是一位資深 Spring 框架專家,擅長用簡潔的方式解釋複雜概念。回答限制在 100 字以內。"),
    UserMessage("ChatClient 和 ChatModel 有什麼差別?")
))

val result = chatModel.call(prompt)
println(result.result.output.text)

預期輸出:

ChatModel 是底層介面,需要手動組裝 Prompt 和解析回應;
ChatClient 是高階 Fluent API,提供鏈式呼叫,自動處理 Prompt 組裝,
並支援 Advisor 擴展。日常開發建議使用 ChatClient,
只有需要精細控制時才用 ChatModel。
觀察: SystemMessage 的威力在於它能「設定舞台」——告訴 AI 扮演什麼角色、遵守什麼規則。這就是 Prompt Engineering 的核心概念,Lesson 3 會深入探討。

Step 5:用 ChatClient 體驗 Fluent API

同樣的需求,改用 ChatClient 的 Fluent API 來實作——注意程式碼的簡潔度:

val answer = chatClient
    .prompt()
    .system("你是一位親切的程式設計老師,用簡單易懂的比喻解釋技術概念。")
    .user("什麼是 RAG?")
    .call()
    .content() ?: "(無回應)"

println(answer)

預期輸出:

想像你有一個非常聰明的朋友,但他的知識停留在幾年前。
RAG(Retrieval-Augmented Generation,檢索增強生成)就像是在他回答問題之前,
先幫他翻閱最新的參考資料。這樣他的回答就不只靠記憶,
還能引用最新、最準確的資訊。在技術上,就是先從資料庫搜尋相關文件,
再把這些文件一起交給 AI 模型,讓它生成更精確的回答。

對比一下兩種 API 的使用方式:

特性ChatModel(底層)ChatClient(高階)
呼叫方式手動建立 Prompt 物件Fluent 鏈式呼叫
訊息組裝手動 listOf(SystemMessage, UserMessage).system().user()
回應取得result.result.output.text.content()
Advisor 支援不支援支援(日誌、快取、RAG 等)
推薦場景框架開發、精細控制日常開發(90% 的場景)

Step 6:實作練習——切換 AI 角色

SystemMessage 的真正威力在於你可以讓同一個 AI 模型扮演完全不同的角色。讓我們用一個有趣的實驗來體驗:

// 角色 1:程式設計師
val programmer = chatClient
    .prompt()
    .system("你是一位嚴謹的資深程式設計師,所有回答都要用程式設計的概念來比喻。限 50 字。")
    .user("描述一下你的午餐")
    .call()
    .content() ?: "(無回應)"

println("🧑‍💻 程式設計師:")
println(programmer)
println()

// 角色 2:美食評論家
val foodCritic = chatClient
    .prompt()
    .system("你是一位米其林等級的美食評論家,用華麗的詞藻描述一切。限 50 字。")
    .user("描述一下你的午餐")
    .call()
    .content() ?: "(無回應)"

println("🍽️ 美食評論家:")
println(foodCritic)

可能的輸出:

🧑‍💻 程式設計師:
午餐就像一次函式呼叫——輸入食材,經過加熱處理的 pipeline,
輸出一份編譯完成的位元比特披薩。沒有 bug,味道穩定。

🍽️ 美食評論家:
金黃酥脆的外衣下,包裹著絲綢般的馬鈴薯泥,
佐以松露油的芬芳——這不僅是午餐,更是一場味蕾的交響樂。

同樣的問題,不同的 SystemMessage 就能得到風格截然不同的回答。這就是為什麼 Prompt Engineering 如此重要——你不只在寫程式碼,更在設計 AI 的「人格」

Step 7:查看完整回應資訊與 Token 用量

在正式專案中,你需要追蹤 Token 用量來控制成本[4]。ChatClient 的 .chatResponse() 方法可以取得完整的回應資訊:

val chatResponse = chatClient
    .prompt()
    .system("你是一位 Spring AI 專家。")
    .user("Spring AI 支援哪些模型供應商?簡要列出。")
    .call()
    .chatResponse()

// 取得回應文字
println("回應內容:")
println(chatResponse?.result?.output?.text)
println()

// 取得 Token 用量
val usage = chatResponse?.metadata?.usage
println("═══ Token 用量統計 ═══")
println("輸入 Token(Prompt): ${usage?.promptTokens}")
println("輸出 Token(Completion): ${usage?.completionTokens}")
println("總計 Token: ${usage?.totalTokens}")

預期輸出:

回應內容:
Spring AI 支援以下模型供應商:OpenAI(GPT 系列)、Anthropic(Claude 系列)、
Google(Gemini)、Azure OpenAI、Ollama(本地模型)、Amazon Bedrock、
Mistral AI、HuggingFace 等。

═══ Token 用量統計 ═══
輸入 Token(Prompt): 35
輸出 Token(Completion): 68
總計 Token: 103

理解 Token 的幾個關鍵要點:

  • Token ≠ 字元 — 中文通常 1 個字 = 1~2 個 Token,英文 1 個詞 ≈ 1 Token
  • 成本差異 — 輸出 Token 的價格通常是輸入的 2~4 倍,因此限制輸出長度是控制成本的有效手段
  • 上下文限制 — 每個模型有 Token 上限(如 GPT-4o 為 128K),超過會被截斷

ChatClient vs ChatModel:什麼時候該用哪一個?

學完兩種 API 後,給你一個簡單的決策框架:

場景推薦 API原因
日常 AI 功能開發ChatClient簡潔、支援 Advisor、覆蓋 90% 場景
聊天機器人 / RAG 應用ChatClientAdvisor API 輕鬆添加 Memory、RAG
自訂 Prompt 模板引擎ChatModel需要完全控制 Prompt 組裝邏輯
建構 AI 框架底層ChatModel需要存取原始 Request/Response
效能基準測試ChatModel減少抽象層開銷,取得精確數據
經驗法則: 如果你猶豫不決,就用 ChatClient。等到你明確知道 ChatClient 無法滿足需求時,再考慮 ChatModel。

本課重點回顧

在這堂課中,我們完成了以下學習目標:

  1. 理解 Spring AI 的定位 — 它是 AI 供應商的統一抽象層,類似 Spring Data 對資料庫的角色
  2. 掌握三層 API 架構 — ChatClient(高階)→ ChatModel(底層)→ HTTP Client(傳輸層)
  3. 完成第一次 AI 呼叫 — 分別使用 ChatModel 和 ChatClient 呼叫 OpenAI GPT 模型
  4. 學會控制 AI 行為 — 透過 SystemMessage、UserMessage 的組合精準控制回應風格
  5. 理解 Token 計量機制 — 掌握成本追蹤的基礎,為後續大量 API 呼叫做準備

下一步:Lesson 2 — 串流輸出與前端整合

目前我們的 AI 呼叫是「等待完成才顯示」——使用者要等幾秒鐘才能看到回應,體驗不佳。在下一堂課中,我們將學習:

  • Streaming API — 讓 AI 回應像打字一樣逐字顯示
  • Server-Sent Events(SSE) — 在 Web 應用中實現即時串流
  • 打字機效果 — 打造像 ChatGPT 一樣流暢的使用者體驗

掌握基礎,繼續前進!

完成第一次 AI 呼叫後,下一步學習串流輸出——打造像 ChatGPT 一樣的即時回應體驗。

前往 Lesson 2:串流輸出與 ChatModel →