27 - PII检测与数据安全

Prompt Guard 中的个人隐私信息检测、策略化处理、输入/输出扫描链路

一、什么是 PII 检测?

PII 的定义

PII(Personally Identifiable Information)指能够单独或与其他信息结合后识别特定个人身份的数据。常见 PII 包括身份证号、手机号、银行卡号、邮箱地址、姓名、住址、IP 地址等。

结合当前项目实现,要把通用概念和已内置能力分开讲:当前内置手机号、身份证号、银行卡号、邮箱地址;IPv4 地址可配置开启;自定义规则可通过代码扩展;中文姓名和住址暂不内置,建议作为自定义规则或后续 NER 增强项。

为什么在 AI 系统中尤为重要?

面试讲法:不要只说“用了正则脱敏”。更稳的说法是:PII 属于 Prompt Guard 的一部分,和提示词注入防护一起构成输入/输出安全链路;当前内置规则覆盖高确定性 PII,低确定性实体留给自定义规则或 NER 扩展。

二、检测规则体系

当前内置模式匹配规则

PII 类型模式特征示例实现要点
手机号11 位,1[3-9] 开头13812345678使用数字边界避免匹配更长数字串
身份证号18 位身份证格式11010519900307001X校验出生日期格式,并按 GB 11643 校验位过滤
银行卡号16-19 位数字6222021234567890123使用 Luhn 校验降低订单号、流水号误报
邮箱地址标准 email 格式user@example.com匹配常见邮箱用户名和域名结构
IP 地址IPv4 地址192.168.1.10默认关闭,通过 detect-ip 开启
自定义业务自定义正则会员号、工号等通过 PIIDetectors.fromPattern(...) 扩展

当前注册方式

项目没有单独的 PiiPatterns 静态表,也没有内置 PiiType.NAME。PII 检测器由 PromptGuardAutoConfiguration 根据配置组装:

@Bean
@ConditionalOnProperty(
    prefix = "hub.prompt-guard.pii",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = true
)
public PIIScanner piiScanner(PromptGuardProperties properties) {
    PromptGuardProperties.Pii piiConfig = properties.getPii();
    List<PIIDetector> detectors = new ArrayList<>();

    if (piiConfig.isDetectPhone()) {
        detectors.add(PIIDetectors.phone());
    }
    if (piiConfig.isDetectIdCard()) {
        detectors.add(PIIDetectors.idCard());
    }
    if (piiConfig.isDetectBankCard()) {
        detectors.add(PIIDetectors.bankCard());
    }
    if (piiConfig.isDetectEmail()) {
        detectors.add(PIIDetectors.email());
    }
    if (piiConfig.isDetectIp()) {
        detectors.add(PIIDetectors.ipAddress());
    }

    return new PIIScanner(detectors, properties);
}

三、处理策略

四种 RedactionStrategy

策略效果示例适用场景
BLOCK拦截请求或响应高敏感场景,检测到 PII 就拒绝继续处理
REDACT[手机号] / [身份证号]保留语义位置,便于 LLM 理解“这里有一个手机号”
MASK138****5678保留少量前后缀,方便用户核对
HASH[hash:3f2a...]需要稳定指纹但不暴露原值的审计或去重场景

策略选择逻辑

  检测到 PII
       │
       ├── block-on-detection = true
       │     └── InputScanner 返回 failed,调用方直接拒绝请求
       │
       └── block-on-detection = false
             ├── PIIScanner 生成 redactedText
             ├── 调用方可使用 redactedText 替换原文后继续
             └── OutputScanner 命中后交给 OutputSanitizer 做最终清洗
当前工程边界:InputScanResult.safeWithPii(...) 已携带脱敏后的 redactedText,但 Web/IM 调用链主要消费的是 isPassed()。因此不要把现状讲成“所有输入都会自动替换后再进入 LLM”。如果要严格做到“PII 不入模”,应开启 block-on-detection,或在调用链中显式使用 getPiiResult().getRedactedText() 替换原消息。

四、输入/输出扫描架构

InputScanner + OutputScanner

当前 PII 能力不是独立的 PiiDetectionHook,而是接入 Prompt Guard 的 InputScannerOutputScanner

  PII 扫描链路:

  User Input
     │
     ▼
  ┌────────────────────────────┐
  │ InputScanner               │
  │ 1. 限流检查                │
  │ 2. 输入注入/黑名单/长度检查 │
  │ 3. PIIScanner              │
  │    ├── 手机号检测          │
  │    ├── 身份证检测          │
  │    ├── 银行卡检测          │
  │    ├── 邮箱检测            │
  │    └── IP 检测(可选)       │
  └──────────────┬─────────────┘
                 ▼
          Agent / LLM / Tool
                 │
                 ▼
  ┌────────────────────────────┐
  │ OutputScanner              │
  │ 1. 金丝雀令牌泄露检测       │
  │ 2. API Key / Bearer 检测    │
  │ 3. PIIScanner              │
  │ 4. OutputSanitizer          │
  └──────────────┬─────────────┘
                 ▼
               User

