iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
生成式 AI

Java AI 核心引擎:30 天從零打造可擴展的智慧 Agent 函式庫系列 第 3

Day 3:聲明式魔法:透過 AiServices 將 Java 介面轉換為 AI 服務

  • 分享至 

  • xImage
  •  

Java AI 核心引擎:30 天從零打造可擴展的智慧 Agent 函式庫
本篇是 IT 鐵人賽系列文章的第3天,將深入 LangChain4j 的 AiServices,用更優雅、更符合介面導向的方式進行 AI 開發。

今日目標

  • 理解 AiServices 的設計理念與優勢
  • 定義 Assistant 介面並實作系統提示詞
  • 建立 AiServiceConfig 配置類別手動建立 AI 服務
  • 比較 ChatLanguageModel 與 AiServices 的開發模式差異

AiServices 介紹

設計理念

在昨天我們直接使用 ChatLanguageModel 進行 AI 對話,這是一種命令式(Imperative)的開發方式。而 AiServices 提供了聲明式(Declarative)的開發模式。從實際開發經驗來看,兩者在開發體驗上有著本質的差異。

開發體驗差異

ChatLanguageModel(命令式開發)
使用 ChatLanguageModel 時,需要直接呼叫模型的 generate() 方法,並且手動管理所有的提示詞設定。當需要加入系統訊息時,必須在每次呼叫時手動拼接,例如:chatModel.generate("你是專業助理。" + userMessage)。這種方式雖然簡單直接,但隨著業務邏輯的複雜化,相關程式碼往往會分散在各個服務類別中。

AiServices(聲明式開發)
相對地,AiServices 能透過介面定義來描述 AI 服務的行為。透過 @SystemMessage 註解,框架會自動處理系統提示詞的設定,開發者只需要專注在業務邏輯的定義上。所有 AI 相關的行為都集中在介面定義中,符合物件導向的封裝原則。

程式碼組織與維護性

從維護角度來看,ChatLanguageModel 的使用方式容易產生重複程式碼,特別是系統提示詞的管理。當需要調整 AI 的角色設定時,可能需要修改多個地方。而 AiServices 將這些設定集中管理,修改時只需要更新介面的註解即可。

核心概念

AiServices 透過 Java 介面定義 AI 服務的行為,框架自動產生實作:

// 定義介面
public interface Assistant {
    String chat(String userMessage);
}

// 使用 AiServices.create() 建立介面的實作(透過動態代理)
Assistant assistant = AiServices.create(Assistant.class, chatModel);
// 注意:這只是建立實例,需要透過 @Bean 註解才能註冊到 Spring 容器

實作步驟

步驟 1:定義 Assistant 介面

src/main/java/org/example/aiagentdemo/service 目錄建立 Assistant.java

package org.example.aiagentdemo.service;

import dev.langchain4j.service.SystemMessage;

/**
 * AI 助理服務介面
 * 
 * 使用 LangChain4j 的 AiServices 功能,透過聲明式方式定義 AI 服務。
 * 此介面將由 LangChain4j AiServices 框架實現,提供更優雅的 AI 互動方式。
 */
public interface Assistant {
    
    /**
     * 與 AI 助理進行對話
     * 
     * @param userMessage 使用者輸入的訊息
     * @return AI 助理的回應
     */
    @SystemMessage("你是一個友善且專業的 AI 助理。請用繁體中文回應,並提供有用、準確的資訊。")
    String chat(String userMessage);
}

關鍵重點

  1. 純介面設計:無需手動編寫實作類別,透過 AiServices.create() 動態產生
  2. @SystemMessage 註解:定義 AI 的角色與行為指示
  3. 類型安全:明確的輸入輸出類型定義

步驟 2:建立 AiServiceConfig 配置類別

由於我們使用 LangChain4j 0.36.2 版本,需要手動建立 AiServices 實例。

src/main/java/org/example/aiagentdemo/config 目錄建立 AiServiceConfig.java

