iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Security

《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》系列 第 17 篇

Day 17|跨文件查核:去識別化之後還能比對嗎?

  • 分享至 

  • xImage
  •  

法務場景的真實需求

一個法務案件不會只有一份文件。典型的組成:

  • 借款契約
  • 保證書
  • 催收紀錄
  • 客戶陳情書
  • 起訴狀
  • 對方答辯狀

律師要做的事情之一是交叉比對:契約上的保證人跟保證書上的簽名人是不是同一人?陳情書裡說的還款日期跟催收紀錄對不對得上?答辯狀裡提到的第三人有沒有出現在其他文件?

這些都需要跨文件的實體關聯。而去識別化如果做得太徹底,這個能力就沒了。

為什麼 scoped 一致性剛好解決這件事

昨天的結論是「案件內一致,跨案件不可關聯」。這個設計剛好對應了業務需求:

  • 案件內一致 → 交叉比對可以做
  • 跨案件不可關聯 → 安全性維持

所以 scope_id 應該設成案件編號,而不是 session ID 或文件 ID。

scope_id = f"case:{case_no}"

這樣同一個案件的所有文件共用同一個 Token 空間,模型看到的是:

契約:      立約人林建成,連帶保證人張雅婷
保證書:    保證人張雅婷
起訴狀:    被告林建成、張雅婷

模型能正確地判斷「保證書上的張雅婷跟契約上是同一人」。它不知道真名是誰,但它知道這是同一個人——而這正是法律分析需要的。

scope 的粒度是一個安全決策

scope_id 設得越大,可比對範圍越大,同時可關聯風險也越大。

scope 粒度 可比對範圍 風險
單一文件 文件內 最低
案件 案件內所有文件 低
客戶 該客戶所有案件 中
部門 全部 高

「客戶」這一級聽起來合理(律師常需要看同一客戶的歷史案件),但它有一個問題:Token 變成了客戶的穩定識別碼。 一旦某個 Token 在某個案件裡被還原確認,該客戶的所有歷史案件就都被關聯起來了。

我的建議是預設「案件」級,需要跨案件比對時走一個明確的、需授權的合併流程:

def create_merged_scope(case_ids: list, requester: str,
                        reason: str) -> str:
    """建立跨案件的合併 scope,需授權並留痕"""
    require_permission(requester, "scope:merge")
    merged_id = f"merged:{uuid4()}"
    audit_log.write({
        "action": "scope_merge",
        "actor": requester,
        "cases": case_ids,
        "reason": reason,
        "merged_scope": merged_id,
        "ts": now(),
    })
    return merged_id

把「擴大可關聯範圍」變成一個需要理由、需要留痕的動作,而不是預設行為。這是最小權限原則在資料關聯上的應用。

一個容易被忽略的洩漏管道:文件結構本身

就算所有個資都遮乾淨了,文件的結構仍然可能洩漏資訊。

文件長度。 兩份看起來完全去識別化的文件,如果長度、段落數、章節結構完全一致,可以推斷它們來自同一個範本、可能是同一批案件。

日期關聯。 就算日期被概化到年,「文件 A 的日期早於文件 B」這個順序關係仍然保留。結合公開資訊(例如某個新聞事件的日期),可能推回具體時間。

金額。 金額通常不去識別化(D9),但「借款餘額新臺幣 1,234,567 元」這種精確到個位數的數字,本身就是一個很強的準識別碼。如果攻擊者有部分外部資料,這個數字足以定位。

這一項在實務上被低估。我的建議是對高精確度的數值做輕度概化:

def generalize_amount(amount: int) -> str:
    if amount < 100_000:
        return f"新臺幣 10 萬元以下"
    if amount < 1_000_000:
        return f"新臺幣 {amount // 100_000 * 10} 萬元級距"
    return f"新臺幣 {amount // 1_000_000} 百萬元級距"

但這會影響法律判斷的精確度。所以要看任務——如果 LLM 的任務是「摘要爭點」,概化沒問題;如果任務是「計算利息」,就必須保留原值。

這個取捨應該由業務單位決定,而且要寫進場景設定,不是由工程師預設。 我的作法是把它做成場景層級的設定項,讓場景管理者(D19 的角色之一)自己選。

跨文件的實體衝突

多份文件合併處理時會遇到衝突:

契約:    保證人 陳美玲,身分證 A223456789
保證書:  保證人 陳美玲,身分證 A223456780   ← 最後一碼不同

這是同一個人打錯字,還是兩個同名的人?

系統不應該猜。正確做法是:

  1. 兩者都 Token 化,產生不同的 Token(因為強識別碼不同)
  2. 標記為「疑似同一實體,需人工確認」
  3. 把這個標記傳給下游,讓律師自己判斷
def detect_entity_conflict(entities):
    conflicts = []
    by_name = defaultdict(list)
    for e in entities:
        by_name[e["name"]].append(e)
    for name, group in by_name.items():
        ids = {e.get("tw_id") for e in group if e.get("tw_id")}
        if len(ids) > 1:
            conflicts.append({
                "name_token": group[0]["token"],
                "distinct_ids": len(ids),
                "note": "同名但識別碼不同,需人工確認",
            })
    return conflicts

寧可讓下游知道有不確定性,也不要幫它決定。 自動合併兩個實體,可能造成法律判斷的錯誤;自動分開,可能漏掉關聯。兩種錯誤都比「標記出來讓人決定」糟糕。


明天進可控還原:先從 Token 隔離講起。


關於作者

我是 Fngi,專注在 AI 資安、LLM 紅隊與 AI 治理框架落地。
IG:@aid3fend
有想討論的架構細節或不同意見,留言或私訊都歡迎。



上一篇
Day 16|Token 一致性:同一個人要對到同一個 Token
下一篇
Day 18|Token 隔離:把爆炸半徑切小
系列文
《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言