文章

LLM Prompt Data Privacy 生产实战:用 PII 脱敏、保留策略与审计边界管住提示词数据

系统拆解大模型应用中的提示词数据隐私治理,覆盖 PII 脱敏、保留策略、审计日志、供应商边界与上线检查清单,帮助企业将隐私治理从一句“不会用于训练”落地为可运行的工程体系。

背景:提示词本身就是新的敏感数据入口

企业把大模型接入客服、知识库、合同审查、投保咨询、代码助手或内部运营系统后,提示词本身就变成了新的敏感数据入口。用户可能把姓名、手机号、证件号、地址、订单号、病历、合同条款、内部系统字段、客户画像和业务审批意见直接贴进对话框。工程团队如果只关注模型准确率、响应延迟和调用成本,很容易忽略一个更基础的问题:这些 Prompt 和 Response 在进入模型、日志系统、排障平台、评估数据集、缓存层和供应商边界时,到底被谁看到、保存多久、能否删除、是否会被二次使用。

这也是为什么”供应商承诺不使用业务数据训练模型”不能等同于”系统已经完成数据隐私治理”:

  • OpenAI 的企业隐私说明中提到,业务数据默认不用于训练模型,API 输入输出通常可能被安全保留最多 30 天用于提供服务和识别滥用,符合条件的场景可申请 Zero Data Retention。
  • Azure 文档明确说明 prompts、completions、embeddings、training data 不会提供给其他客户或模型提供商,也不会在未经许可的情况下用于训练生成式 AI 基础模型。
  • Google Cloud 的 Gemini Enterprise Agent Platform 文档进一步列出了要实现 zero data retention 时必须关注的 prompt logging、grounding、request-response logging 和 in-memory caching 等具体场景。

换句话说,Prompt Data Privacy 不是一句”不会拿去训练”就结束,而是一个围绕数据最小化、脱敏、保留、访问、审计和删除展开的生产工程问题。

核心原理

1. 把 Prompt 当作一类数据资产,而不是临时字符串

传统 Web 系统里,用户输入通常会进入表单校验、权限控制、日志脱敏和数据库权限体系。但很多 LLM 应用在早期原型中会直接把用户输入拼进 Prompt,然后传给模型 API。这种做法在 Demo 阶段很快,在生产阶段风险很高。

更合理的抽象是把一次模型调用拆成几个数据对象:

数据对象说明敏感度
Raw Prompt用户原始输入最高
Normalized Prompt经过模板化、格式化、上下文拼接后的请求
Sanitized Prompt经过脱敏、替换、裁剪或策略过滤后的模型输入
Model Response模型输出,可能回显或生成敏感内容中高
Audit Metadata调用元数据(租户、模型、策略版本、脱敏命中类型等)低中

生产系统应该尽量让 Raw Prompt 的生命周期最短,真正跨边界流转的是 Sanitized Prompt 和 Audit Metadata。

2. PII 检测不是单一正则,而是多层识别

PII 检测通常要组合三类能力:

  • 确定性规则:邮箱、手机号、身份证号、银行卡、URL token、API key、订单号。适合用正则、校验和、前后缀规则和业务字段白名单识别。
  • 命名实体识别(NER):人名、地址、组织名称、地名、医疗机构、车牌、病历号。对自然语言更有效,但存在误报和漏报。
  • 上下文相关判断:同样是”张三”,在”客户张三的身份证号是……”中通常是 PII,在”张三丰是文学人物”中未必需要脱敏。同样是订单号,在售后查询中可能是必要上下文,在通用问答中可能只需保留格式占位符。

Presidio 将其定位为识别和匿名化文本、图像中敏感实体的工具,支持预定义和自定义 PII recognizer,组合 NER、正则、规则逻辑、校验和与上下文;但它也明确提醒,自动检测机制不能保证找到所有敏感信息,仍需要其他系统和保护措施配合。

3. 脱敏不只有删除,还有伪名化和可逆映射

很多团队一开始会把隐私治理理解成”把敏感字段全部替换成 [REDACTED]”。这在日志系统中通常可行,但在模型交互中可能破坏任务语义。

例如用户问:

帮我写一段短信,通知李明,订单 A202607031029 已审核通过,尾号 9832 的银行卡会在 3 个工作日内收到退款。

