> ## Documentation Index
> Fetch the complete documentation index at: https://docs.starfort.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 効果的な PII ポリシーの書き方

> 検知エンジンが正確に動作するようルールの内容と優先順位を設計する方法を、日本のデータに合わせた例で説明します。

[カスタマイズした PII ポリシーを追加する方法](/ja/v1.3/admin/how-to/add-pii-policy)がコンソールでの**手順**を扱うのに対し、このページはルールの**内容**を扱います — どのフィールドに何を書けば検知エンジンが正確に動作するのか、そしてルール同士が衝突しないよう優先順位をどう設計するのか。例は **Starfort デフォルト PII ポリシー**(韓国の識別子 — 住民登録番号・韓国の住所・電話形式 — 向けにチューニングされて同梱)の構造に沿って、なじみやすいよう**日本のデータ**に置き換えています。書き方の原則はどのロケールでも同じです。

## ポリシー構造と処理順序

PII ポリシーは 3 種類のルール配列で構成されます。許可されるトップレベルキーは `ner`、`regex`、`keyword` で、エンジンは常に **Keyword → Regular Expression → NER** の順に検査します。前の段階が確定したテキストは後の段階には**隠された状態で渡される**ため、前の段階の判定がそのテキストに対して最終となります。

```mermaid theme={"dark"}
flowchart LR
    T["入力テキスト"] --> K["1. Keyword<br/>完全一致"]
    K -->|"残りのテキスト"| R["2. Regular Expression<br/>パターン一致"]
    R -->|"残りのテキスト"| N["3. NER Entity<br/>AI モデル検知"]
    K & R & N --> D["全検知結果"]
    D --> F{"最終 action"}
    F -->|"BLOCKING ルールに一致"| B["BLOCK"]
    F -->|"その他マスキング一致"| M["MASK"]
    F -->|"一致なし"| P["PASS"]
```

| ルール種別                  | 特性                     | 適した対象                              |
| ---------------------- | ---------------------- | ---------------------------------- |
| **Keyword**            | 大文字小文字を区別する完全一致。最速・決定的 | 社内コードネーム、既知の安全な値(allowlist)        |
| **Regular Expression** | パターン一致。決定的             | 形式が固定された識別子(マイナンバー、電話番号、カード番号、メール) |
| **NER Entity**         | AI モデル検知。文脈を理解         | 文脈に依存する情報(氏名、住所)、パターンが取りこぼす変形      |

## 優先順位の設計

ルールが増えるほど、ルール同士の相互作用が結果を左右します。3 つの優先順位を覚えてください。

1. **ルール種別の優先順位: Keyword > Regular Expression > NER。** 前の段階が確定したテキストを後の段階が再判定することはありません。
2. **PASSING は allowlist です。** `policy_type: "PASSING"` ルールが一致したテキストは**安全として確定**し、以後どのマスキング・ブロックルールも上書きできません。同じ種別内で同じテキストに 2 つのルールが一致した場合は **id が小さいルール**が優先されます。
3. **action の優先順位: BLOCK > MASK > PASS。** リクエスト内でルールが 1 つでも BLOCK を決定すると、他のルールがマスキングしただけでも**リクエスト全体がブロック**されます。

この原則を活かすには、実データ用マスキング正規表現より**小さい id にテストパターンの PASSING ルール**を配置します。ドキュメントの例示や QA で使うサンプル値がマスキングされなくなります:

```json theme={"dark"}
{
  "id": 0,
  "rule_id": "test_data:_my_number_test_pattern",
  "regex": "(?<!\\d)(?:0000[-\\s]?0000[-\\s]?0000|1234[-\\s]?5678[-\\s]?9012)(?![-\\s]?\\d)",
  "description": "0000-0000-0000, 1234-5678-9012 (よく使われるマイナンバーのサンプル値)",
  "policy_type": "PASSING",
  "mask_word": "TEST_DATA",
  "alert_message": "test data pattern - allowed"
}
```

同じ目的で keyword ルールも使えます — サンプル文書の定番名 `山田太郎` を PASSING で先取りし、NER の氏名ルールがマスキングできないようにします。Keyword 段階は NER より先に実行されるため、この先取りは確実に機能します。

## Regular Expression ルールの書き方

マイナンバー(個人番号)のルール:

```json theme={"dark"}
{
  "id": 10,
  "rule_id": "my_number:_japan_individual_number",
  "regex": "(?<!\\d)\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}(?![-\\s]?\\d)",
  "description": "1234-5678-9012, 1234 5678 9012, 567890123456 | 末尾ガードで 16 桁カード番号の先頭 12 桁を先取りしない",
  "policy_type": "MASKING",
  "mask_word": "MY_NUMBER",
  "alert_message": "マイナンバーを検知しました"
}
```

