搭建 Prompt 管理後台(二):打造 LLM Prompt 版本控制功能

搭建 Prompt 管理後台(二):打造 LLM Prompt 版本控制功能

背景

在上篇文章中,我提到因為工作上需求,建立了一個簡易的 Prompt 管理後台。還沒看過前一篇的讀者,建議先閱讀開發背景 🙂

3 小時快速上手:用 Next.js + Firestore 搭建 Prompt 管理後台
背景 最近工作上負責一個比較探索型的任務,主要目標是將音樂 (Track) 和影像 (Video) 做配對,最後產生一個吸引人的廣告。 由於缺乏機器學習的背景,我只能透過市面上大模型的多模態 (Multi-modal) 能力,想辦法摸索出方法。而方法的其中一個環節是透過理解音樂/影像後,進行特徵抽取 (Feature Extraction)。 由於項目很早期,一開始我都直接自己寫個 script/python 腳本,調用 Google Vertex API 後存成 json 檔案。但後來發現這個方式,讓主管很難掌握目前專案進度 ; 需要別人協作幫忙時,也只能在各自電腦先修改再傳給彼此看,非常麻煩。 後來想了想,如果有個簡單的影片音樂/影像的特徵管理平台 (Feature Portal) 會方便很多,於是就決定手動搭建! 功能需求 * 輸入 GCS 影片名稱(及路徑),創建 LLM 任務

總之呢,雖然開發了初步的 Prompt 管理後台,但面對不同 Prompt 版本的切換管理、甚至是團隊內多人協作,就需要版本控制來解決。

功能需求

  • 支持查看 Prompt 過去版本,能比較不同 Prompt 結果差異
  • 可以下載/複製 輸出結果為 json 格式
  • (進階)支持測試及發佈新版本的 Prompt,區分測試及正式版

其他需求 (Non-Functional Requirement)

  • 容錯 (Fault-tolerance) :當批量進行 LLM 查詢時, 如果某些 id 失敗不能全部重來
目前成果:支持發布新版本、區分測試用途

設計迭代過程

系統設計/軟體開發有趣的地方在於,一個問題往往有許多種方法可以解決,只在於如何取捨。以這次版本控制功能來說,我就思考了好幾個方案,最後才確定用目前的設計。

v0: 無版本控制

  • 每入 videoId 產生一筆 Prompt + LLM Output 的紀錄
  • 可以調整這筆紀錄的 Prompt、重新觸發 LLM 測試
Schema
- createdAt: 創建時間
- updatedAt: 創建時間
- video_id(string): video的唯一鍵,用來查詢在GCS的檔案
- prompt(string): 實際輸入提示詞
- feature(string): LLM輸出結果

接著思考:要如何加入版本控制?

Potential Solution 1: 調整現有 schema、容納多個版本

為了容納多版本,可以將 prompt 及 feature 改成 array。當每次用戶有新嘗試 (ex.調整 Prompt 重新觸發 LLM),就在後面插入一個新版本,如下方調整:

Schema
- createdAt: 創建時間
- updatedAt: 創建時間
- video_id(string): video的唯一鍵,用來查詢在GCS的檔案
- prompts([]string): 輸入提示詞的歷史紀錄
- features([]string): LLM輸出結果的歷史紀錄

這個方案的特點是由於每次變更都插入 (append),用戶所有的操作都會被記錄下來。但仔細想會發現,這個方案無法進行有效的版本控制,因為只要對同個 video有操作,版本就會立即覆蓋。

Potential Solution 2: 透過新紀錄及 status 管理線上版本

有鑒於上面的版本覆蓋問題,聯想到第二個方案:如果變更 prompt,並不會創建新紀錄,只有當創建新任務才會新增一筆 record。因此使用時,用戶可以盡情測試一個 prompt,等確定後再新增一筆紀錄覆蓋原有版本即可。

但即使如此,變更 prompt 時還是會改到最新版本。因此為了區分 test 跟 production,需要再加入 status 的概念。每筆新增紀錄預設都是 "Test",只要當用戶選擇升級才會變成 "Production"。

Schema
- createdAt: 創建時間
- updatedAt: 創建時間
- video_id(string): video的唯一鍵,用來查詢在GCS的檔案
- prompt(string): 實際輸入提示詞
- feature(string): LLM輸出結果
- status (string): 紀錄此版本的狀態:Production為線上版本、Test是測試版本

其實這個方案是可行的,能解決版本控制/切換的問題。但如果參考像是 github 這種成熟的版本控制系統,會發現它還需要保存過去版本部署紀錄。這種「版本部署」及「LLM 任務」的不同特性,讓我決定採用接下來分拆方案。

Final Solution: 分拆 LLM任務及版本部署

我決定不再將歷史紀錄放在 tasks table,而是將它視為「測試紀錄」,並且另外創建一個專門做版本管理的 version table。

tasks table
- createdAt: 創建時間
- updatedAt: 創建時間
- video_id(string): video的唯一鍵,用來查詢在GCS的檔案
- prompt(string): 實際輸入提示詞
- feature(string): LLM輸出結果
- status (string): 紀錄此版本的狀態:Production為線上版本、Test是測試版本

version table
- createdAt
- updatedAt
- promptVersions: []PromptVersion
- featureVersions: []FeatureVersion
- currentPrompt
- currentPromptVersion
- currentFeature
- currentFeatureVersion

不只是分拆 table,在前端頁面也進行分隔。這樣好處在於,用戶可以隨意在 tasks 進行測試,不用擔心影響到正式版本。唯有當對測試結果滿意後,就可以點選 "Publish",將紀錄同步到 version table。

這個設計不僅讓後端管理 tasks 及 version 的分工邏輯變得明確,也更容易支持像「版本部署紀錄」這種進階功能。

小結

其實上面設計也是迭代了好幾版才算比較滿意。如果嚴格一點檢視,系統設計對於非功能要求如 scalability、fault-tolerance 等考慮還是顯得比較欠缺。但我覺得學習系統設計就是這樣,要實際處理過問題,才會知道系統哪裡可能有瓶頸,再去看別人的系統設計影片時也才比較有感覺。

至於功能方面,之後可能也要規劃如何將這些實際 LLM 的計算結果,與正式服務進行整合、甚至可以指定使用版本。我也發現有不少開源方案都有成熟的 Prompt 版本管理服務,連結附在下方,也可以找時間參考看看別人怎麼做。

參考