如果粗暴删除所有敏感字段,模型只会看到:

帮我写一段短信,通知 [NAME],订单 [ORDER_ID] 已审核通过,尾号 [CARD] 的银行卡会在 3 个工作日内收到退款。

这种处理适合生成模板,但不适合直接生成最终通知文本。更好的方式是按场景选择:

策略说明适用场景
Mask只保留尾号或局部信息,如 138****9021日志展示、客服界面
Tokenize用稳定占位符替换,如 PERSON_1ORDER_1需要保持实体一致性
Surrogate用同类型假值替代,如虚构姓名、同城市虚构地址需要保留任务语义
Encrypt Mapping本地保存”占位符→原值”的加密映射需要可逆恢复
Block直接拒绝或改走内部模型密钥、密码、完整证件号、医疗隐私

最近的 SurrogateShield 论文也讨论了纯占位符脱敏会降低语义连贯性,而类型一致的 surrogate substitution 可以在不把真实 PII 发送到第三方端点的前提下保留更多任务语义。这说明了一个重要工程方向:隐私保护不只是在”保留原文”和”全部删除”之间二选一

工程落地

1. 建立 Prompt Privacy Boundary

最小可行架构可以这样拆分:

Client / App → Prompt Intake → PII Detector → Privacy Policy Engine
→ Prompt Transformer → Model Provider → Response Scanner
→ Audit Logger → Retention Worker

其中关键边界是 Prompt Transformer 到 Model Provider。在这个边界之前,系统可以处理 Raw Prompt;跨过这个边界后,应该只发送 Sanitized Prompt。

2. 定义策略,而不是把规则写死在代码里

建议把隐私策略做成版本化配置。示例:

policy_id: prompt-privacy-v1
scope:
  tenant: default
  app: customer-support-agent
input_rules:
  EMAIL_ADDRESS: mask
  PHONE_NUMBER: mask
  CREDIT_CARD: block
  API_KEY: block
  PERSON: tokenize
  LOCATION: tokenize
  ORDER_ID: keep_partial
output_rules:
  CREDIT_CARD: block
  API_KEY: block
  PERSON: allow_if_from_input_mapping
retention:
  raw_prompt: none
  sanitized_prompt: 7d
  response: 7d
  audit_metadata: 180d
logging:
  store_raw_prompt: false
  store_sanitized_prompt: true
  store_detector_spans: false
  store_entity_types: true
approval:
  require_security_review: true

这类配置至少要记录 policy_idversioneffective_timeownerreviewerrollback_policy_id。一旦线上出问题,团队才能知道某次模型调用到底使用了哪一版脱敏策略。

3. 调用前拦截与调用后复查都要做

只做输入脱敏不够。模型输出可能出现三类问题:

  • 回显用户输入中的敏感字段。
  • 把工具返回结果中的敏感字段带入回复。
  • 生成看似合理但不应该暴露的个人信息或内部信息。

因此建议至少执行两次扫描:

from typing import Dict, Any

BLOCK_TYPES = {"API_KEY", "CREDIT_CARD", "PASSWORD"}
MASK_TYPES = {"EMAIL_ADDRESS", "PHONE_NUMBER"}
TOKENIZE_TYPES = {"PERSON", "LOCATION", "ORDER_ID"}


def sanitize_prompt(raw_text: str, detector, vault) -> Dict[str, Any]:
    findings = detector.analyze(raw_text)
    transformed = raw_text
    mapping = {}
    for item in sorted(findings, key=lambda x: x.start, reverse=True):
        value = raw_text[item.start:item.end]
        if item.entity_type in BLOCK_TYPES:
            raise ValueError(f"Blocked sensitive entity: {item.entity_type}")
        if item.entity_type in MASK_TYPES:
            replacement = mask_value(value)
        elif item.entity_type in TOKENIZE_TYPES:
            replacement = vault.create_token(item.entity_type, value)
            mapping[replacement] = vault.mapping_id(replacement)
        else:
            replacement = "[REDACTED]"
        transformed = (
            transformed[:item.start] + replacement + transformed[item.end:]
        )
    return {
        "sanitized_prompt": transformed,
        "entity_types": sorted({x.entity_type for x in findings}),
        "mapping_ids": mapping,
    }


