iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
生成式 AI

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

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

  • 分享至 

  • xImage
  •  

Java AI 核心引擎:30 天從零打造可擴展的智慧 Agent 函式庫
本篇是 IT 鐵人賽系列文章的第四天,我們將為 AiServices 加上串流能力,實現即時的打字機效果,提升使用者互動體驗。

今日目標

  • 理解 StreamingChatLanguageModel 的概念與應用
  • 擴展 Assistant 介面支援 TokenStream 串流方法
  • 修改 AiServiceConfig 同時支援同步與串流兩種模式
  • 建立 StreamingAiServiceRunner 展示打字機效果
  • 掌握 TokenStream API 的 onNext、onComplete、onError 回調處理

為什麼需要串流模式?

昨天我們體驗了 ChatLanguageModel 的同步對話方式,每次詢問 AI 都要等待完整的回應生成後才能看到結果。當 AI 思考複雜問題時,這種等待過程可能長達數十秒,使用者完全不知道 AI 是否還在處理,還是已經「當機」了。

想像一下現實生活中的對話場景:當朋友在回答問題時,我們能看到對方的表情變化、思考的過程,這種即時回饋讓對話變得自然流暢。串流模式就是要把這種體驗帶到 AI 互動中。

串流模式的實際體驗

使用 StreamingChatLanguageModel 時,AI 的回應會像打字一樣逐字出現。這不僅讓使用者知道 AI 正在工作,更重要的是提供了「思考過程」的視覺化體驗。特別是當 AI 在生成程式碼或長篇說明時,這種即時回饋能大幅改善使用者體驗。

TokenStream 的設計哲學

LangChain4j 的 TokenStream 採用回調模式設計,這是處理串流資料的經典方式:

TokenStream tokenStream = assistant.chatInStream("問題");
tokenStream
    .onNext(token -> {
        // 每個新字符出現時的處理
        System.out.print(token);
    })
    .onComplete(response -> {
        // 完整回應生成後的處理
        System.out.println("回應完成!");
    })
    .onError(error -> {
        // 錯誤處理
        System.err.println("發生錯誤: " + error.getMessage());
    })
    .start();

這種設計讓我們能夠在串流的不同階段執行不同的邏輯,既靈活又易於理解。


開始實作串流功能

擴展 Assistant 介面

既然要添加串流功能,最直接的做法就是在 Assistant 介面中新增一個串流版本的對話方法。在設計時我特別注意要保持向後相容性,畢竟昨天的同步版本 chat() 方法還是很實用的。

package org.example.aiagentdemo.service;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.TokenStream;

/**
 * AI 助理服務介面
 * 
 * 使用 LangChain4j 的 AiServices 功能,透過聲明式方式定義 AI 服務。
 * 此介面將由 LangChain4j AiServices 框架實現,提供更優雅的 AI 互動方式。
 */
public interface Assistant {
    
    /**
     * 與 AI 助理進行對話
     * 
     * @param userMessage 使用者輸入的訊息
     * @return AI 助理的回應
     */
    @SystemMessage("你是一個友善且專業的 AI 助理。請用繁體中文回應,並提供有用、準確的資訊。")
    String chat(String userMessage);
    
    /**
     * 與 AI 助理進行串流對話
     * 
     * 使用 TokenStream 提供即時的打字機效果,讓使用者能夠看到 AI 回應的逐字輸出。
     * 相較於同步的 chat() 方法,此方法提供更好的使用者互動體驗。
     * 
     * @param userMessage 使用者輸入的訊息
     * @return TokenStream 物件,可透過回調函式處理逐字輸出
     */
    @SystemMessage("你是一個友善且專業的 AI 助理。請用繁體中文回應,並提供有用、準確的資訊。")
    TokenStream chatInStream(String userMessage);
}

關鍵設計考量

  1. 向後相容性:保留原有的 chat() 方法,確保既有程式碼不受影響
  2. 一致的 AI 行為:兩個方法使用相同的 @SystemMessage,確保回應風格一致
  3. 清晰的方法命名chatInStream() 明確表達這是串流版本的對話方法

升級配置,讓 AiService 同時支援兩種模式

既然介面已經定義好了,接下來就要讓我們的配置類別支援這兩種不同的模型。這裡有個重要的觀念:我們需要同時注入 ChatLanguageModelStreamingChatLanguageModel 兩個 Bean,然後透過 AiServices.builder() 的方式來建立服務。

