系列: 與 AI 一起學寫程式

在紙上勾勒 SDK 的資料夾結構

在任何程式碼存在之前先畫出的 SDK 骨架。

資料夾結構就是規格 先畫出代理人必須在其中撰寫的方格 /sdk /apps /modules /assets /docs /config arena_demo/ coach_demo/ hud_designer/ ...8 demos hud gesture gaze sync fx voice widgets audio gesture icons README guides API refs layouts roles 在實作存在之前,樁就先鎖定契約
結構就是紀律——代理人透過他們寫入的方格繼承這份紀律。

由代理人打造的程式碼庫,要嘛三個月後變成一團糨糊,要嘛多年保持連貫——而分野在寫下第一行程式碼之前就已注定。RakuAI 先把方格畫好,讓代理人永遠落在正確的位置。

有一種設計工作,從外面看不見,從裡面卻是承重結構。程式碼庫的資料夾結構就是其中之一。它看起來像檔案管理。實際上它是程式碼的組織圖。及早做對,未來兩年每一個 PR 都會落在正確的位置。及早做錯,未來兩年每一個 PR 都得爭論自己該放哪裡。

這個週六就用來及早把它做對。

這個結構必須支撐什麼

在畫方格之前,我先列出結構必須滿足的限制條件。

每個展示都有自己的家。 我上個月定好規格的八個展示,各自需要一個資料夾,讓它們的程式碼、素材和文件住在一起。開發者閱讀任何一個展示時,不應該需要在 SDK 的四個不同角落之間跳來跳去才能看懂。

共用模組可與展示分離。 任何跨展示重複使用的東西(HUD 渲染、手勢輸入、視線追蹤、多人同步、語音控制、視覺特效)都住在自己的地方,展示透過公開介面來使用它們。

素材按類型組織,而不是按展示。 一張血條 PNG 是 HUD 元件,不是競技場展示的素材。可重複使用的視覺素材住在共用素材資料夾裡,需要它們的展示以連結方式取用。

文件與其描述的對象放在一起。 SDK 安裝文件放在 SDK 旁邊。展示指南放在展示旁邊。HUD API 文件放在 HUD 模組旁邊。文件能不能被找到,大半取決於文件放在哪裡。

設定是獨立的關注點。 HUD 設定檔、預設版面配置、多人角色設定。這些不是程式碼、不是素材、不是文件。它們有自己的家。

結構本身

一個上午的白板作業之後,配置長這樣。

根資料夾就是 SDK 本身。根目錄之下:

/apps/ 是展示的所在。每個展示有一個子資料夾。arena_demo/ 是單人競技場。coach_demo/ 是 AR 教練。hud_designer/ 是 HUD 疊加層設計器。streamer_demo/ 是實況主模式。pos_demo/ 是服務點疊加層。companion_demo/ 是 AR 伴侶 HUD。aimlab_demo/ 是 AR 瞄準訓練器。hudsync_demo/ 是多人 HUD 同步。每個展示資料夾包含自己的原始碼、自己的設定、自己專屬的素材,以及自己的 README。

/modules/ 是共用程式碼的所在。一開始有六個模組。HUD 渲染器,每個展示都會用到。手勢輸入偵測,大多數展示會用到。視線追蹤,由需要的展示使用。多人同步,由區域網路多人展示使用。負責視覺特效的疊加層動畫器。給接受語音輸入的展示使用的語音控制介面。每個模組對外暴露一個小而穩定的公開介面,供展示連結。

/assets/ 是共用素材庫。HUD 元件(血條、彈藥計數、羅盤疊加層),格式為 PNG 和 SVG。命中、警示、旁白用的音效。在手勢被偵測到時提示使用者的手勢圖示。展示連結到這裡的素材,而不是各自複製一份。

/docs/ 是文件的家。一份介紹 SDK 並逐步說明安裝的 README。一份說明如何執行八個展示的展示指南。HUD 模組的 API 參考。語音控制模組的 API 參考。文件刻意放在對應位置旁,讓想了解 HUD 怎麼運作的開發者能在同一個資料夾裡同時讀到原始碼和文件。

