LLM应用开发中的PII隐私保护:prompt-scrub本地优先敏感信息擦除工具
2026/8/19 8:04:00 网站建设 项目流程

在 LLM 应用开发中,你是否遇到过这样的困扰:用户输入或模型输出中可能包含手机号、邮箱、身份证号等敏感信息,直接将这些数据发送给第三方大模型 API 存在隐私泄露风险。手动编写正则或规则来过滤这些信息,不仅繁琐、容易遗漏,而且难以应对各种复杂的格式变体。

今天要介绍的prompt-scrub,正是为解决这一痛点而生。它是一个本地优先(Local-First)的 PII(个人可识别信息)擦除工具,专门用于在将提示词(Prompt)发送给 LLM 前,或处理 LLM 返回的响应时,自动识别并替换掉其中的敏感信息。它基于 Node.js 开发,提供了简洁的 CLI 和 API,能无缝集成到你的 AI 应用流水线中,为数据安全加上一道可靠的保险。

本文将带你从零开始,全面掌握prompt-scrub的核心概念、安装部署、API 使用、高级配置以及生产环境最佳实践。无论你是刚接触 LLM 应用开发的初学者,还是正在寻找成熟隐私处理方案的资深工程师,都能从中获得可直接复用的代码和配置方案。

1. 背景与核心概念:为什么需要 PII 擦除?

在深入prompt-scrub之前,我们有必要厘清几个关键概念,理解其背后的必要性。

PII (Personally Identifiable Information)个人可识别信息,是指任何能够直接或间接识别特定个人身份的数据。常见例子包括:

  • 直接标识符:姓名、身份证号、护照号、社保号。
  • 间接标识符:电话号码、电子邮箱、家庭住址、IP 地址、出生日期。
  • 生物识别信息:指纹、面部识别数据。
  • 关联信息:用户名、账号 ID、车辆识别码(VIN)。

在 LLM 应用场景中,用户可能在聊天对话、文档上传、表单填写等环节无意或有意地输入这些信息。如果未经处理直接发送至云端 LLM 服务(如 OpenAI GPT、 Anthropic Claude 等),将面临多重风险:

  1. 隐私泄露:违反 GDPR、CCPA 等数据保护法规,可能导致巨额罚款。
  2. 数据滥用:敏感数据可能被模型服务提供商用于训练,造成不可控的扩散。
  3. 安全漏洞:为恶意攻击者提供了社会工程学攻击的素材。

Local-First (本地优先) 架构prompt-scrub强调“本地优先”,这意味着所有的敏感信息识别和擦除操作都在你的本地环境或受控服务器上完成,处理后的“干净”文本才会被发送出去。其核心优势在于:

  • 数据不出域:原始敏感数据从未离开你的基础设施,从根本上杜绝了传输过程中的泄露风险。
  • 低延迟:本地处理避免了网络往返延迟,对用户体验影响更小。
  • 离线可用:不依赖外部网络服务,稳定性更高。
  • 可定制化:你可以完全控制识别规则、替换策略和处理逻辑。

Prompt 与 Response 处理prompt-scrub的工作流程是双向的:

  • Prompt Scrubbing (提示词擦除):在将用户输入(或系统构造的提示词)发送给 LLM API 之前,进行 PII 擦除。
  • Response Scrubbing (响应擦除):在接收到 LLM 返回的响应后,再次进行擦除。这一步是为了防止 LLM 在生成文本时,“回忆”或推理出了用户输入中的敏感信息(尽管输入已被擦除),或生成了新的敏感内容。

接下来,我们将进入实战环节,从环境搭建开始。

2. 环境准备与安装

prompt-scrub是一个 Node.js 工具,因此你需要先准备好 Node.js 运行环境。

2.1 Node.js 环境准备

确保你的系统已安装 Node.js。prompt-scrub可能需要较新的 Node 版本(例如 >= 16),建议使用 LTS 版本。

检查现有 Node.js 版本:打开终端(Windows 下为 CMD 或 PowerShell,macOS/Linux 下为 Terminal),输入以下命令:

node --version npm --version

如果已安装,会显示类似v18.19.010.2.3的版本号。

安装/更新 Node.js:如果未安装或版本过低,请访问 Node.js 官网 下载并安装最新的 LTS 版本。安装过程通常很简单,一路点击“下一步”即可。安装完成后,重新打开终端,再次执行上述命令确认安装成功。