def scan_response(response_text: str, detector) -> str:
    findings = detector.analyze(response_text)
    for item in findings:
        if item.entity_type in BLOCK_TYPES:
            raise ValueError("Model response contains blocked sensitive data")
    return response_text

这段代码只是表达结构,不代表可以直接覆盖所有 PII 场景。生产实现还要处理多语言、业务字典、重叠实体、误报、流式输出、工具调用和用户授权。

4. 审计日志要记录”足够排障”,不要记录”全部原文”

很多隐私事故不是模型本身造成的,而是日志系统、埋点系统、客服工单、A/B 实验平台或异常堆栈把原文保存了下来。

建议把日志拆成两类:

可长期保存的元数据

{
  "request_id": "req_20260703_0001",
  "tenant_id": "tenant_a",
  "app_id": "support_agent",
  "model": "gpt-5.5",
  "policy_id": "prompt-privacy-v1",
  "entity_types": ["PERSON", "PHONE_NUMBER"],
  "action_summary": {"mask": 1, "tokenize": 1, "block": 0},
  "prompt_hash": "sha256:...",
  "response_hash": "sha256:...",
  "status": "success",
  "created_at": "2026-07-03T02:58:37-04:00"
}

短期、受控、可删除的排障材料:sanitized prompt、模型响应、脱敏前后 diff。它们要有单独的 retention policy、访问审批、导出控制和删除任务。

以下位置尤其不要把 Raw Prompt 默认打进去

  • HTTP access log 的 query/body dump
  • SDK debug log
  • 异常堆栈中的 request payload
  • APM trace attribute
  • Prompt playground 历史记录
  • 离线评估数据集自动采样
  • 工单系统中的复制粘贴内容

5. 供应商策略要做成能力矩阵

不同模型供应商和不同产品形态的数据策略不完全一样,不能只看一句”not used for training”。建议维护一张能力矩阵:

ProviderProductTraining UseRetentionAbuse MonitoringRequest LoggingData ResidencyZDR SupportNotes
OpenAIAPIDefault NoUp to 30d typicalYesFeature dependentProject/location optionsEligible endpointsVerify endpoint eligibility
AzureFoundry ModelsNo without permissionService/feature dependentYesFeature dependentAzure regionContractual/config dependentOpenAI provider cannot access Azure-hosted prompts
GoogleGemini Enterprise Agent PlatformNo without permissionScenario dependentPrompt logging may applyDisabled by defaultLocation dependentRequires actions/exceptionsGrounding features may retain data

矩阵要进入架构评审和采购评审,而不是只放在法务文档里。对工程团队来说,更重要的是把它变成运行时策略:某些租户只能使用 ZDR 兼容端点,某些业务线禁止开启 request-response logging,某些场景必须走私有部署或本地模型。

适用场景

这套方案适合以下场景:

  • 客服、售后、理赔、金融、医疗、教育等高敏感业务。
  • 多租户 SaaS 中不同客户有不同数据保留要求。
  • 企业内部知识助手会访问合同、客户资料、工单、邮件或代码库。
  • Agent 会调用 CRM、ERP、工单、支付、搜索等外部工具。
  • 团队需要把线上请求采样为评估数据集,但又不能直接保存原始用户输入。
  • 公司需要同时使用多个模型供应商,且每个供应商的数据保留和日志策略不同。

如果只是公开内容生成、低敏感度营销文案或完全匿名输入,这套系统可以简化,但仍建议至少保留基础脱敏和日志最小化。

常见误区

误区一:只要供应商不训练数据,就没有隐私问题

不用于训练只覆盖一个风险点。Prompt 仍可能进入临时保留、滥用监控、调试系统、日志系统、企业内部审计、法务保留或下游工具调用。真正的治理目标是控制数据生命周期,而不是只确认训练用途。

误区二:PII 检测命中率越高越好

过高的召回率可能带来大量误报,导致模型输入被过度破坏。生产系统需要在隐私风险任务可用性之间设定策略。例如密钥、密码、完整证件号应该高强度阻断;普通姓名和地名则需要结合业务上下文判断。

误区三:脱敏后就可以无限期保存