/config/ 是設定。作為新專案起點的預設 HUD 版面配置。從 HUD 設計器匯出的範例版面配置。多人角色定義。把設定與程式碼分開,能讓展示保持乾淨,也讓開發者不用重新編譯就能改變行為。

資料夾結構本身只是一個空的檔案系統。今天起草的另一樣東西,是一組程式碼樁,向代理人展示(當他們最終開始針對這個結構寫程式時)每個進入點的真實實作應該長什麼樣子。

這些樁目前是 Python 偽程式碼。等引擎儲存庫開張後,它們會被翻譯成正式語言(執行環境用 C++,並提供 Python 及其他生態系的繫結)。Python 形式讓我能推敲介面,而不會迷失在實作細節裡。

一個樁的範例,屬於手勢輸入模組。大致如下:

def detect_gesture(frame):
    """Detect gestures like swipe, point, raise, duck in a single sensor frame."""
    if is_swipe(frame):
        return "swipe"
    elif is_raise(frame):
        return "raise_arm"
    else:
        return None

這不是正式程式碼。這是契約。正式程式碼會把 is_swipeis_raise 換成真正的電腦視覺管線。介面維持不變。這個簽名就是展示要依循撰寫的東西。及早鎖定簽名,代表我可以在 CV 管線還沒萬無一失之前就先出貨展示,因為展示不依賴實作,它們依賴介面。

我為 /modules/ 資料夾裡的每個模組都起草了這種形式的樁。六個小小的 Python 檔案。沒有一個實作任何東西。每一個都定義了最終實作必須遵守的契約。

為什麼代理人需要這個

這就接回幾個週六前的代理人名單。

等到要寫真正的程式碼時,代理人不會去發明架構。架構已經在紙上。資料夾結構告訴他們新程式碼放哪裡。模組樁告訴他們要實作什麼介面。素材的組織方式告訴他們去哪裡找需要的東西。文件結構告訴他們寫好的文件放哪裡。

這就是「三個月後變成糨糊的代理人程式碼庫」和「多年保持連貫的程式碼庫」之間的差別。結構就是紀律。代理人透過結構繼承紀律。

沒有這些準備工作,一個被要求「實作手勢輸入」的代理人會發明一個資料夾、發明一套 API、寫好程式碼,做出一個孤立運作的東西。乘以六個模組,這種做法會產出六個互不相容的迷你引擎。結構透過畫出代理人必須寫入的方格,來阻止這種失敗模式。

這件事難在哪裡

幾句老實話。

資料夾結構會想要長大。 六個月後我會想加第七個模組。結構必須容許這件事,又不變成雜物抽屜。我給自己定的規則:任何新的頂層資料夾都必須說明為什麼它不是現有資料夾的子資料夾。大多數新東西最後都是子資料夾。

樁需要維護。 隨著真正的實作陸續落地,樁會過時。把樁當作「契約」來讀的代理人,會繼承樁與真實實作之間的任何漂移。解法是讓樁成為事實來源,並要求真實實作在契約改變時同步更新樁。

其中一些資料夾是推測性的。 /config/ 資料夾是一個賭注,賭執行期設定檔會成為這套 SDK 裡真實存在的一類產物。如果事實證明不是,這個資料夾就會縮小或併進別的地方。我可以接受。之後移除一個資料夾,比一年後在慣例已經漂移時才發明一個要便宜得多。

接下來是什麼

下個週六是 API 介面設計。上述六個模組每一個都需要指定其公開介面。也就是決定哪些資料型別跨越邊界、回傳哪些錯誤碼、發出哪些事件。資料夾結構完成後,API 設計就有了落腳的地方。

之後是逐一為每個展示指定它使用哪些模組、以什麼順序使用。然後是執行環境架構。最後,引擎儲存庫才開張。

專利資產已經等了十年。再多幾週把設計做對的工夫,不會是讓我們錯失窗口的原因。

資料夾結構完成。程式碼庫現在有了形狀,即使裡面還沒有任何程式碼。

一套不會在你腳下變動的 SDK

一個不會改變的資料夾結構、一個在實作之前就鎖定的 API 介面——在一個為長期而設計的空間運算執行環境上打造。

← 所有文章