iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
AI & Data

Rust 加 MLOps,你說有沒有搞頭?系列 第 13

[Day 13] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖 (4/10)|對話の資料結構

  • 分享至 

  • xImage
  •  

今日份 Ferris

今天是超級自信之作哈哈哈
https://ithelp.ithome.com.tw/upload/images/20230928/20141304uhqJ62H5Gs.jpg

模組化

🏮 今天完整的程式碼可以拉到最底下 Put it together 區塊或是在 GitHub 找到。

在實作任何程式邏輯之前,我們先來定義一個結構以代表使用者與 LLM 之間的對話。
這在 Rust 中可以透過自訂 Struct 型別來完成,它又稱為結構體 (structure),可以用來把各種相關的數值組合起來成為一個自訂的型別,以物件導向的概念來說,Struct 就像是物件的資料屬性 (attribute)。

將程式碼模組化能把相關的功能組織起來,並依照功能分門別類,如此一來,我們就能清楚地知道實作特定功能的程式碼在哪。
而 Rust 提供一系列管理程式碼組織的功能,其中包含了哪些實作細節能對外公開、哪些細節是私有的,以及程式中每個作用域的名稱為何,這些功能統一稱作模組系統 (module system),其中包含:

  • 套件 (Package): 讓你建構、測試並分享 crate 的 Cargo 功能
  • Crates: 產生函式庫或執行檔的模組集合
  • 模組 (Modules) 與 use: 讓你控制組織、作用域與路徑的隱私權
  • 路徑 (Paths): 對一個項目的命名方式,像是一個結構體、函式或模組

這裡簡略說明模組、路徑、usepub 關鍵字在編譯器如何運作:

  • 從 crate 源頭開始:編譯 crate 時,編譯器會先尋找 crate 的源頭檔案來編譯程式碼。
    若為函式庫 crate 一般是指 src/lib.rs,執行檔 crate 則是指 src/main.rs

    crate 是 Rust 編譯器在當下視為程式碼的最小單位,例如每次初始化專案會給的 main.rs 檔案。
    crate 有兩種形式:執行檔 (Binary) crate 或函式庫 (Library) crate。
    前者是能編譯成執行檔並執行的程式,通常需要 main 函式來定義在執行時該做什麼事。
    後者則不會有 main 函式也不會被編譯成執行檔,就跟我們熟悉的函式庫功能相同。

  • 宣告模組:在 crate 源頭檔案中,可以使用 mod 關鍵字宣告新的模組。
    例如我們宣告了一個「LycoReco」模組 mod LycoReco;
    編譯器會在下面這幾個地方尋找模組的程式碼:

    • 同檔案內 mod LycoReco {...} 區塊中
    • src/LycoReco.rs 檔案中
    • src/LycoReco/mod.rs 檔案中

      這是比較舊的路徑風格,使用這種風格最大的缺點就是專案中有大量名為 mod.rs 的檔案,同時開啟時很容易混淆。

  • 宣告子模組:除了 crate 源頭之外,其他檔案也可以宣告子模組。
    例如我們可以在 src/LycoReco.rs 中宣告 mod closet;
    編譯器會與當前模組同名的目錄底下這幾處尋找子模組的程式碼:

    • 同檔案內 mod closet {...} 區塊中
    • src/LycoReco/closet.rs 檔案中
    • src/LycoReco/closet/mod.rs 檔案中
  • 模組的路徑:一旦模組成為 crate 的一部分,只要隱私權規則允許,其程式碼可以在 crate 內任意地方被使用。
    例如「LycoReco」模組下「closet」模組中的 Walnut 型別可以用 crate::LycoReco::closet::Walnut 來找到。

  • 私有 vs 公開:模組內的程式碼從上層模組來看預設是私有的。要公開的話,必須將它宣告為 pub mod,而公開模組內的項目也可以用 pub 來公開。

  • use 關鍵字:在一個作用域內,use 關鍵字可以建立項目的捷徑,來縮短冗長的路徑名稱。

而上面的範例專案就可以包含以下這些檔案與資料夾:

.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── LycoReco
    │   └── closet.rs
    ├── closet.rs
    └── main.rs

https://ithelp.ithome.com.tw/upload/images/20230928/20141304OrA0yk5Qkw.jpg

而根據上面的說明,我們可以先在 src 資料夾中建立一個 model 資料夾與 model.rs,然後在 model 資料夾中加上 conversation.rs
然後在 model.rs 的上方加上 pub mod conversation;lib.rspub mod app; 下面加上 pub mod model; 來將這些模組加入專案的模組樹中。
所以此時 src 資料夾的結構如下:

.
└── src
    ├── model
    │   └── conversation.rs
    ├── model.rs
    ├── lib.rs
    ├── app.rs
    └── main.rs

建立結構體

Message

在建立代表對話的結構體之前,我們首先需要另一個結構體來代表訊息本身,這裡將其命名為 Message

pub struct Message {
    pub user: bool,
    pub text: String,
}

其中有兩個欄位 (fields),user 以布林值區分是否為使用者輸入的訊息,而 text 則為訊息本身。

Conversation

有了代表訊息的結構體後,就可以建立代表對話的結構體了,我們將其命名為 Conversation,它只有一個欄位,就是以 Message 為元素向量:

pub struct Conversation {
    pub messages: Vec<Message>,
}

為了能更輕鬆地產生新的 Conversation 實例,而非每次都要建立一個空向量:我們可以實作一個 關聯函式 作為建構子:

impl Conversation {
    pub fn new() -> Conversation {
        Conversation {
            messages: Vec::new(),
        }
    }
}

如此一來,之後要建立新對話時,只需要使用 Conversation::new() 即可。

序列化

因為這些結構體會在客戶端與伺服器端傳遞,所以必須有將其序列化與反序列化的方法。
而在 Rust 中可以透過 serde crate 來達成。
這時候可以使用 cargo add serde -F derive 或直接到 Cargo.toml 檔 dependencies 區塊加上

`serde = { version = "1.0.188", features = ["derive"] }`

這裡特別註明要使用了 derive 巨集的功能,所以只要在結構體上加上 #[derive(Serialize, Deserialize)] 就能讓該型別自動實作序列化與反序列化。

Put it together

所以最後 conversation.rs 的程式碼整理如下:

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Conversation {
    pub messages: Vec<Message>,
}

impl Conversation {
    pub fn new() -> Conversation {
        Conversation {
            messages: Vec::new(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Message {
    // distinguish the message from the LLM and the user
    pub user: bool,
    // the message
    pub text: String,
}

有了可以代表對話的資料結構之後,明天就可以開始實作一些程式邏輯了,明天見啦~
/images/emoticon/emoticon07.gif


上一篇
[Day 12] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖 (3/10)|Leptos 小教室
下一篇
[Day 14] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖 (5/10)|Signal & Action
系列文
Rust 加 MLOps,你說有沒有搞頭?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言