TypeScript で state を最小化する
typescript// lib/pii-minimize.ts — redact direct identifiers before the Jev call.
// A routing decision keys off what the ticket SAYS, not who said it:
// queue "billing" does not need a name, an email, or a card number.
const PATTERNS: [RegExp, string][] = [
[/[\w.+-]+@[\w-]+\.[\w.]+/g, '[email]'],
[/\b(?:\d[ -]*?){13,16}\b/g, '[card]'],
[/\+?\d[\d\s().-]{7,}\d/g, '[phone]'],
];
// Salted FNV-1a: the same customer id maps to the same stable token, so
// decisions stay correlatable in YOUR logs while the identifier itself
// never leaves your system.
function pseudonym(value: string): string {
const input = value + (process.env.PSEUDONYM_SALT ?? '');
let h = 2166136261;
for (let i = 0; i < input.length; i++) {
h ^= input.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return 'cust_' + (h >>> 0).toString(36);
}
export function minimizeState(state: string): string {
let out = state.replace(/customer[ _#]?\d+/gi, (m) => pseudonym(m));
for (const [pattern, tag] of PATTERNS) out = out.replace(pattern, tag);
return out;
}
// Usage — one line before the existing call:
// const state = minimizeState(rawTicketBody);
// await evaluate({ model: 'jev-latest', state, questions });これらの正規表現はあくまで例示であり、コンプライアンス管理ではありません。典型的な直接識別子は捕らえますが、文脈型の PII(文章として書かれた住所、自由記述の健康情報)は見逃します。規制対象のトラフィックでは、本物の検出器(Microsoft Presidio など)を通すか、state を構造化済みの許可リストのフィールドに限定してから、出力を信頼してください。