脱敏不等于匿名化,更不等于永久可保存。占位符、哈希、局部掩码、伪名化映射都有被关联还原的可能。即使保存的是 sanitized prompt,也应该有保留期限、访问控制和删除流程。

误区四:只需要保护输入,不需要保护输出

模型输出可能回显输入,也可能把工具结果、检索结果或历史上下文中的敏感内容带出来。响应扫描、流式输出拦截和工具结果脱敏都应纳入同一个策略体系。

上线检查清单

数据入口

  • 是否识别所有进入 LLM 的字段:用户输入、系统 Prompt、RAG 上下文、工具结果、文件内容、图片 OCR、语音转写。
  • 是否区分 Raw Prompt、Sanitized Prompt、Response、Audit Metadata。
  • 是否禁止在 debug log 中直接打印原始请求体。

脱敏策略

  • 是否覆盖邮箱、手机号、证件号、银行卡、地址、姓名、订单号、API Key、密码、内部 ID。
  • 是否支持业务自定义 recognizer。
  • 是否按租户、应用、模型、数据类型配置策略。
  • 是否有策略版本、审批、灰度和回滚机制。

供应商边界

  • 是否确认模型供应商的数据训练、保留、滥用监控、request logging 和 data residency 策略。
  • 是否明确哪些端点支持 Zero Data Retention 或类似能力。
  • 是否对 grounding、file upload、batch、assistant/thread、vector store、缓存等状态化功能单独评估。

日志与审计

  • 是否默认保存元数据而不是原文。
  • 是否对 sanitized prompt 设置短期保留。
  • 是否对原文排障访问设置审批和过期时间。
  • 是否支持按 request_id 删除或追踪数据。

评估与回归

  • 是否准备 PII 检测测试集。
  • 是否对误报、漏报、回答质量下降分别度量。
  • 是否在上线前回放真实脱敏样本或合成样本。
  • 是否监控 PII 命中率、阻断率、策略拒绝率、人工申诉率和恢复失败率。

总结

Prompt Data Privacy 不是一个”做完就结束”的安全功能,而是一套贯穿 LLM 应用全生命周期的工程实践。它的核心在于:

  1. 定义边界:明确 Raw Prompt 的生命周期,让 Sanitized Prompt 和 Audit Metadata 成为跨边界流转的主体。
  2. 分层检测:组合确定性规则、NER 和上下文判断,而不是依赖单一正则。
  3. 按场景脱敏:在删除、掩码、伪名化、surrogate 替换和加密映射之间做选择,而不只是一刀切。
  4. 策略版本化:让脱敏规则可追溯、可审批、可回滚。
  5. 日志最小化:默认只保存元数据,原文排障走受控通道。
  6. 供应商对齐:把供应商数据策略变成运行时约束,而不是仅停留在法务文档中。

参考资料

  1. OpenAI Enterprise Privacy
  2. Microsoft Azure Foundry Models: Data, privacy, and security
  3. Google Cloud Gemini Enterprise Agent Platform and zero data retention
  4. NIST AI Risk Management Framework
  5. Presidio Documentation
  6. Presidio Analyzer
  7. SurrogateShield: Beyond Redaction for High-Utility, Privacy-Preserving LLM Interactions

常见问题

只要模型供应商承诺不训练客户数据,是否就不需要做 PII 脱敏?
不是。不用于训练只解决训练用途问题,仍然要处理请求日志、调试、abuse monitoring、内部审计、法务保留和下游工具调用中的数据暴露风险。
PII 脱敏应该放在应用层、网关层还是模型调用 SDK 层?
建议采用分层设计:应用层做业务字段识别,网关层做统一策略和审计,SDK 层做兜底检测与调用前拦截。
所有敏感信息都应该直接删除吗?
不一定。生产系统更常用的是按场景选择删除、掩码、伪名化、局部保留或转本地模型处理,避免隐私安全和回答质量两头失控。
PII 脱敏会不会明显降低模型效果?
有可能,特别是任务依赖具体实体关系时。因此不要一刀切删除所有实体,可以使用局部掩码、稳定占位符、surrogate 替换、本地映射恢复、按任务授权保留等策略,把隐私策略和任务类型绑定。