package org.example.aiagentdemo.config;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
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。
 * 使用 AiServices.builder() 模式同時支援同步和串流兩種 AI 服務。
 * Spring Boot 自動配置提供 ChatLanguageModel 和 StreamingChatLanguageModel Bean。
 */
@Configuration
public class AiServiceConfig {
    
    /**
     * 建立 Assistant Bean
     * 
     * 使用 AiServices.builder() 方法建立 Assistant 介面的實現,
     * 同時支援同步 (ChatLanguageModel) 和串流 (StreamingChatLanguageModel) 兩種 AI 服務。
     * Spring Boot 自動配置提供所需的模型 Bean。
     * 
     * @param chatLanguageModel Spring 自動配置的 ChatLanguageModel Bean
     * @param streamingChatLanguageModel Spring 自動配置的 StreamingChatLanguageModel Bean
     * @return Assistant 實例,支援同步和串流方法
     */
    @Bean
    public Assistant assistant(ChatLanguageModel chatLanguageModel, 
                              StreamingChatLanguageModel streamingChatLanguageModel) {
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(chatLanguageModel)
                .streamingChatLanguageModel(streamingChatLanguageModel)
                .build();
    }
}

API 升級小插曲

還記得昨天我們使用的 AiServices.create() 嗎?那是最簡潔的方式,適合單純的同步對話。但現在我們需要同時支援兩種模型,就得升級到 builder() 模式了:

昨天的簡潔版本

return AiServices.create(Assistant.class, chatLanguageModel);

今天的進階版本

return AiServices.builder(Assistant.class)
        .chatLanguageModel(chatLanguageModel)
        .streamingChatLanguageModel(streamingChatLanguageModel)
        .build();

這個變化其實很有意思。隨著功能越來越豐富,簡潔的 create() 方法就顯得力不從心了。Builder 模式雖然稍微囉嗦一點,但它為未來的擴展(比如加入記憶、工具等功能)預留了空間。

重要配置:讓 Spring Boot 認識串流模型

這裡有個關鍵點需要特別注意!Spring Boot 雖然會自動配置 ChatLanguageModel,但對於 StreamingChatLanguageModel 就沒那麼聰明了。如果我們不明確告訴它該怎麼建立這個 Bean,程式就會在啟動時報錯。

解決方法很簡單,在 application.properties 中為串流模型加上專用的配置:

spring.application.name=ai-agent-demo

# Ollama Configuration
langchain4j.ollama.chat-model.base-url=http://localhost:11434
langchain4j.ollama.chat-model.model-name=llama3.1:8b-instruct-q4_K_M
langchain4j.ollama.chat-model.temperature=0.7
langchain4j.ollama.chat-model.timeout=PT5M

# Ollama Streaming Configuration
langchain4j.ollama.streaming-chat-model.base-url=http://localhost:11434
langchain4j.ollama.streaming-chat-model.model-name=llama3.1:8b-instruct-q4_K_M
langchain4j.ollama.streaming-chat-model.temperature=0.7
langchain4j.ollama.streaming-chat-model.timeout=PT5M

# 日誌等級設定 - 顯示詳細的 AI 互動日誌
logging.level.org.example.aiagentdemo=INFO
logging.level.dev.langchain4j=DEBUG

踩坑心得分享

在實作過程中發現了一個有趣的現象:Spring Boot 對於同步模型的自動配置非常完善,但串流模型就需要我們手動指定配置。這是因為串流功能相對新穎,框架還沒有達到完全的「零配置」程度。

另一個重要觀念是,兩個模型的參數設定必須保持一致。畢竟它們背後其實是同一個 Ollama 服務和同一個語言模型,只是互動方式不同而已。如果參數不一致,可能會導致同步和串流模式的回應風格出現差異。

精彩的展示時刻:打字機效果實作

現在到了最讓人期待的部分!我們要建立一個專門的執行器,展示串流功能的各種可能性。這個實作會比昨天的例子更豐富,包含三個不同的測試場景,每個都能展現 TokenStream 的不同面向。

package org.example.aiagentdemo.runner;

import org.example.aiagentdemo.service.Assistant;
import dev.langchain4j.service.TokenStream;
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;

import java.util.concurrent.CompletableFuture;