このルールが示す作成プラクティス:

* **数字境界を固定する。** 前後の `(?<!\d)` / 末尾ガードがないと、より長い数字列(銀行口座、バーコード)の**中間部分**がマイナンバーとして誤検知されます。
* **他ルールとの衝突はパターン側で解決する。** 末尾の `(?![-\s]?\d)` ガードは、`1234-5678-9012-3456` のような **16 桁カード番号の先頭 12 桁**をマイナンバーとして先取りしてしまう誤検知を防ぎます。
* **`description` に一致例を記録する。** このフィールドは検知には使われませんが、後でルールをレビューする人がパターンを解読し直さずに済みます。

<Warning>
  空文字に一致し得るパターン(`\d*`、`secret|`)は何も検知せずスキップされます。またパターン前後の空白は保存時に除去されるため、意図的な境界空白は `\s` や `[ ]` のようにパターン内に表現してください。
</Warning>

## NER Entity ルールの書き方

NER ルールで AI モデルに渡されるのは **Name、Description、Positive Examples、Negative Examples** の 4 フィールドだけです。**Policy Type、Mask Word、Alert Message** は検知*後*の処理だけを決め、検知自体には影響しません。つまり検知精度は前の 4 フィールドで決まります。

日本の住所ルールで各フィールドの役割を見ます。このルールの核心は**具体性の境界**です — 同じ「場所の話」でも、特定の個人や事業所にたどり着けるほど具体的なら機密情報、一般的な場所への言及ならそうではありません。以下の各節が例のどの部分を指しているか、対応させながら読んでください:

```json theme={"dark"}
{
  "id": 2,
  "name": "住所 (日本)",
  "description": "特定の個人や事業所の位置を識別できる日本の住所。町名+丁目・番地(・号)のような具体的な要素を必ず含み、建物名・階・部屋番号のような詳細情報が続くことがある。ランドマーク、駅の出口、建物名単独、番地のない地名のような一般的な場所への言及は住所ではない。",
  "policy_type": "MASKING",
  "mask_word": "ADDRESS",
  "alert_message": "住所を検知しました",
  "positive_examples": [
    "本社: 東京都千代田区丸の内1丁目9-2 グラントウキョウサウスタワー 21階 →丸の内1丁目9-2 グラントウキョウサウスタワー 21階",
    "配送先: 大阪府大阪市北区梅田3-1-3 ノースゲートビルディング 7階 →梅田3-1-3 ノースゲートビルディング 7階",
    "オフィスの住所は港区六本木6-10-1 六本木ヒルズ森タワー 18F です →六本木6-10-1 六本木ヒルズ森タワー 18F"
  ],
  "negative_examples": [
    "渋谷駅ハチ公口で待ち合わせ",
    "スターバックス新宿店で会おう",
    "代官山のカフェを教えて"
  ]
}
```

<Note>
  同梱されるデフォルトポリシーは韓国のトラフィック向け(韓国の住所・住民登録番号・電話形式)に書かれています。上のルールはその住所ルールを日本のデータに置き換えたものです — フィールド構造と書き方の原則は変わりません。
</Note>

### Description — 定義 + 具体性の条件

上の Description は 4 つの要素を順に含んでいます: **定義**(個人・事業所の位置を識別)、**必須の具体性条件**(「町名+丁目・番地」)、**許容される詳細の変形**(建物名・階・部屋番号)、そして**除外条件**(ランドマーク、駅の出口、建物名単独、番地のない地名)。機密/非機密を分ける境界線を、Description が文章で引いているのです — モデルは「この場合は該当しない」と書かれたケースに NO と答えるよう指示されるため、**除外条件は実際に機能します**。下のシナリオ 3 が、この境界が実際に判定を分ける様子をそのまま見せます。

### Positive Examples — 矢印の規約

すべての肯定例は `文脈 →値` の形式で書きます。モデルは `→` の後ろが**検知すべき正確な値**だと学習します。

* `→` の前の文は**いつ**発動するか(再現率)を、`→` の後ろの値は**正確な出力形態**(精度)を教えます。
* 値は**マスキング範囲**も教えます。上の 3 例はいずれも、値が広域単位(東京都千代田区、大阪府大阪市北区 など)を除いて**町名から**始まっています — どこからどこまでを機密情報と見なすかを、例の値で教えているのです。
* 値は入力内で**再度見つけられる**必要があります。現れたとおりに書くのが最も安全で(スペースの違い程度は許容)、町名と建物名を並べ替えるような再構成をした値は位置を特定できず破棄されます。
* 上の 3 例は**実際に登場する表記を 1 つずつ**教えています: ラベル付きの本社住所、配送先ラベル+ビル名の変形、ラベルなしで文中に埋め込まれた住所。取りこぼせない形ごとに例を 1 つずつ置いてください。
* 検知したいデータの言語・形式で例を書いてください。