为什么需要双向扫描?

五、与提示词注入防护的协作

PII 检测 vs 提示词注入防护:

PII 检测聚焦隐私保护,检测个人信息模式,防止数据泄露,保护对象主要是用户。

提示词注入防护聚焦系统安全,检测攻击指令、系统提示词泄露、工具滥用等风险,保护对象主要是系统边界。

执行顺序

  InputScanner:
  1. 注入限流
  2. 输入检查: 长度 / 关键词黑名单 / 正则攻击模式
  3. PIIScanner
  4. 按 block-on-detection 决定阻断或返回 PII 结果

  OutputScanner:
  1. 金丝雀令牌泄露检测
  2. 系统上下文 / API Key / Bearer Token 敏感模式检测
  3. PIIScanner
  4. OutputSanitizer 清洗最终文本

六、配置示例

# application.yml — 当前项目实际配置前缀
hub:
  prompt-guard:
    pii:
      enabled: true
      strategy: MASK          # BLOCK / REDACT / MASK / HASH
      detect-phone: true
      detect-id-card: true
      detect-bank-card: true
      detect-email: true
      detect-ip: false
      block-on-detection: false

建议面试时补一句:detect-ip 默认关闭,是为了避免内网地址、示例地址、日志片段造成误报;中文姓名和住址不内置,是因为它们需要上下文和实体识别能力,纯正则误杀率很高。

七、面试高频问题

Q1: PII检测和提示词注入防护有什么区别?
A: PII 防数据泄露,提示词注入防系统被操控。PII 看的是手机号、身份证、银行卡、邮箱这类数据形态;注入防护看的是“忽略之前指令”“泄露系统提示词”等攻击语义和敏感模式。当前项目把两者都放在 Prompt Guard 中,但职责不同。
Q2: 正则表达式检测PII的局限性是什么?有什么补充方案?
A: 普通数字串可能误判,空格/全角/分隔符变形可能绕过,姓名和地址这类实体缺少上下文很难靠正则准确判断。当前项目通过身份证校验位、银行卡 Luhn 校验、数字边界等方式减少误报。后续增强可以加入文本归一化、上下文关键词、NER 命名实体识别和业务白名单。
Q3: REDACT、MASK、HASH、BLOCK 应该怎么选?
A: 如果场景要求 PII 绝不进入模型,优先开启 block-on-detection 或使用 BLOCK 语义直接拒绝。需要保留语义位置时用 REDACT,例如 [手机号]。需要用户核对时用 MASK,例如 138****5678。需要审计去重但不能暴露原文时用 HASH。
Q4: 如果用户主动要求“帮我记住我的手机号是xxx”,应该怎么处理?
A: 不要默认把原始手机号写入长期记忆。更合理的策略是先明确用户授权和业务必要性,再决定是否存储;如果只是普通对话,应阻断或脱敏。若确实要保存,应该限制用途、记录审计、提供删除能力,并避免把原值再次注入 LLM 上下文。当前项目没有把“PII 长期加密存储”做成通用机制,面试时不要夸大。
Q5: 为什么当前不内置中文姓名和地址检测?
A: 中文姓名和地址强依赖上下文。2-4 个汉字在中文文本中非常常见,纯正则会把“人工智能”“数据分析”之类普通词误判为姓名;地址也会和机构名、景点、公司地址示例混在一起。因此当前项目只内置高确定性的 PII,把姓名、地址留给自定义规则、业务白名单和 NER 增强。
Q6: 输出扫描的必要性是什么?
A: LLM 可能复述用户之前输入的 PII,也可能根据工具结果输出第三方 PII,还可能生成格式上像真实 PII 的内容。输出扫描是最后一道防线:即使输入侧没有阻断,最终返回用户前仍能识别并清洗高风险内容。
Q7: SSE 流式输出场景下,当前怎么做PII检测?
A: 当前更准确地说是“最终响应/聚合完成后扫描”。Web 和通道链路会在最终文本阶段调用输出扫描或清洗逻辑。逐 chunk 滑动窗口检测是合理演进方向,但不是当前已经完整落地的实现。若要做实时流式清洗,需要维护跨 chunk 缓冲区,并在最终完整响应上再做一次兜底扫描。
Q8: PII检测的性能开销如何控制?
A: 当前内置检测以预编译正则和校验算法为主,复杂度接近线性。主要优化点包括:正则复用、长度上限保护、身份证/银行卡二次校验、默认关闭误报较高的 IP 检测,以及把姓名/地址这类高成本语义识别留给按需扩展。