iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
自我挑戰組

與 AI 共舞:打造更高效的日常系列 第 21

Spring AI 驅動的 ETL 流程:構建文本上傳工作流並實現 RAG

  • 分享至 

  • xImage
  •  

前言

今天又是在 deadline 前完成任務的一天。在處理這種實作比較多的題目時,確實需要投入更多時間和精力。然而,我參加這次鐵人賽的初衷是將其設定為一個非常自由的挑戰,原本的想法就是「想到什麼寫什麼」,因此每天都是靠靈感即興發揮。說真的,我並沒有特別去做事前準備,每次都是下班吃完晚餐後,在洗澡時稍微思考一下今天該寫的主題,然後靈光一閃,就開始撰寫。基本上我的寫作流程就是這麼簡單、隨性,也因此常常落得在 deadline 前趕工想內容的窘境。

解決 Qdrant 儲存向量的問題

在昨日的文章中我曾提及,嘗試使用 Spring AI 進行 Embedding,當時使用了 OpenAI 的 text-embedding-3-small 模型,將文字轉換成向量,並選擇 Qdrant 作為向量資料庫來儲存這些轉換後的向量。然而,測試過程中遇到了一些問題,一度無法釐清問題所在,但今日終於弄清楚了整個前因後果。

問題的核心:Collection 初始化

其實問題很單純,就是我在使用 Qdrant 的時候,是透過 Qdrant Console 手動進行一些操作,像是建立 Collection,而不是透過 Spring AI 自動初始化。在 Qdrant 裡手動建立 Collection 的時候,如果只是給它命名,卻沒有設定相關的參數,那麼在寫入向量的時候就會出現問題。

這就是整個問題的癥結點!

Collection 關鍵配置

  • 向量大小(Vector Size):這是最重要的設定之一,必須設定向量的維度。以 text-embedding-3-small 模型為例,它的向量大小是 1536,這個數字必須在 Collection 初始化時指定。
  • 距離計算方式(Distance Calculation Method):大多數情況下,我們會選用 Cosine Similarity 來計算向量之間的距離,這也是初始化時需要設定的參數。

如果沒有在初始化 Collection 時設定好這些參數,那麼向量就無法正確存入 Qdrant。我之前就是忽略了這些設定,結果搞得自己一頭霧水。還好問題並不複雜,今天順利解決了!

建立簡單的文本上傳 API

在解決 Qdrant 的問題後,我決定更進一步,著手建立一個簡易的文字檔案上傳 API。由於 Spring AI 本身就提供了 ETL Pipeline 的功能,因此整個流程相當順暢。以下是我的實作步驟:

  1. 檔案讀取:利用 Spring AI 的 TextReader 來讀取上傳的 TXT 檔案,並將內容轉換成 Document 物件的列表。

  2. 文字分段:接著,透過 TokenTextSplitter 將文件切割成較小的片段(Chunks),其預設使用 CL100K_BASE 編碼,確保文字能被有效處理。

  3. 向量化與儲存:最後,使用 VectorStoreAdd 方法,將這些分段後的文字片段進行 Embedding,轉換成向量後儲存到 Vector Store 中。

圖片來源:ETL Pipeline :: Spring AI Reference

整個流程相當直觀,難度不高,但對於建立基本的文字處理流程來說,非常實用。

@PostMapping("/api/upload")
fun uploadAndProcessDocument(
    @RequestParam(
        value = "file"
    ) file: MultipartFile
): ResponseEntity<Map<*, *>> {
    if (file.isEmpty) {
        return ResponseEntity.badRequest().body(java.util.Map.of("error","Please upload a non-empty file."))
    }

    if (!file.originalFilename?.endsWith(".txt", ignoreCase = true)!!) {
        return ResponseEntity.badRequest().body(java.util.Map.of("error","Please upload a .txt file."))
    }

    val textReader = TextReader(file.resource)
    val documents = textReader.get()
    val tokenTextSplitter = TokenTextSplitter()
    val splitDocuments = tokenTextSplitter.apply(documents)
    vectorStore.add(splitDocuments)
    return ResponseEntity.ok(java.util.Map.of("upload", "ok"))
}

總結

今天的工作進度大致分享到這裡,主要完成了昨天 Qdrant 向量儲存遇到的問題,並成功建置一個簡易的文字上傳 API。上傳的文字內容會經過 ETL Pipeline 處理後,儲存至 Vector Store。由於明天是颱風假,我會思考看看還有什麼新東西可以嘗試,或許會有更多有趣的靈感出現!

也祝各位好好享受颱風假,希望明天我們都有更多創作的機會!


上一篇
打造 RAG 基礎:Spring AI 中的 Embedding 與 Vector Store 整合
下一篇
淺談 AI 繪圖工具:MidJourney 的實用技巧分享
系列文
與 AI 共舞:打造更高效的日常30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言