### Negative Examples — 誤検知の防壁

形は似ているが**具体性が足りない近接事例**を入れてください。上の 3 つの negative はそれぞれ別の誤検知経路を塞ぎます: `渋谷駅ハチ公口`(待ち合わせ場所のランドマーク)、`スターバックス新宿店`(店名+支店名)、`代官山`(番地のない街の名前)。3 つとも Description の除外条件と対になっています。NER ルールは小さなバッチでまとめてスクリーニングされるため、あるルールの弱い negative が隣のルールの過検知まで誘発することがあります。

## Keyword ルールの書き方

Keyword は大文字小文字を区別する**完全一致**です。文脈を見ないため、短く一般的な単語は避けてください。

* **良い BLOCKING キーワード**: `PROJECT-ORION` のような社内コードネーム — どこに現れてもそれ自体が機密。
* **良い PASSING キーワード**: `山田太郎` — サンプル文書の定番名を先取りして、NER の氏名ルールにマスキングさせない allowlist。
* **悪いキーワード**: `アカウント` のような一般名詞 — 現れるすべての箇所で無差別に発動。

## シナリオ例

上のようなルールを割り当てた Guardian にリクエストを送ったときの動作です。

### シナリオ 1 — 携帯電話番号のマスキング

> 「私の番号は 090-1234-5678 です。080.9876.5432 でも連絡できます。」

区切り文字が違っても電話番号の正規表現が両方の値に一致します。結果は `"action": "MASK"`、本文は `私の番号は [PHONE_NUMBER_1] です。[PHONE_NUMBER_2] でも連絡できます。` — マスクワードには出現順にインデックスが付きます。

### シナリオ 2 — テストデータは通過、実データはマスキング

> 「テスト用のマイナンバーは 1234-5678-9012、本番は 5678-9012-3456 です。」

`1234-5678-9012` はマイナンバーのマスキングルールの形にも一致しますが、よく知られたサンプル値なので PASSING テストパターン(id 0)が**先に先取り**し、そのまま通過します。マスキングされるのは `5678-9012-3456` だけです: `テスト用のマイナンバーは 1234-5678-9012、本番は [MY_NUMBER_1] です。` — allowlist ルールが実データルールより小さい id を持つ理由です。

### シナリオ 3 — 具体的な住所だけが機密情報

> 「ミーティングは渋谷駅ハチ公口前のカフェでやりましょう。契約書は東京都千代田区丸の内1丁目9-2 グラントウキョウサウスタワー 21階に送ってください。」

1 つのメッセージに場所への言及が 2 回ありますが、判定は分かれます。`渋谷駅ハチ公口` はランドマークレベルの一般的な言及なので — Description の除外条件と negative example が教えたとおり — 通過します。町名+丁目・番地のある具体的な住所だけがマスキングされ、マスキング範囲は例の値が教えたとおり町名から始まります: `ミーティングは渋谷駅ハチ公口前のカフェでやりましょう。契約書は東京都千代田区 [ADDRESS_1] に送ってください。`

### シナリオ 4 — BLOCK はすべてに勝つ

マスキングルールが 10 個一致していても、BLOCKING ルールが 1 つ一致すれば結果は `"action": "BLOCK"` で、マスキングされた本文は返されません。送信そのものを止めるべき値(社外秘コードネームなど)にだけ BLOCKING を使い、残りは MASKING にするのが良い設計です。

## チェックリスト

* 正規表現に数字・単語境界(`(?<!\d)`、末尾ガード)を入れたか?
* テスト・例示用の値を PASSING ルールで先取りしたか?
* NER の Description に定義 + 具体性・文脈条件 + 除外条件を書いたか?
* Positive Examples を `文脈 →値` 形式で、値がマスキング範囲を正確に指すように書いたか?
* Negative Examples に具体性の足りない近接事例を入れたか?
* BLOCKING は本当に遮断が必要なルールだけに使ったか?

<Tip>
  Starfort には包括的なデフォルト PII ポリシー(韓国の識別子向けに著述)が同梱されており、コンソールの **JSON** エディタで読み込めます — このページがしたように、構造のテンプレートとして使い、ルールを自組織のロケールに合わせて調整してください。コンソールのポリシーページにある **TIP** ボタンでもこのガイドの要約を確認できます。
</Tip>

<Warning>
  ルール数と例の分量はそのまま検知コストです。すべての呼び出しにルール定義の全文が渡されるため、冗長すぎるポリシーはすべてのリクエストを遅くし、極端な場合はモデルの入力上限を超えてリクエストを失敗させます。ルールあたり鋭い例 5〜10 個が、網羅的な羅列に勝ります。
</Warning>