/**
 * 串流 AI 服務執行器 - 展示 TokenStream 打字機效果
 * 
 * 使用 TokenStream 提供即時的串流對話體驗,展示逐字輸出的打字機效果。
 * 相較於同步的 chat() 方法,TokenStream 提供:
 * - 即時回應體驗,使用者可以看到 AI 思考過程
 * - onNext 回調處理每個 token 的輸出
 * - onComplete 回調處理完整回應
 * - onError 回調處理異常情況
 */
@Component
public class StreamingAiServiceRunner implements CommandLineRunner {
    
    private static final Logger logger = LoggerFactory.getLogger(StreamingAiServiceRunner.class);
    
    // 注入支援串流功能的 Assistant Bean
    @Autowired
    private Assistant assistant;
    
    @Override
    public void run(String... args) throws Exception {
        logger.info("========== 開始串流 AI 服務測試 ==========");
        
        // 測試 1:基本串流對話
        testBasicStreaming();
        
        // 等待一段時間再進行下一個測試
        Thread.sleep(2000);
        
        // 測試 2:打字機效果展示
        testTypingEffect();
        
        // 等待一段時間再進行下一個測試
        Thread.sleep(2000);
        
        // 測試 3:串流錯誤處理
        testStreamingErrorHandling();
        
        logger.info("========== 串流 AI 服務測試完成 ==========");
    }
    
    /**
     * 測試基本串流對話功能
     */
    private void testBasicStreaming() throws InterruptedException {
        logger.info("\n--- 測試 1:基本串流對話 ---");
        String question = "請簡單自我介紹,大約 30 字以內。";
        logger.info("使用者: {}", question);
        logger.info("AI 助理回應 (串流模式):");
        
        CompletableFuture<Void> future = new CompletableFuture<>();
        
        TokenStream tokenStream = assistant.chatInStream(question);
        tokenStream
            .onNext(token -> System.out.print(token))
            .onComplete(response -> {
                System.out.println(); // 換行
                String responseText = response.content().text();
                logger.info("串流完成,完整回應長度: {} 字符", responseText.length());
                future.complete(null);
            })
            .onError(error -> {
                System.err.println("\n串流發生錯誤: " + error.getMessage());
                future.completeExceptionally(error);
            })
            .start();
        
        // 等待串流完成
        future.join();
    }
    