package org.example.aiagentdemo.config;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import org.example.aiagentdemo.service.Assistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * AI 服務配置類別
 * 
 * 負責建立和配置 LangChain4j AiServices 相關的 Bean。
 * LangChain4j 提供簡潔的 create() 方法來建立 AI 服務實例。
 */
@Configuration
public class AiServiceConfig {
    
    /**
     * 建立 Assistant Bean
     * 
     * 使用 AiServices.create() 方法建立 Assistant 介面的實現,
     * 這是官方推薦的簡潔方式,適用於基本的 AI 服務配置。
     * 
     * @param chatLanguageModel Spring 自動配置的 ChatLanguageModel Bean
     * @return Assistant 實例
     */
    @Bean
    public Assistant assistant(ChatLanguageModel chatLanguageModel) {
        return AiServices.create(Assistant.class, chatLanguageModel);
    }
}

API 方法說明

LangChain4j 提供兩種建立 AiServices 的方式:

1. 簡潔方式(推薦)

AiServices.create(Assistant.class, chatLanguageModel)

2. Builder 方式(進階用法)

AiServices.builder(Assistant.class)
    .chatLanguageModel(chatLanguageModel)
    .tools(customTools)           // 可選:加入工具
    .chatMemory(chatMemory)       // 可選:加入記憶
    .build()
  • Spring 整合:透過 @Bean 註解將 AiServices 註冊到 Spring 容器
  • 依賴注入:自動注入已配置的 ChatLanguageModel
  • 擴展性:Builder 模式適合需要更多配置的複雜場景

步驟 3:建立 AiServiceRunner 測試執行器

src/main/java/org/example/aiagentdemo/runner 目錄建立 AiServiceRunner.java

package org.example.aiagentdemo.runner;

import org.example.aiagentdemo.service.Assistant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
 * AI 服務執行器 - 示範 AiServices 聲明式 AI 服務使用方式
 * 
 * 使用 AiServices.create() 建立的 Assistant Bean,展示聲明式 AI 服務開發模式。
 * 相較於直接使用 ChatLanguageModel,AiServices 提供:
 * - 聲明式介面設計,符合 Spring Bean 管理模式
 * - 自動處理系統訊息與提示詞模板(@SystemMessage)
 * - 類型安全的介面設計,支援結構化輸出
 * - 更好的程式碼組織和維護性
 */
@Component
public class AiServiceRunner implements CommandLineRunner {
    
    private static final Logger logger = LoggerFactory.getLogger(AiServiceRunner.class);
    
    // 注入透過 AiServiceConfig 手動建立的 Assistant Bean
    @Autowired
    private Assistant assistant;
    
    @Override
    public void run(String... args) throws Exception {
        logger.info("========== 開始 AiServices 測試 ==========");
        
        // 測試 1:聲明式服務問候
        testServiceGreeting();
        
        // 測試 2:專業AI助理回應
        testProfessionalAssistant();
        
        // 測試 3:比較聲明式與直接呼叫的差異
        testServiceApproach();
        
        logger.info("========== AiServices 測試完成 ==========");
    }
    
    /**
     * 測試聲明式 AI 服務的基本對話
     */
    private void testServiceGreeting() {
        logger.info("\n--- 測試 1:AiService 聲明式問候 ---");
        String question = "請問你是誰?你有什麼功能?";
        logger.info("使用者: {}", question);
        
        String response = assistant.chat(question);
        logger.info("AI 助理回應: {}", response);
    }
    
    /**
     * 測試 AI 助理的專業回應能力
     */
    private void testProfessionalAssistant() {
        logger.info("\n--- 測試 2:專業 AI 助理回應 ---");
        String question = "請解釋什麼是 Spring Boot 的依賴注入,並舉一個簡單的例子。";
        logger.info("使用者: {}", question);
        
        String response = assistant.chat(question);
        logger.info("AI 助理回應: {}", response);
    }
    
