上一篇把合成 PII 測試資料拆成三個階段(Stage):先生成含 PII 的原文,再產生去識別化版本(Redaction),同時從原文建立候選實體標註(Entity Candidate)。這樣至少能把「整包結果不對」縮小到某一個階段。
但把工作拆開,不代表資料已經接對。
如果三個 Stage 都拿同一筆 Seed,各自生成一份內容,最後可能出現一個很麻煩的結果:原文有地址、去識別化版本也成功遮住地址,候選標註同樣列出一個地址;三份資料單獨看都合理,實際上卻來自三段不同文字。拿這組資料去測 Guardrail,標註位置、遮罩結果與原文自然對不起來。
這不是再補幾句 Prompt 就能解決的問題。要處理的是階段之間的相依性(Dependency):下游產物到底引用哪一份上游結果,以及上游改變後,哪些下游結果應該一起失效。
我會把 Dependency 定義得很窄:一個下游 Stage 必須讀取某個明確的上游產物,不能只是收到相同的 Seed、共用同一個 case_id,或被排在上游工作之後執行。
這三種做法看起來都有「先後順序」,卻不一定有資料依賴。
例如 Redaction 工作即使在原文生成完成後才啟動,如果它實際收到的仍是 Seed,再請模型重新寫一段客服對話並遮罩,那麼它並沒有依賴前一個 Stage 的 source_text。候選標註也一樣:若標註階段重新生成一句相似內容,再對自己生成的文字找實體,輸出格式再完整也不是原文的標註。
執行順序只能告訴我們「誰先跑」;Dependency 還要回答「後面的人實際讀了前面哪個輸出」。
以目前的 PII 案例來看,最小的有向無環圖(Directed Acyclic Graph, DAG)可以畫成:
flowchart LR
seed["Seed Contract"] --> source["source_text<br>source_version"]
source --> redacted["redacted_text<br>source_ref"]
source --> entities["entity_candidates<br>source_ref"]
這裡有一個容易畫錯的地方:entity_candidates 不應該依賴 redacted_text。
候選實體標註要回答的是「原始內容裡有哪些 PII、文字是什麼、位於哪裡」。一旦先經過遮罩,實體文字可能已被 [ADDRESS] 或其他占位符(placeholder)取代,start/end 位置也會跟原文不同。此時再拿去識別化版本做標註,得到的其實是另一個任務。
所以正確的關係是:
redacted_text 依賴原始 source_text。entity_candidates 也依賴同一份原始 source_text。NVIDIA NeMo Data Designer 的欄位設計也是沿著這個概念運作:下游欄位在 Prompt 或設定中引用其他欄位,框架由這些引用建立相依圖(dependency graph),再以拓樸排序決定執行順序。資料關係先說清楚,框架才知道第一步、第二步、第三步應該怎麼排。
record_id 還不夠只保存 record_id,可以知道三份資料屬於哪一筆案例,卻無法保證它們引用同一個版本。
假設 PII-01-007 的原文第一次生成後,因為漏用 Seed 指定的電話而重新生成。它仍然是同一筆案例,但 source_text 已經不同。如果 Redaction 使用新版原文,候選標註仍停在舊版,三份資料表面上共享同一個 record_id,內容卻已經分岔。
因此,Dependency 至少要保留「引用哪一份上游產物」的資訊。欄位名稱可以依實作調整,概念上可包含:
| 下游產物 | 必須讀取的上游資料 | 至少要留下的關聯 | 用途 |
|---|---|---|---|
redacted_text |
確切版本的 source_text |
record_id、source_version 或內容摘要(digest)、Redaction 設定版本 |
確認遮罩的是哪一份原文 |
entity_candidates |
同一版本的 source_text |
record_id、source_version 或內容摘要(digest)、實體定義與索引規格版本 |
確認實體文字與位置能回到原文 |
例如 redacted_text 指向 PII-01-007@v2,entity_candidates 卻指向 PII-01-007@v1,系統就能在內容進入後續評估以前發現版本不一致。這裡使用 v1、v2 只是設計示意;實作可以採版本號、不可變產物識別碼(Artifact ID)或內容摘要(digest),重點是不能只靠一個會被重用的案例編號。
把 DAG 寫清楚之後,上游變更的影響範圍才有辦法被推導。
| 變更 | 直接受影響的節點 | 依賴關係告訴我們什麼 |
|---|---|---|
| 只修改 Redaction 規則 | redacted_text |
候選標註仍引用未變更的原文,不必因為流程順序而一起重做 |
| 修改實體類型定義或 start/end 索引規格 | entity_candidates |
Redaction 不依賴候選標註,不應被連帶失效 |
重新生成或修正 source_text |
redacted_text 與 entity_candidates |
兩個下游產物都指向舊原文,應標記為過期並重新產生 |
這不代表管線已經有完整的重試機制,也不是在宣稱每個框架都會自動完成 Artifact 版本管理。DAG 先提供的是可計算的影響範圍:哪個節點改變,哪些後代節點不能繼續沿用。至於失敗時要 Accept、Reject、Quarantine,或如何只重跑必要 Stage,還需要下一步的 Quality Control 與執行策略。
Dependency 可以讓管線回答幾個原本回答不了的問題:
但 DAG 不會自動證明內容正確。Redaction 可能確實引用同一份原文,卻仍漏掉電話;候選標註也可能指向正確版本,卻標錯實體範圍。模型或規則產生的 entity_candidates 仍只是候選標註,不能因為資料沿襲(lineage)完整就升格成 Ground Truth。
本文也沒有實際執行資料生成、NeMo Data Designer 或重試測試;這裡整理的是 PII 合成測資管線的 Dependency 設計。今天先確保三份資料真的來自同一條資料路徑,下一步才有資格討論哪些結果可以留下。