    /**
     * 測試打字機效果 - 添加延遲模擬真實打字
     */
    private void testTypingEffect() throws InterruptedException {
        logger.info("\n--- 測試 2:打字機效果展示 ---");
        String question = "請用繁體中文解釋什麼是 Spring Boot,限 50 字以內。";
        logger.info("使用者: {}", question);
        logger.info("AI 助理回應 (打字機效果):");
        
        CompletableFuture<Void> future = new CompletableFuture<>();
        
        TokenStream tokenStream = assistant.chatInStream(question);
        tokenStream
            .onNext(token -> {
                try {
                    System.out.print(token);
                    System.out.flush(); // 強制刷新輸出緩衝區
                    // 添加小延遲模擬打字機效果
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            })
            .onComplete(response -> {
                System.out.println(); // 換行
                String responseText = response.content().text();
                logger.info("打字機效果完成!總共輸出: {} 字符", responseText.length());
                future.complete(null);
            })
            .onError(error -> {
                System.err.println("\n打字機效果發生錯誤: " + error.getMessage());
                error.printStackTrace();
                future.completeExceptionally(error);
            })
            .start();
        
        // 等待打字機效果完成
        future.join();
    }
    
    /**
     * 測試串流錯誤處理機制
     */
    private void testStreamingErrorHandling() throws InterruptedException {
        logger.info("\n--- 測試 3:串流錯誤處理 ---");
        String question = "請告訴我關於人工智慧的發展歷史,用簡潔的方式說明。";
        logger.info("使用者: {}", question);
        logger.info("AI 助理回應 (含錯誤處理):");
        
        CompletableFuture<Void> future = new CompletableFuture<>();
        
        try {
            TokenStream tokenStream = assistant.chatInStream(question);
            tokenStream
                .onNext(token -> {
                    System.out.print(token);
                    System.out.flush();
                })
                .onComplete(response -> {
                    System.out.println(); // 換行
                    logger.info("串流成功完成,錯誤處理測試通過");
                    future.complete(null);
                })
                .onError(error -> {
                    System.err.println("\n捕獲到串流錯誤: " + error.getMessage());
                    logger.error("TokenStream 錯誤處理測試:", error);
                    // 即使發生錯誤也標記完成,以便程式繼續執行
                    future.complete(null);
                })
                .start();
            
        } catch (Exception e) {
            logger.error("啟動串流時發生異常:", e);
            future.complete(null);
        }
        
        // 等待測試完成
        future.join();
    }
}

深入理解 TokenStream API

回調模式的智慧設計

TokenStream 的設計真的很巧妙,它採用了回調函式的設計模式。這種設計在處理串流資料時特別有效,因為我們永遠不知道下一個 token 什麼時候會到達,也不知道整個串流什麼時候會結束。

TokenStream tokenStream = assistant.chatInStream(userMessage);
tokenStream
    .onNext(token -> {
        // 處理每個 token
        System.out.print(token);
        System.out.flush();
    })
    .onComplete(response -> {
        // 處理完整回應
        String fullText = response.content().text();
        System.out.println("\n回應完成: " + fullText.length() + " 字符");
    })
    .onError(error -> {
        // 處理錯誤情況
        System.err.println("串流錯誤: " + error.getMessage());
    })
    .start(); // 啟動串流

異步處理的精髓

TokenStream 有一個很重要的特性:它是非阻塞的。這意味著當我們呼叫 .start() 後,主線程會立即繼續執行,不會乾等著串流結束。這種設計對於需要處理其他任務的應用程式來說非常友善。

但有時候我們確實需要等待串流完成,比如在我們的測試程式中。這時候就可以搭配 CompletableFuture 來達到同步等待的效果:

CompletableFuture<Void> future = new CompletableFuture<>();
tokenStream
    .onComplete(response -> future.complete(null))
    .onError(error -> future.completeExceptionally(error))
    .start();

future.join(); // 等待串流完成

打字機效果的實作秘訣

要做出逼真的打字機效果,其實有幾個小技巧。最重要的是理解輸出緩衝區的概念:當我們使用 System.out.print() 時,內容不一定會立即顯示在螢幕上,可能會暫存在緩衝區中。這時候 System.out.flush() 就派上用場了,它能強制把緩衝區的內容立即輸出。

另一個關鍵是延遲時間的掌握。太快的話就失去了打字機的感覺,太慢又會讓使用者不耐煩。經過實際測試,30ms 的延遲是個不錯的平衡點,既能呈現逐字輸出的效果,又不會讓人覺得太慢。

最後要注意執行緒安全的問題,因為我們在回調函式中使用了 Thread.sleep(),必須正確處理可能的中斷異常。


實際執行的精彩時刻

親眼見證打字機效果

執行我們的應用程式後,真的很有成就感!看著 AI 的回應一個字一個字地出現,就像真的有人在對面打字一樣。以下是實際的執行畫面:

========== 開始串流 AI 服務測試 ==========
--- 測試 1:基本串流對話 ---
使用者: 請簡單自我介紹,大約 30 字以內。
AI 助理回應 (串流模式):您好!我是 LLaMA,中文助理,我是一個友善且專業的 AI 助手,致力於為您提供快速、準確和有用的資訊和支持。歡迎使用我的服務!
串流完成,完整回應長度: 65 字符
--- 測試 2:打字機效果展示 ---
使用者: 請用繁體中文解釋什麼是 Spring Boot,限 50 字以內。
AI 助理回應 (打字機效果):Spring Boot 是一種基於 Spring Framework 的開發框架,簡化了 Spring 架構的部署和配置過程。它提供了一組預設設定、自動化配置和嵌入式容器等功能,使得開發者可以快速、輕松地建立雲端應用程式或微服務。
打字機效果完成!總共輸出: 115 字符
--- 測試 3:串流錯誤處理 ---
使用者: 請告訴我關於人工智慧的發展歷史,用簡潔的方式說明。
AI 助理回應 (含錯誤處理):人工智慧(Artificial Intelligence, AI)的發展歷史可以追溯到20世紀50年代。在這裡,我會給你一個簡要的概述:....略
串流成功完成,錯誤處理測試通過
========== 串流 AI 服務測試完成 ==========

執行結果觀察心得

從測試結果中能觀察到一些很有意思的現象。首先,串流的回調函式確實是在獨立的線程中執行的,從日誌的線程名稱( main 和 lhost )就能看出來。這證實了我們之前說的非阻塞特性。

其次,每個 token 的即時顯示真的讓整個互動變得更加生動。特別是在處理較長回應時,這種差異更加明顯。使用者不再是乾等著一個完整的回應突然出現,而是能夠看到 AI 的「思考過程」。

打字機效果的 30ms 延遲經過實際測試,確實提供了很自然的視覺體驗。太快會失去效果,太慢又會讓人不耐煩,這個數值是個不錯的平衡點。

最重要的是,我們的錯誤處理機制運作良好,即使在網路不穩定或其他異常情況下,程式也能正常繼續執行。


兩種模式的實際比較

使用者體驗的天壤之別

經過這幾天的實際使用,我深刻體會到了兩種模式的差異。同步模式就像是傳統的問答方式:你問一個問題,然後乾等著完整的回答出現。如果 AI 在處理複雜問題,你可能會等待好幾秒,完全不知道它是在思考還是卡住了。

相對地,串流模式就像是真實的對話。你能看到對方一邊思考一邊回答,這種即時回饋讓整個互動變得生動許多。特別是當 AI 在生成程式碼或長篇說明時,這種差異更是明顯。你不再是坐著乾等,而是能夠跟著 AI 的「思考節奏」一起前進。

程式碼複雜度的權衡

從程式碼的角度來看,兩種模式各有優缺點:

同步模式 - 簡潔明瞭

String response = assistant.chat("問題");
System.out.println(response);

串流模式 - 功能豐富但稍複雜

assistant.chatInStream("問題")
    .onNext(token -> System.out.print(token))
    .onComplete(response -> System.out.println())
    .onError(error -> System.err.println("錯誤: " + error))
    .start();

從程式碼行數來看,串流模式確實複雜一些,但這個複雜度帶來的是更精細的控制能力。我們可以在每個 token 到達時做任何我們想要的處理,也可以優雅地處理各種異常情況。

場景選擇的實務建議

經過實際使用,我認為兩種模式都有其適用的場景:

  • 同步模式比較適合:簡單的問答、批次處理、單元測試驗證這類不需要即時回饋的情況
  • 串流模式更適合:互動式對話、長文本生成、即時客服、輔助系統等需要良好使用者體驗的場景

效能觀察與心得

有趣的是,從實際測試來看,兩種模式的總處理時間其實差不多。串流模式的優勢主要體現在使用者的感知體驗上,而不是實際的處理速度。

這讓我聯想到網頁載入的概念:同樣是載入 10 秒,如果有進度條的話使用者會覺得比較能接受,而如果是純粹的白畫面就會讓人焦慮。串流模式就是提供了這種「進度條」的效果。


實作過程中踩過的坑

第一個坑:Bean 找不到的懊惱

在實作過程中最讓人頭痛的就是這個錯誤:

No qualifying bean of type 'StreamingChatLanguageModel' available

當時真的很困惑,明明 ChatLanguageModel 都可以自動注入,為什麼 StreamingChatLanguageModel 就不行呢?後來才發現,Spring Boot 的自動配置對於串流模型還沒有那麼智能,需要我們手動在 application.properties 中明確指定:

langchain4j.ollama.streaming-chat-model.base-url=http://localhost:11434
langchain4j.ollama.streaming-chat-model.model-name=llama3.1:8b-instruct-q4_K_M
langchain4j.ollama.streaming-chat-model.temperature=0.7

這個小細節花了我不少時間才搞懂,希望能幫大家省點時間。

第二個坑:Response 物件的誤解

另一個讓我卡住的地方是這個編譯錯誤:

cannot find symbol: method length()

我原本以為 onComplete 回調會直接給我一個字串,所以寫成了 response.length()。後來才發現,它實際上傳入的是一個 Response<AiMessage> 物件,需要透過 .content().text() 才能取得實際的文字內容:

.onComplete(response -> {
    String responseText = response.content().text();
    System.out.println("長度: " + responseText.length());
})

這種小錯誤雖然編譯器會提醒,但理解背後的邏輯能讓我們寫出更正確的程式碼。

第三個坑:打字機效果調校

一開始實作打字機效果時,發現字符輸出得太快,完全沒有打字的感覺。經過幾次調整後發現需要注意這幾個要點:

  1. 加入延遲Thread.sleep(30) 是個不錯的起點
  2. 強制輸出System.out.flush() 確保每個字符立即顯示
  3. 找到平衡:延遲太短沒效果,太長又會讓人不耐煩

這個數值真的需要實際測試才能找到最佳平衡點。

第四個坑:多 Runner 的輸出衝突

當專案中有多個 CommandLineRunner 時,它們會同時執行,導致輸出內容混在一起,很難看清楚個別的效果。解決方法很簡單,暫時把不需要的 Runner 註解掉:

//@Component  // 暫時註解,專注測試串流功能
public class AiServiceRunner implements CommandLineRunner {
    // ...
}

這樣就能專心觀察我們想要測試的功能了。


設計思考與架構考量

向後相容性的重要性

在設計這個功能時,我特別重視向後相容性。畢竟昨天的同步版本還是很實用的,不應該因為加入新功能而破壞既有的程式碼。所以我們採取了這樣的策略:

  1. 保留既有方法:原有的 chat() 方法完全不變
  2. 新增串流方法:透過 chatInStream() 提供新功能
  3. 配置同步:兩種模型並存,各取所需

這樣的設計讓使用者可以根據實際需求選擇適合的方式,不會有被強迫升級的困擾。

為未來預留空間

create() 升級到 builder() 模式,其實是為未來的擴展做準備。現在我們可能只需要兩種聊天模型,但將來可能會需要加入更多功能:

return AiServices.builder(Assistant.class)
        .chatLanguageModel(chatLanguageModel)
        .streamingChatLanguageModel(streamingChatLanguageModel)
        // 未來的可能性
        .tools(customTools)           // 工具呼叫
        .chatMemory(chatMemory)       // 對話記憶
        .build();

Builder 模式的好處就是讓這種擴展變得很自然。

多層錯誤處理的考量

在錯誤處理方面,我們建立了多層的防護機制:

  1. TokenStream 層:透過 onError 回調處理串流過程中的問題
  2. 應用層:用 try-catch 處理初始化時的異常
  3. 同步層:透過 CompletableFuture 統一處理異步操作的結果

這樣的設計確保了即使在各種意外情況下,程式也能優雅地處理錯誤,不會讓使用者看到莫名其妙的當機。


總結

今日完成項目

  • 成功理解並實作 StreamingChatLanguageModel 串流功能
  • 擴展 Assistant 介面支援 TokenStream,保持向後相容性
  • 修改 AiServiceConfig 使用 builder 模式支援雙模型
  • 建立完整的 StreamingAiServiceRunner 展示三種測試場景
  • 實現逼真的打字機效果,提升使用者互動體驗

今天的重要收穫

回顧今天的實作過程,最重要的收穫包括:

  1. 串流思維的轉變:從「等待完整結果」轉為「處理逐步到達的數據」
  2. 回調模式的實際應用:透過 TokenStream 體驗異步程式設計的魅力
  3. 使用者體驗的重要性:技術不只是功能的堆疊,更要考慮實際的使用感受
  4. 向後相容的設計智慧:新功能的加入不應該破壞既有的程式碼
  5. 配置細節的重要性:看似簡單的功能,背後往往有很多配置的眉角

從同步到串流的思維轉變

今天最大的感想是體會到了程式設計思維的轉變。同步模式讓我們習慣了「一問一答」的簡單邏輯,但串流模式開啟了「持續對話」的可能性。

這種轉變不只是技術層面的,更是使用者體驗層面的突破。TokenStream 提供的細粒度控制讓我們能夠打造更自然、更人性化的 AI 互動體驗。我覺得這正是優秀軟體的特質:不只是功能強大,更要讓人用起來舒服。

明日預告:Day 5 - 精準調校:掌握 temperature 等模型參數控制

明天我們將:

  • 深入理解 temperature、topP 等模型參數的意義與影響
  • 實作在配置檔案和程式碼中動態調整參數
  • 實驗不同參數對模型輸出風格的具體影響
  • 建立參數調校的最佳實踐指南

參考資料


感謝大家的閱讀,我們明天見!

Day 4 完成 | 明天繼續我們的 AI 冒險之旅!


上一篇
Day 3:聲明式魔法:透過 AiServices 將 Java 介面轉換為 AI 服務
系列文
Java AI 核心引擎:30 天從零打造可擴展的智慧 Agent 函式庫4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言