2.2 安装 prompt-scrub

prompt-scrub可以通过 npm 或 yarn 进行全局安装,方便在命令行中直接使用;也可以作为项目依赖安装,集成到你的代码中。

方式一:全局安装(推荐用于 CLI 工具使用)在终端中执行:

npm install -g prompt-scrub # 或者使用 yarn # yarn global add prompt-scrub

安装完成后,验证是否成功:

scrub --help

如果看到帮助信息,说明安装成功。

方式二:作为项目依赖安装(用于集成到 Node.js 项目)在你的项目根目录下执行:

npm install prompt-scrub # 或 # yarn add prompt-scrub

这会将prompt-scrub添加到你的package.json文件的dependencies中。

3. CLI 命令行工具快速上手

CLI 是prompt-scrub最直接的使用方式,适合快速处理文本文件或进行测试。

3.1 基础使用:处理单条文本

最基本的用法是直接对一段文本进行擦除:

echo "我的电话是 138-0013-8000,邮箱是 zhangsan@example.com。" | scrub

或者将文本保存在文件中处理:

# 假设有一个 input.txt 文件,内容包含敏感信息 scrub -i input.txt -o output.txt

执行后,output.txt文件中的电话号码和邮箱地址会被替换成占位符,例如:我的电话是 [PHONE_NUMBER],邮箱是 [EMAIL_ADDRESS]。

3.2 常用 CLI 参数详解

scrub --help会列出所有参数,以下是几个关键参数:

  • -i, --input <path>: 指定输入文件路径。如果不指定,则从标准输入读取。
  • -o, --output <path>: 指定输出文件路径。如果不指定,则输出到标准输出。
  • -c, --config <path>: 指定自定义配置文件路径(JSON 或 YAML 格式),用于覆盖默认的 PII 识别规则和替换策略。
  • -f, --format <format>: 指定输入格式,如text(默认)、json。如果输入是 JSON,可以使用--json-path指定需要处理的字段。
  • --json-path <path>: 当输入格式为json时,使用 JSONPath 表达式来定位需要擦除的文本字段。例如:--json-path “$.messages[*].content“
  • -v, --verbose: 输出更详细的日志信息,包括识别出了哪些类型的 PII。
  • --version: 显示当前版本。

示例:处理 JSON 格式的聊天记录假设有一个chat.json文件,结构如下:

{ “conversation_id“: “123“, “messages“: [ {“role“: “user“, “content“: “我叫李四,住在北京市朝阳区。“}, {“role“: “assistant“, “content“: “你好,李四!“} ] }

我们只想擦除messages数组中每个对象的content字段。命令如下:

scrub -i chat.json -f json --json-path “$.messages[*].content“ -o chat_scrubbed.json

处理后的chat_scrubbed.json中,地址信息会被替换为[LOCATION]

4. Node.js API 深度集成

对于需要将 PII 擦除功能嵌入到应用程序中的场景,使用 Node.js API 是更灵活的选择。

4.1 基本 API 调用

首先,在你的项目文件中引入prompt-scrub

// 示例:scrub-demo.js const { scrubText } = require(‘prompt-scrub‘); // 或者使用 ES Module 语法 // import { scrubText } from ‘prompt-scrub‘; async function main() { const sensitiveText = “患者张三,身份证号 110101199003077832,主诉头痛。预约电话:010-12345678。“; try { const scrubbedResult = await scrubText(sensitiveText); console.log(‘擦除前:‘, sensitiveText); console.log(‘擦除后:‘, scrubbedResult.text); // 输出处理后的文本 console.log(‘被替换的实体:‘, scrubbedResult.entities); // 输出识别到的实体详情 } catch (error) { console.error(‘PII 擦除失败:‘, error); } } main();

运行这个脚本:

node scrub-demo.js

你将看到类似输出:

擦除前: 患者张三,身份证号 110101199003077832,主诉头痛。预约电话:010-12345678。 擦除后: 患者 [PERSON],身份证号 [ID_NUMBER],主诉头痛。预约电话: [PHONE_NUMBER]。 被替换的实体: [ { type: ‘PERSON‘, value: ‘张三‘, start: 3, end: 5 }, { type: ‘ID_NUMBER‘, value: ‘110101199003077832‘, start: 9, end: 27 }, { type: ‘PHONE_NUMBER‘, value: ‘010-12345678‘, start: 34, end: 46 } ]

