コンテンツへスキップ
Tutorial

AI エージェントのログから PII がデータベースに到達する前に秘匿化します。

| 7 min read

エージェントはすべてのプロンプトとツール呼び出しを記録します。 トランスクリプト内の 1 つの SSN の欠落は、GDPR の開示に変わります。 3 行のミドルウェアは、行が書き込まれる前にこの問題を修正します。

Padlock representing PII redaction and data protection in AI agent logs
Photo by FLY:D on Unsplash

AI エージェントはプロンプトを記録します。 ツール呼び出し入力。 ツールの結果。 最終的な返答。 これはかつてデバッグ データの宝庫でした。 今日では、検証を過ぎた SSN をサポート チケットが送信するのを待っている GDPR 開示が行われています。

ユーザーによる機密データの貼り付けを阻止することはできません。 生のテキストがログ ストア、可観測性ベンダー、および毎週の評価エクスポートに到達するのを阻止できます。 小さなミドルウェアはそれを 1 ホップで実行します。

ほとんどのエージェント設定ですでに発生しているリーク

// before: every raw prompt, tool call, and tool result lands in the log row
logger.info({
  event: 'agent.turn',
  prompt: userInput,
  tool_calls: toolCalls,
  tool_results: toolResults,
});

// one support ticket later: "My SSN is 123-45-6789 and card 4111 1111 1111 1111"
// sits in the logs, the observability vendor, and the weekly eval export.

各行にカード番号が含まれるようになりました。 ログ ストアには 30 日間保存されます。 オブザーバビリティ SDK はサードパーティに配布します。 評価エクスポートでは 2 日後に同じ文字列が取得されます。 1 枚のカードを 5 枚コピーします。 これらはすべて、保存時の暗号化ポリシーの範囲外です。

1 回の通話で PII を検出

ザ・ボトイ /v1/pii/detect エンドポイントは、電子メール、電話番号、SSN、クレジット カード (Luhn 検証済み)、IP アドレス、生年月日のテキストをスキャンします。 開始オフセット、終了オフセット、およびその場にドロップできるマスクされた値を含む各結果が返されます。

リクエスト

curl -X POST https://api.botoi.com/v1/pii/detect \\
  -H "Content-Type: application/json" \\
  -d '{"text": "Reach me at alice@example.com or 555-123-4567. Card: 4111 1111 1111 1111."}'

応答

{
  "found": true,
  "count": 3,
  "findings": [
    { "type": "email",       "value": "alice@example.com",      "start": 12, "end": 29, "masked": "al***@example.com" },
    { "type": "phone",       "value": "555-123-4567",           "start": 33, "end": 45, "masked": "***-***-4567" },
    { "type": "credit_card", "value": "4111 1111 1111 1111",    "start": 53, "end": 72, "masked": "************1111" }
  ]
}

3 つの一致、3 つのマスクされた置換、きれいに接続できる位置。 保守するための正規表現ライブラリも、最新に保つための SSN プレフィックス テーブルも、自分で作成するための Luhn パスも必要ありません。

書き込み前に編集するログミドルウェア

これに適切な場所は、行がプロセスを離れる前の最後のホップです。 すべての上流コンポーネントは、必要な生のテキストを引き続き参照します。 永続化されたコピーはサニタイズされます。

// log-redact.ts
import type { LogRecord } from './types';

const PII_FIELDS = ['prompt', 'tool_calls', 'tool_results', 'output'] as const;

export async function redactPii(record: LogRecord): Promise<LogRecord> {
  const clone = structuredClone(record);
  for (const field of PII_FIELDS) {
    const value = clone[field];
    if (!value) continue;
    clone[field] = await scrub(JSON.stringify(value));
  }
  return clone;
}

