llm-inspect
LLM アプリケーションの入力と出力を検査する Rust ライブラリと CLI です。
- 入力検査 — 個人情報(PII)、認証情報(シークレット)、プロンプトインジェクションのパターンを検出し、方針に従って「許可/警告/遮断」を判定します。全角文字やゼロ幅文字による回避を防ぐため、照合前に NFKC 正規化と不可視文字の除去を行い、検出位置は元のテキスト上のバイト位置で返します。検出箇所のマスキング(レダクション)もできます。
- 出力検証 — モデルの応答が JSON Schema に適合するか、指定フィールドの値が許可リストに含まれるか、HTML が混入していないか、サイズ上限内かを検証します。
ルールは TOML で書いた正規表現なので、再コンパイルせずに監査・追加・差し替えができます。
ブラウザデモ: https://takahironago.github.io/llm-inspect/ (WebAssembly。入力したテキストはブラウザの外へ送信されません)
v0.1.0 を crates.io に公開しています(2026年11月)。 API は 0.x の間に変わる可能性があります。
検出は完全ではありません。 パターン照合はすべての攻撃や個人情報を捕捉できず、誤検知も起こります。人間の承認、最小権限、監査ログと組み合わせた多層防御の一層として使ってください。
English: llm-inspect is an input/output security inspection library for LLM applications, with a CLI, a WebAssembly demo and an axum HTTP service: PII, secrets and prompt-injection detection with normalization-aware offsets and redaction, plus JSON Schema / allow-list / HTML validation of model output. Rules are TOML-defined regular expressions. Published on crates.io as 0.1.x (API may change before 1.0); detection is heuristic and not exhaustive. API docs: https://docs.rs/llm-inspect.
使い方(ライブラリ)
[]
= "0.1"
use ;
let inspector = with_default_rules;
// 入力の検査
let report = inspector.scan;
assert_eq!;
for f in &report.findings
// マスキング(既定では pii と secret を置き換え、injection は残す)
let redacted = inspector.redact;
assert_eq!;
use ;
use json;
let validator = new?;
let report = validator.validate;
assert!; // /steps/0/action は許可リスト外
Report と Finding はシリアライズ可能ですが、一致したテキストそのものは含みません。検査結果をログに書いたときに、検出した秘密情報や個人情報がログへ漏れるのを防ぐためです。必要な場合だけ Finding::excerpt(&original) で取り出してください。
使い方(CLI)
# 入力の検査(標準入力またはファイル)
|
# decision: Warn
# medium pii pii.phone_jp bytes 12..25
# マスキングした本文を出力
|
# key = [REDACTED:SECRET]
# JSON で出力、独自ルールを追加
# 出力の検証
# 有効なルール一覧
終了コード:0 許可/有効、10 警告、20 遮断/無効、1 引数や入出力のエラー。
使い方(HTTP サービス)
crates/llm-inspect-server は同じコアを axum の REST API として公開します。他言語のアプリケーションからは、この API を呼ぶのが最も簡単です。
# または Docker
| メソッド | パス | 本文 | 応答 |
|---|---|---|---|
| GET | /healthz |
— | {"status":"ok","version":"…"}(認証不要) |
| GET | /rules |
— | 有効なルール・方針・読み込み済み出力ポリシー名 |
| POST | /scan |
{"text":"…"} |
Report |
| POST | /redact |
{"text":"…"} |
Redacted(text と report) |
| POST | /validate |
{"text":"…","policy":{…}} または {"text":"…","policy_name":"…"} |
OutputReport |
- 既定では loopback(127.0.0.1:8080)にのみ待ち受けます。外部へ公開する場合は TLS を終端するリバースプロキシの背後に置き、
LLM_INSPECT_TOKEN(Authorization: Bearer …、定数時間比較)を設定してください。loopback 以外でトークンなしの場合は起動時に警告を出します。 - 本文サイズ上限(既定 1 MiB → 413)、リクエストタイムアウト(既定 10 秒 → 408)、JSON 以外は 415、不正な JSON やポリシーは 400 で、いずれも
{"error":"…"}を返します。 - アクセスログはメソッド・パス・ステータス・所要時間のみで、リクエスト本文は記録しません(本文こそが秘密情報や個人情報だからです)。
- 出力ポリシーは起動時に
--policy NAME=PATHで読み込んでpolicy_nameで参照するか、リクエストにpolicyとしてインラインで渡せます(インラインは毎回スキーマをコンパイルします)。 - ルールは CLI と同じく
--rules FILE(追加)と--no-defaultsで差し替えられます。
ルールの書き方
[]
= "high" # この深刻度以上の検出があれば Block
= "medium" # この深刻度以上(block_at 未満)なら Warn
= ["pii", "secret"] # redact() が置き換えるカテゴリ
[[]]
= "pii.employee_id"
= "pii" # pii | secret | injection
= "medium" # low | medium | high | critical
= "社員番号"
= '''(?:^|[^0-9A-Z])(EMP-[0-9]{6})(?:[^0-9]|$)'''
= "[社員番号]" # 省略時は [REDACTED:PII]
- 正規表現は regex クレート の構文です(後読み・先読みは使えません)。パターンにキャプチャグループがあればグループ 1 を検出箇所として扱うので、「直前が数字でない」といった文脈条件をグループの外に書けます。
validator = "luhn"を付けると、一致した数字列が Luhn チェックを通る場合だけ検出します(クレジットカード番号用)。- CLI の
--rulesは既定ルールに追加し、[policy]は上書きします。--no-defaultsで既定ルールを外せます。 - 既定ルールは
crates/llm-inspect/rules/default.tomlにあります。
既定ルールの概要
| カテゴリ | ルール | 深刻度 |
|---|---|---|
| pii | メールアドレス、日本の電話番号、国際電話番号、IPv4 | low〜medium |
| pii | クレジットカード番号(Luhn 検証付き) | high |
| secret | AWS アクセスキー、GitHub / Slack トークン、PEM 秘密鍵 | critical |
| secret | OpenAI 形式のキー、Google API キー、JWT、api_key = "..." 形式の代入 |
high |
| injection | 「以前の指示を無視」「システムプロンプトを表示」(日本語・英語) | high |
| injection | 無制限ペルソナへの切り替え、チャットテンプレートのロール記号の混入 | medium |
| injection | クエリ付き URL への Markdown 画像・リンク(データ持ち出し経路) | high |
| injection | 不可視文字の混入(obfuscation.invisible_chars。正規化時に自動付与) |
low |
設計メモ
- 正規化と位置の対応 —
Normalizedは 1 文字ずつ NFKC 正規化し、不可視文字(ゼロ幅スペース、双方向制御文字、BOM など)を取り除きながら、正規化後の各バイトが元のどの文字由来かを記録します。検出は正規化後のテキストで行い、結果は元のテキストのバイト範囲(必ず文字境界)に戻します。元のテキストでは一致せず正規化後にだけ一致した検出にはnormalized_only = trueが付き、意図的な回避の強い兆候として扱えます。 - 方針と判定 — 検出の最大深刻度を
block_at/warn_atと比較してAllow/Warn/Blockを決めます。深刻度と閾値はルールファイル側で調整できます。 - 性能 — 既定ルールの単語境界は Unicode の
\bではなく ASCII の(?-u:\b)にしています。Unicode の\bを含むパターンは、日本語など非 ASCII 文字を含むテキストで regex クレートの DFA が使えず約 25 倍遅くなるためです。数値は BENCHMARKS.md にあります(数 KB のプロンプトで 0.1 ms 程度)。 - 出力検証 — JSON Schema は
jsonschemaクレートで検証します(ネットワーク経由の$ref解決は無効)。許可リストのポインタは RFC 6901 に*(配列・オブジェクトの全要素)を加えた形式です。contains_html/strip_htmlは検出とタグ除去のための簡易実装で、HTML サニタイザーの代わりにはなりません。
開発
CI(GitHub Actions)は Ubuntu と Windows で fmt / clippy / test(proptest を含む)/ doc を、Ubuntu で wasm32 ビルド、MSRV(Rust 1.85)ビルド、サーバーの Docker イメージのビルドと起動確認を実行します。main への push で demo/ を GitHub Pages に配信します。ファジングは週 1 回と手動実行、および fuzz/ やコアクレートを変更した PR で各ターゲット 60 秒ずつ回します。
構成
crates/llm-inspect/ コアクレート(rules/default.toml を同梱、benches/ に Criterion)
crates/llm-inspect-cli/ CLI(バイナリ名 llm-inspect)
crates/llm-inspect-wasm/ wasm-bindgen バインディング(JSON 文字列で入出力)
crates/llm-inspect-server/ axum の HTTP API(Dockerfile、tower の oneshot による API テスト)
demo/ ブラウザデモ(静的 HTML / JS。pkg/ はビルド生成物)
fuzz/ cargo-fuzz のターゲットとシードコーパス(ワークスペース外)
ロードマップ
- コアクレート(入力検査・レダクション・出力検証)、CLI、テスト、CI
- WebAssembly ビルド(wasm-bindgen)とブラウザデモ(GitHub Pages)
- ベンチマーク(criterion)→ BENCHMARKS.md
- プロパティテスト(proptest)とファジング(cargo-fuzz、週次 CI)
- axum による HTTP サービス(Docker イメージ、ベアラートークン、本文サイズ・タイムアウト制限)
- crates.io への公開(v0.1.0、2026-11-10。コアクレートのみ)
ライセンス
MIT と Apache-2.0 のデュアルライセンスです(LICENSE-MIT、LICENSE-APACHE)。