業務詞彙表¶
業務詞彙表是一套活的詞彙,覆蓋在你的數據模型之上。語意層裡的每個實體欄位都會解析到一個術語——凡多個欄位承載同一個概念,不論拼法差多遠,都歸到同一個共用術語。每個術語可以帶有一段定義、一組通往其他術語的具類型關係,以及一份對該含義負責的主題專家名單。
這套共用詞彙,正是業務語言與實體數據之間的橋樑。知道「客戶」指的是哪些承載客戶識別碼的欄位的 AI 代理,毋須猜測 cust_id、customerId 與 CUSTOMER_KEY 三者哪個才對——它們全部解析到同一個術語,而定義就在該術語身上。
術語如何推導而來¶
Provisa 會依一條決定性的正規化規則(REQ-1387),自動從每個欄位名稱推導出術語:大小寫摺疊、分隔符與 camelCase 斷詞、縮寫展開,以及尾端代理權杖的剝除。
縮寫展開把常見的企業簡寫對應回完整形式:cust → customer、txn → transaction、qty → quantity,諸如此類。id 與 key 都展開為 identifier。這張對照表是固定而保守的——st、min、no 這類含糊的簡寫維持原樣,不去冒猜錯的險。
代理權杖剝除會移除尾端的 identifier、code、index 或 reference 權杖。名為 cust_id 的欄位命名的並不是識別碼本身;它是透過一個代理值來指稱客戶。剝掉代理之後,cust_id 與 customerId 就一同落在 customer 這個術語上。只有尾端的權杖會被剝除,而且絕不剝到只剩最後一個:孤零零的 id 欄位展開為 identifier 後便停在那裡。
去重才是重點所在。正規化規則是決定性的,因此 cust_id、customerId 與 CUSTOMER_KEY 全都產出 customer。每個欄位都在那個唯一產出的術語上取得一個 ref,而不是生出三個各自為政的術語。如此一來,策展只需在一個地方補上定義,而非三個。
通用短語¶
有些正規化後的短語太通用,本身撐不起一個概念。孤零零的 name、date 或 identifier 欄位,命名的是所屬表那個概念的一項屬性,而不是獨立於該表的概念。員工有名字;產品有名字;兩者並非同一回事。
當一個短語落在通用集合之內,而又有表脈絡可用時,術語會被限定為 <table concept> <phrase>:employees.first_name 正規化為 employee first name,而 orders.id 正規化為 order——因為代理剝除接著會把這個限定短語收攏到它所識別的那個概念上。最後這個情況很重要:orders 的主索引鍵,連同其他表上每個 order_id 外部索引鍵,全都落在 order 上,毋須額外策展。
通用集合涵蓋屬性名詞(name、date、status、type、amount、quantity)、審計軌跡短語(created_at、modified_by、submitted_timestamp),以及另外幾個幾乎每張表都有的字眼。
用業務名稱,而非實體名稱¶
推導出來的術語跟隨欄位的業務名稱——建模者設定了別名就用別名,沒設定就用實體名稱(REQ-1581)。當 usr_nm 的別名是 user name 時,推導出的術語就是 user name,而不是 user number 或任何由 usr_nm 展開而來的東西。
替欄位設別名是較有力的修正。別名會傳到每個讀取該欄位的介面——SQL、GraphQL、AI 代理、目錄——因此模型在各處都能正確地自述。改術語名稱只修好一筆目錄項目,卻把讀作 usr_nm 的欄位留給下一位讀者。UI 裡的提議術語橫幅把話說得很白:先替欄位設別名;只有當欄位名稱本身沒問題、有問題的是詞彙時,才去改術語名稱。
替欄位重設別名會重新推導它的提議術語,因此詞彙表跟著模型走,而不會就同一處修正問你兩次。一旦策展者為某術語補上定義、關係或專家,別名編輯就不會再搬動那個 ref——那是策展者的功夫,它留在原處。
存取路徑式的表名¶
有些表名描述的是一條存取路徑,而不是一個概念:user_by_name 是經名稱查找而觸及的使用者,並非另一種實體。Provisa 為通用短語限定而推導表概念時,會在連接詞處把名稱切斷(REQ-1582)。user_by_name 變成 user;orders_by_customer 變成 order。
若不切斷,user_by_name 上的代理索引鍵就會正規化為 user name,與真正的 users.name 屬性相撞——一個術語同時裝著一樣事物和它自己的一個欄位。這項切斷只適用於表概念。在欄位名稱裡,by 是複合名詞的一部分:pet_by_name 與 pet_name 正規化為同一個術語 pet name。
術語何時算是已策展¶
由欄位正規化而生的術語一開始是空白的——它是一項提議,還不算詞彙。以下任一條成立時,它便成為已策展:
- 已儲存一段定義。
- 已加入一條關係邊。
- 已指派一位主題專家。
- 策展者已手動將它退役。
策展關乎術語的生命週期。當一個已策展術語在模型中的最後一個實體欄位被移除時,該術語會被淘汰而非刪除:它退出服務,保留編輯者提供的內容,並在同一欄位再度出現時自動復活。未策展且已無欄位的術語則直接移除。
從表重新同步¶
每當一張表被儲存或重新載入,sync_table_refs 便會把該表的欄位與既有的 ref 對帳。新欄位會建立或連結術語;離去的欄位會撤掉自己的 ref;而移除或淘汰的規則會為任何失去最後一個 ref 的術語作出了結。
重新推導只發生在未策展的術語上。若你替某欄位設了別名,而提議術語因此不同了,ref 就會移到新術語去。若該術語已策展,連結則維持不動——別名編輯並未推翻策展者選定的術語。
一個抽象術語若通往實體數據的唯一路徑經由某個正在離去的術語,它會被淘汰而非移除,在重新接線之前先保住概念結構。
關係¶
術語透過具類型的邊與其他術語產生關聯。受支援的關係類型如下:
| 類型 | 含義 |
|---|---|
KIND_OF |
來源術語是目標術語的一種。 |
PART_OF |
來源術語是目標術語的一個組成部分。 |
SYNONYM_OF |
兩個術語在此領域中可互換。 |
RELATED_TO |
一種鬆散的關聯——沒有更強的說法貼切。 |
VALID_VALUE_OF |
來源是目標列舉或值域的一個允許值。 |
DERIVED_FROM |
來源由目標計算而得或取自目標。 |
REPLACES |
來源取代了已淘汰的目標。 |
PREFERRED_TERM_FOR |
相對於不建議使用的目標,來源是首選術語。 |
TRANSLATION_OF |
來源是目標的地區設定或語言翻譯。 |
ANTONYM_OF |
來源在語意上與目標相反。 |
關係有方向性。UI 會同時顯示外向邊(此術語 → 另一術語)與內向邊(另一術語 → 此術語),並各自以自己的白話說法標示方向。
這些邊存放在 glossary_term_edges 中,這是一張宣告為聯結(junction)關係的關聯表(REQ-1586):它的 rel_type 欄是判別欄,因此上表中的每種類型都是兩個 GlossaryTerm 節點之間獨立的 Cypher 關係類型,而不是具體化節點上的一個屬性。該表隨中繼資料綱要的其餘部分一併佈建,在圖用戶端中不會顯示為節點——它就是邊。它沒有任何專屬於業務術語表的地方:它的宣告方式與你在自己的表上宣告聯結完全相同,並且由同一套程式碼讀取。
[tool-verified: provisa/cypher/label_map.py:378-397, provisa/api/startup_seed.py:508-550]
抽象術語¶
抽象術語自身沒有任何實體欄位 ref。當某個業務概念橫跨多個具體術語時就用它——先立一把傘,再接線到真正持有欄位的那些具體術語。舉例來說,revenue 可以是抽象術語,由 order amount、adjustment amount 與 refund amount 各拉一條 PART_OF 邊指向它。
無法經關係圖觸及任何實體欄位的抽象術語,是一項懸空的提議。它不會出現在代理的術語搜尋中,也不會出現在中繼資料匯出裡——命名不到任何數據的術語,什麼也答不了。
消費介面的准入規則¶
消費介面可以提供的術語,必須滿足三項條件(REQ-1387):
- 在役——未退役(策展者將它撤出服務)且未淘汰(它失去了最後一個欄位,僅因刪掉它會留下懸空之物而被保住)。
- 已定義——它帶有一段定義。從欄位名稱推導而來的術語是一個權杖,不是一個含義。沒有定義,它就只是一項等待策展者的提議,絕不是代理能據以立足來回答問題的詞彙。
- 已扎根——經由在役術語,連通到至少一個持有實體欄位 ref 的術語。詞彙表是通往數據的入口,因此每條鏈都必須終止於某個欄位。
連通性會沿圖傳播:抽象術語可經由任何本身觸及得到數據的在役鄰居而觸及數據。不在役的術語不導電——已退役的術語不會替它的依賴者續命。
中繼資料匯出¶
詞彙表會作為中繼資料匯出的一部分,發佈到外部數據目錄。同一條准入規則適用,但收窄了一處:術語是否扎根,只以真正會發佈的欄位為準。若某術語的欄位全數被排除於匯出之外——因為它們所屬的表未標記為數據產品,或因為技術性篩選器把它們排除掉——那麼即使它在控制平面裡持有 ref,就匯出而言仍屬未扎根。
關係邊只在兩端術語都會發佈時才發佈。
欄位資產則獨立匯出。某個術語被排除,並不會把底下的數據藏起來。
把術語排除於匯出之外¶
有些欄位承載的是水電管線而非業務數據:ETL 批次識別碼、行版本、擷取時間戳記。由這類欄位推導而來的術語,其定義可能準確無誤,只是它根本不算業務詞彙(REQ-1583)。排除於中繼資料匯出這項控制項會把該術語,以及任何以它為終點的關係邊,一併從 Provisa 發佈的目標目錄中扣起,而欄位本身仍會以資產形式匯出。
判準在於業務上是否會說這個詞,而不在於定義寫得好不好。ETL 批次識別碼有清楚的含義,對工程師而言它該留在詞彙表裡;但它不該在業務目錄中與 customer 和 revenue 並排。
使用詞彙表¶
在 UI 中開啟 管理 → 詞彙表。左側面板列出所有術語;點選其一即可開啟其詳細檢視。從那裡可以:
- 重新命名術語,改動它的措辭而不搬動它的欄位。
- 加入定義:自行輸入,或點擊 AI 草擬按鈕,依術語名稱、其實體欄位及其關係生成一個起點。草稿在你確認之前不會儲存。
- 移動 ref 以合併兩個術語:在任一實體 ref 旁的下拉式選單中挑選目標術語。若來源術語因此失去最後一個 ref,系統會自動依移除或淘汰的規則作出了結。
- 加入關係,連結此術語與另一術語,並從封閉集合中選擇類型。既有的邊可就地改類型,毋須先刪再加。
- 指派專家,以使用者 ID 指定,種類可為
expert或author。 - 退役某術語,將它撤出服務。它保留自己的欄位,在此處仍可編輯,但代理的術語搜尋與中繼資料匯出都會略過它。日後概念回歸時可再還原。
- 批次生成定義,一次補齊所有空白的定義。只會寫入空白的定義;人寫的文字絕不會被覆寫。
- 批次生成關係,就整份術語清單提議具類型的邊。格式有誤的提議——未知的術語名稱、指向自身的邊、無法辨識的類型——會被自動丟棄。
沒有定義的術語上會出現提議中橫幅,它會告訴你該術語是未定義(去替欄位設別名,或補上定義)還是未扎根(把它關聯到某個持有欄位的術語)。看到這個橫幅,就表示該術語尚未能被代理或目錄觸及。