    /**
     * 展示聲明式 AI 服務的優勢
     */
    private void testServiceApproach() {
        logger.info("\n--- 測試 3:聲明式服務優勢展示 ---");
        String question = "請用繁體中文說明 AiServices 相比直接使用 ChatLanguageModel 的優點。";
        logger.info("使用者: {}", question);
        
        String response = assistant.chat(question);
        logger.info("AI 助理回應: {}", response);
    }
}

執行與測試

編譯專案

# 清除並編譯專案
./mvnw clean compile

執行應用程式

./mvnw spring-boot:run

預期輸出結果

當應用程式啟動後,會看到類似以下的輸出:

========== 開始 AiServices 測試 ==========

--- 測試 1:AiService 聲明式問候 ---
使用者: 請問你是誰?你有什麼功能?
AI 助理回應: 我是一個友善且專業的 AI 助理。我能夠理解和回應使用者的問題、提供資訊和建議...

--- 測試 2:專業 AI 助理回應 ---
使用者: 請解釋什麼是 Spring Boot 的依賴注入,並舉一個簡單的例子。
AI 助理回應: Spring Boot 依賴注入是一種設計模式,用於管理類別之間的依存關係...

--- 測試 3:聲明式服務優勢展示 ---
使用者: 請用繁體中文說明 AiServices 相比直接使用 ChatLanguageModel 的優點。
AI 助理回應: AiServices 相比直接使用 ChatLanguageModel 有以下優點...

========== AiServices 測試完成 ==========

兩種開發模式比較

程式碼對比

ChatLanguageModel 方式(命令式)

@Autowired
private ChatLanguageModel chatModel;

public void doChat() {
    // 方法 1:簡單字串拼接(不推薦,易產生格式問題)
    String response = chatModel.generate("你是專業助理。" + userMessage);
    
    // 方法 2:使用 ChatMessage 物件(推薦做法)
    List<ChatMessage> messages = Arrays.asList(
        SystemMessage.from("你是專業助理。"),
        UserMessage.from(userMessage)
    );
    Response<AiMessage> response = chatModel.generate(messages);
    String result = response.content().text();
    
    // 需要引入的類別:
    // import dev.langchain4j.data.message.*;
    // import dev.langchain4j.model.output.Response;
}

AiServices 方式(聲明式)

@Autowired
private Assistant assistant;

public void doChat() {
    String response = assistant.chat(userMessage);
    // @SystemMessage 自動處理系統提示詞
}


總結

今日完成項目

  • 成功理解 AiServices 的設計理念與優勢
  • 定義並實作 Assistant 介面與系統提示詞
  • 建立 AiServiceConfig 手動配置 AI 服務 Bean
  • 完成聲明式 AI 服務的測試與驗證

關鍵開發重點

  1. 聲明式設計:AiServices 提供更優雅的介面導向開發方式
  2. 系統提示詞管理@SystemMessage 註解自動處理系統指示
  3. Spring 整合:透過配置類別將 AiServices 整合到 Spring 生態系
  4. 版本適配:不同版本的 API 差異與解決方案

設計模式思考

今天的實作展示了從命令式聲明式程式設計的轉變:

  • 命令式:告訴程式「如何做」(ChatLanguageModel)
  • 聲明式:告訴程式「做什麼」(AiServices)

這種轉變讓程式碼更加簡潔、可維護,並且更符合物件導向設計原則。

明日預告:Day 4 - 即時互動:用框架內建功能實現 SSE 串流打字機效果

明天將:

  • 實作 StreamingChatLanguageModel 的功能
  • 在 Assistant 介面中新增串流方法
  • 實現逐字輸出的打字機效果
  • 體驗更流暢的 AI 互動體驗

參考資料


感謝閱讀,明天繼續努力!

Day 3 完成 | 明天繼續 AI 開發之旅!


上一篇
Day 2:首次對話:ChatLanguageModel 的基本使用
系列文
Java AI 核心引擎:30 天從零打造可擴展的智慧 Agent 函式庫3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言