scrubText函数返回一个对象,包含擦除后的文本 (text) 和被识别出的 PII 实体列表 (entities)。

4.2 高级配置与自定义规则

默认的 PII 检测规则可能无法覆盖所有场景,或者你可能希望自定义替换后的占位符格式。这时可以通过配置对象来实现。

const { createScrubber } = require(‘prompt-scrub‘); async function main() { // 1. 创建一个配置好的擦除器实例 const customScrubber = createScrubber({ // 自定义实体识别器 entities: [ // 使用内置规则,但调整其优先级或启用状态 { type: ‘EMAIL‘, enabled: true }, { type: ‘PHONE_NUMBER‘, enabled: true }, // 添加自定义正则表达式规则 { id: ‘CUSTOM_ID‘, name: ‘内部员工号‘, regex: /EMP-\d{6}/g, replacement: ‘[EMPLOYEE_ID]‘ } ], // 全局替换策略 replacementStrategy: ‘placeholder‘, // ‘placeholder‘ (默认), ‘mask‘, ‘remove‘ // 自定义占位符格式 placeholderFormat: ‘[{type}]‘, // 例如将 [PHONE_NUMBER] 改为 [PHONE] // 上下文窗口大小,用于改善识别精度 contextWindow: 50, }); const text1 = “联系客服 EMP-123456 获取帮助。“; const text2 = “我的手机丢了,号码是 13912345678。“; const result1 = await customScrubber.scrub(text1); console.log(result1.text); // 输出:联系客服 [EMPLOYEE_ID] 获取帮助。 const result2 = await customScrubber.scrub(text2); console.log(result2.text); // 输出:我的手机丢了,号码是 [PHONE_NUMBER]。 } main();

通过createScrubber工厂函数,你可以细粒度地控制擦除行为,构建出最适合你业务需求的处理器。

4.3 集成到 LLM 应用流水线

一个典型的集成场景是在调用 OpenAI API 前后插入prompt-scrub

const { scrubText } = require(‘prompt-scrub‘); const OpenAI = require(‘openai‘); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); async function getSafeLLMResponse(userInput) { // 步骤1: 擦除用户输入中的 PII const scrubbedInput = await scrubText(userInput); console.log(‘安全化的用户输入:‘, scrubbedInput.text); // 步骤2: 使用擦除后的文本构造提示词 const prompt = `请根据以下用户描述回答问题:${scrubbedInput.text}\n\n问题:这段描述主要讲了什么?`; // 步骤3: 调用 LLM API const completion = await openai.chat.completions.create({ model: ‘gpt-3.5-turbo‘, messages: [{ role: ‘user‘, content: prompt }], }); const llmRawResponse = completion.choices[0].message.content; console.log(‘LLM 原始响应:‘, llmRawResponse); // 步骤4: 擦除 LLM 响应中可能出现的 PII (二次防护) const scrubbedResponse = await scrubText(llmRawResponse); console.log(‘安全化的最终响应:‘, scrubbedResponse.text); // 步骤5: 返回安全响应,并可选地记录被擦除的实体(用于审计) return { safeResponse: scrubbedResponse.text, removedFromInput: scrubbedInput.entities, removedFromOutput: scrubbedResponse.entities, }; } // 使用示例 const testInput = “我叫王五,我的信用卡号是 4111-1111-1111-1111,有效期 12/25。我昨天在王府井消费了500元。“; getSafeLLMResponse(testInput).then(result => { console.log(‘最终安全结果:‘, result.safeResponse); console.log(‘审计日志-输入擦除:‘, result.removedFromInput); });

这个例子展示了完整的防御链条:输入擦除 -> LLM 调用 -> 输出擦除。同时,保留了被擦除实体的审计日志,符合数据治理规范。

5. 配置文件详解与规则定制

对于复杂的规则,使用配置文件比在代码中硬编码更易于管理。prompt-scrub支持 JSON 或 YAML 格式的配置文件。

5.1 配置文件结构

创建一个scrub-config.yaml文件(或.json文件):

# scrub-config.yaml version: ‘1.0‘ # 实体定义 entities: # 启用并自定义内置实体 - id: ‘EMAIL‘ enabled: true replacement: ‘[EMAIL]‘ # 自定义占位符 # 内置实体通常有预定义的正则模式,这里也可以覆盖 # pattern: ‘...‘ - id: ‘PHONE_NUMBER‘ enabled: true replacement: ‘[TEL]‘ - id: ‘ID_NUMBER‘ # 中国身份证号 enabled: true # 使用自定义正则表达式增强识别 patterns: - ‘\\b[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]\\b‘ replacement: ‘[ID_CARD]‘ # 完全自定义实体 - id: ‘CUSTOM_ORDER_ID‘ name: ‘订单号‘ patterns: - ‘ORDER-\\d{8}-[A-Z]{3}‘ replacement: ‘[ORDER_REF]‘ confidence: ‘high‘ # 置信度:low, medium, high # 全局设置 global: replacementStrategy: ‘placeholder‘ # placeholder, mask, remove placeholderFormat: ‘[{type}]‘ # 如果未在实体中指定 replacement,则使用此格式,{type}会被替换为实体ID contextAware: true # 是否使用上下文提高识别准确性 # 忽略某些上下文中的误报(例如,在代码片段中) ignorePatterns: - ‘`.*?`‘ # 忽略反引号内的代码 - ‘“.*?“‘ # 忽略双引号内的字符串(可能不精确,根据情况调整)

5.2 使用配置文件

在 CLI 中使用:

scrub -i input.txt -c scrub-config.yaml -o output.txt

在 Node.js API 中使用:

const { createScrubberFromConfig } = require(‘prompt-scrub‘); const fs = require(‘fs‘).promises; const yaml = require(‘js-yaml‘); // 需要安装 js-yaml 包来解析 YAML async function main() { const configText = await fs.readFile(‘scrub-config.yaml‘, ‘utf8‘); const config = yaml.load(configText); const scrubber = createScrubberFromConfig(config); const result = await scrubber.scrub(“您的订单 ORDER-20240315-ABC 已发货。“); console.log(result.text); // 输出:您的订单 [ORDER_REF] 已发货。 } main();

6. 常见问题与排查指南

在实际使用中,你可能会遇到以下问题。

6.1 识别不准确(漏报或误报)

问题现象

  • 漏报:真实的手机号或身份证号没有被识别出来。
  • 误报:将非 PII 的文本(如产品代码“SN-12345”)识别为 PII。

排查与解决

  1. 检查输入文本格式:确保文本编码正确(UTF-8),并且没有特殊字符干扰。
  2. 启用详细日志:使用 CLI 的-v参数或在 API 中检查返回的entities数组,查看实际识别到了什么。
    scrub -i test.txt -v
  3. 调整上下文窗口:有些实体(如姓名)需要上下文才能准确识别。在配置中增大contextWindow值。
  4. 自定义规则:对于漏报,在配置文件中为特定实体添加更全面的patterns(正则表达式)。对于误报,可以使用ignorePatterns全局忽略,或为特定实体设置更高的置信度阈值(如果 API 支持)。
  5. 考虑使用更高级的模型prompt-scrub默认可能基于规则(正则)。对于极其复杂或模糊的 PII,可以考虑其是否集成了基于 ML 的识别器,或者自行接入如 Microsoft Presidio、Google DLP 等专业服务,再将结果与prompt-scrub的规则引擎结合。

6.2 性能问题

问题现象:处理长文档或高并发请求时速度慢。

优化建议

  1. 缓存 Scrubber 实例:在 Node.js 服务中,不要每次请求都调用createScrubber。在服务启动时创建实例并复用。
    // server.js let scrubberInstance; async function initializeScrubber() { if (!scrubberInstance) { scrubberInstance = await createScrubber(/* config */); } return scrubberInstance; } // 在处理请求时使用同一个实例
  2. 精简实体列表:在配置中只启用 (enabled: true) 你真正关心的 PII 类型。禁用不必要的检测器可以提升速度。
  3. 异步处理:确保你的scrub调用是异步的 (await),避免阻塞事件循环。对于批量文件处理,可以考虑使用流(Stream)或工作线程(Worker Threads)。
  4. 预处理文本:如果文本中包含大量无需处理的部分(如 base64 图片码、二进制数据),可以先将其移除或标记,减少待处理文本长度。

6.3 集成后 LLM 理解能力下降

问题现象:PII 被替换为[PHONE_NUMBER]等占位符后,LLM 无法基于这些信息进行推理,影响了回答质量。

解决方案

  1. 上下文保留策略:不要简单地“移除”,而是使用“泛化”或“标记化”策略。例如,将“张三”替换为“<人名1>”,并在整个会话中保持一致。这需要更复杂的上下文管理。
  2. 在 System Prompt 中说明:在发送给 LLM 的指令中明确告知:“用户信息中的敏感部分已被替换为如[PHONE]的标签,请理解这些标签代表一类信息,并基于此进行回答。”
  3. 后处理还原(高风险)仅在绝对安全的内网环境且经过严格法律评估后考虑。将擦除后的文本发送给 LLM,拿到响应后,在本地根据映射表将占位符反向替换为原始值。这要求映射表必须被极其安全地保管。不推荐在大多数场景使用

7. 生产环境最佳实践

prompt-scrub用于生产环境时,需遵循以下准则以确保其有效性、可靠性和可维护性。

7.1 安全与合规

  • 纵深防御prompt-scrub应作为隐私保护的一层,而非唯一一层。结合输入验证、输出过滤、访问日志审计、网络隔离等共同构建安全体系。
  • 审计日志:务必记录被擦除的 PII 实体(类型、位置、替换后的文本)。这些日志对于合规性检查、事故追溯和规则调优至关重要。确保日志本身的安全存储和访问控制。
  • 密钥与配置管理:如果配置中包含自定义正则表达式(可能暴露业务逻辑),或集成了外部服务的密钥,应使用环境变量或安全的配置管理服务(如 Vault)来管理,而非硬编码在配置文件或代码中。
  • 法规遵从性:了解你所处地区及业务涉及地区的隐私法规(如 GDPR、HIPAA、PIPL)。prompt-scrub的规则集需要根据法规要求覆盖的 PII 类型进行调整。

7.2 工程化与部署

  • 版本化配置:将 PII 擦除规则配置文件纳入版本控制系统(如 Git)。任何规则的变更都应经过评审和测试,并记录变更原因。
  • 单元测试与集成测试:为你的擦除逻辑编写全面的测试用例。
    // scrubber.test.js const { scrubText } = require(‘./your-scrubber-module‘); test(‘should scrub email addresses‘, async () => { const result = await scrubText(‘Contact me at test@domain.com‘); expect(result.text).toBe(‘Contact me at [EMAIL]‘); expect(result.entities).toContainEqual( expect.objectContaining({type: ‘EMAIL‘, value: ‘test@domain.com‘}) ); });
  • 监控与告警:监控 PII 擦除服务的错误率、延迟和漏报率。可以设置告警,当检测到疑似高敏感信息(如完整的信用卡号)未被规则覆盖时触发。
  • 作为独立服务:在高并发或微服务架构中,可以考虑将prompt-scrub封装成一个独立的 gRPC 或 HTTP 服务,供其他服务调用,便于统一升级、扩容和监控。

7.3 规则维护与迭代

  • 定期回顾规则:新的 PII 格式(如新型身份证号、虚拟电话号码)和业务数据(如新的内部工号规则)会不断出现。应定期(如每季度)审查和更新检测规则。
  • 利用真实数据(脱敏后)测试:在测试环境中,使用脱敏后的真实业务数据流进行测试,以发现规则盲区。
  • 假阳性处理流程:建立渠道让用户或内部测试人员报告误报(将正常文本识别为 PII)。分析这些案例,优化ignorePatterns或调整正则表达式的精确度。

prompt-scrub作为一个本地优先的工具,为 LLM 应用开发者提供了一个强大、灵活且隐私友好的解决方案。通过本文的讲解,你应该已经掌握了从安装、配置到高级集成的全流程。核心在于理解其“数据不出域”的设计哲学,并结合自身业务场景,精心设计检测规则和集成架构。

在实际项目中,建议先从最关键的一两类 PII(如手机号、邮箱)开始实施,逐步扩大范围。同时,牢记隐私保护是一个持续的过程,需要将工具、流程和人的意识相结合。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询