async function scrub(text: string): Promise<string> {
  const res = await fetch('https://api.botoi.com/v1/pii/detect', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: \`Bearer \${process.env.BOTOI_API_KEY}\`,
    },
    body: JSON.stringify({ text }),
  });
  const data = await res.json();
  if (!data.found) return text;

  // Replace from the end of the string so offsets stay valid.
  const sorted = [...data.findings].sort((a, b) => b.start - a.start);
  let scrubbed = text;
  for (const f of sorted) {
    scrubbed = scrubbed.slice(0, f.start) + f.masked + scrubbed.slice(f.end);
  }
  return scrubbed;
}

このスニペットでは 3 つの詳細が重要です。 まず、レコード全体ではなく、既知の PII の多いフィールドを歩きます。 リクエスト ID をスクラブする必要はありません。 次に、API に送信する前に各フィールドを単一の文字列にシリアル化するため、1 回の呼び出しでツールの結果全体がカバーされます。 3 番目に、文字列の末尾から置換を結合して、その下でオフセットが移動しないようにします。

ロガーに配線します

// logger.ts
import { redactPii } from './log-redact';

export async function logTurn(raw: LogRecord) {
  const safe = await redactPii(raw);
  await logStore.write(safe);
}

// anywhere in your agent loop:
await logTurn({
  event: 'agent.turn',
  prompt: userInput,
  tool_calls: toolCalls,
  tool_results: toolResults,
});

電話 logTurn 既存の代わりに logger.info ターン境界で。 上流ではすべてが同じままです。

フェイルサイレントではなくフェイルクローズ

検出エンドポイントは通常、20 ミリ秒以内に応答します。 タイムアウトになった場合でも、行を生のままログに記録するか (リークのリスク)、機密フィールドを削除してマーカーをログに記録するかの選択肢があります。 コンプライアンスに敏感なワークロードの場合、削除はより安全なデフォルトです。

async function redactPiiSafe(record: LogRecord): Promise<LogRecord> {
  try {
    return await Promise.race([
      redactPii(record),
      new Promise<LogRecord>((_, reject) =>
        setTimeout(() => reject(new Error('pii-detect timeout')), 250)
      ),
    ]);
  } catch (err) {
    // Fail closed: drop the sensitive fields rather than logging them raw.
    return { ...record, prompt: '[REDACT_FAILED]', tool_calls: [], tool_results: [] };
  }
}

タイムアウトを小さい値に設定します。 250 ミリ秒は、健全なリクエスト パスをブロックすることなく、地域の速度低下を吸収するのに十分です。

Pythonのバージョン

# log_redact.py
import os, json, httpx

PII_FIELDS = ('prompt', 'tool_calls', 'tool_results', 'output')
API = 'https://api.botoi.com/v1/pii/detect'

async def scrub(text: str) -> str:
    async with httpx.AsyncClient(timeout=0.25) as client:
        r = await client.post(
            API,
            headers={'Authorization': f"Bearer {os.environ['BOTOI_API_KEY']}"},
            json={'text': text},
        )
    data = r.json()
    if not data.get('found'):
        return text
    out = text
    for f in sorted(data['findings'], key=lambda x: x['start'], reverse=True):
        out = out[:f['start']] + f['masked'] + out[f['end']:]
    return out

落とす scrub エージェント フレームワークのログ フックに追加します。 FastAPI ミドルウェア、LangChain コールバック、および OpenInference スパン エクスポーターはすべて、非同期関数を受け入れます。

このミドルウェアができないこと

  • サポートされているタイプに似ていない名前、住所、口座番号は検出されません。 これらには、名前付きエンティティ モデルとポリシー決定 (マスク?削除?ターン全体を編集?) が必要です。
  • モデルのベンダーが生のプロンプトを表示することを防ぐことはできません。 そのためには、モデルに送信する前に、クライアント上で同じ検出呼び出しを実行します。
  • データ保持ポリシーに代わるものではありません。 とにかくログ TTL を短くしてください。

これが属する 2 つの場所

守る 通話のタイミング
モデルリクエスト前 モデルベンダー、トレーニングデータ、評価リーク ブロッキング、ユーザーに見える遅延
ログ書き込み前 ログストア、可観測性ベンダー、エクスポート 帯域外、ユーザーには見えない

最初にログ書き込みミドルウェアを出荷します。 ホットパスの外側で動作し、最も一般的なリークパターンをブロックします。 ログ側がカバーされたら、プレモデル バージョンを追加します。

APIキーを取得して開始する

匿名アクセスでは、1 分あたり 5 つのリクエストが発生します。これは、サンプル ログに対してエンドポイントを試すのに十分です。 実稼働ミドルウェアの場合は、次の場所で無料キーを取得してください。 botoi.com/api/signup。 無料枠では、クレジット カードなしで 1 日あたり 1,000 件のスクラブ コールがカバーされます。

完全なエンドポイント リファレンスについては、次の URL を参照してください。 PII 検出 API ページ または閲覧する api.botoi.com/docs 他の 149 のエンドポイントについては。

FAQ

AI エージェントのログから通常のサーバーのログよりも多くの PII が漏洩するのはなぜですか?
エージェントは、プロンプト全体、すべてのツール呼び出し入力、およびすべてのツール出力をログに記録します。 かつては「ログを記録しない」フラグの背後に存在していたサポート トランスクリプトは、オーケストレーター、ツール サーバー、オブザーバビリティ ベンダー、モデル プロバイダー、トレーニング評価セットの 5 つの場所に表示されるようになりました。
編集ステップはどこで実行する必要がありますか?
行がログ ストアに送信される直前に、ログ ライターの境界で実行します。 そうすることで、すべての上流コンポーネント (オーケストレーター、ツール、可観測性 SDK) が必要な生のテキストを認識し、永続化されたコピーのみがサニタイズされます。
正規表現リダクターはすべてをキャッチしますか?
いいえ、ロールユアオウン正規表現では、異常な間隔のクレジット カード番号、他の 9 桁の番号に似た SSN、および人名が検出されません。 /v1/pii/detect のような API はカード上で Luhn を実行し、SSN プレフィックスをフィルタリングして位置を返すため、行全体ではなく一致するものだけを削除できます。
Botoi PII Detect API により追加されるレイテンシーはどれくらいですか?
エンドポイントはエッジで実行され、500 トークンのペイロードの場合は 20 ミリ秒未満で戻ります。 ユーザーに表示される応答時間に影響を与えることなく、ログ ミドルウェアで同期的に呼び出すことができます。 ロギングは応答が送信された後に行われます。
モデルに送信する前にクライアントで編集できますか?
はい、それは優れた 2 番目のレイヤーです。 サーバーミドルウェアで編集すると、ログストアが保護されます。 クライアントで編集すると、モデルベンダーが生の PII を参照することがなくなります。 両方を組み合わせると、GDPR に適した設定になります。

botoiで開発を始めよう

150以上のAPIエンドポイント。検索、テキスト処理、画像生成、開発者ユーティリティに対応。無料プラン、クレジットカード不要。