
在 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 等将面临多重风险隐私泄露违反 GDPR、CCPA 等数据保护法规可能导致巨额罚款。数据滥用敏感数据可能被模型服务提供商用于训练造成不可控的扩散。安全漏洞为恶意攻击者提供了社会工程学攻击的素材。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 或 PowerShellmacOS/Linux 下为 Terminal输入以下命令node --version npm --version如果已安装会显示类似v18.19.0和10.2.3的版本号。安装/更新 Node.js如果未安装或版本过低请访问 Node.js 官网 下载并安装最新的 LTS 版本。安装过程通常很简单一路点击“下一步”即可。安装完成后重新打开终端再次执行上述命令确认安装成功。2.2 安装 prompt-scrubprompt-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邮箱是 zhangsanexample.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。排查与解决检查输入文本格式确保文本编码正确UTF-8并且没有特殊字符干扰。启用详细日志使用 CLI 的-v参数或在 API 中检查返回的entities数组查看实际识别到了什么。scrub -i test.txt -v调整上下文窗口有些实体如姓名需要上下文才能准确识别。在配置中增大contextWindow值。自定义规则对于漏报在配置文件中为特定实体添加更全面的patterns正则表达式。对于误报可以使用ignorePatterns全局忽略或为特定实体设置更高的置信度阈值如果 API 支持。考虑使用更高级的模型prompt-scrub默认可能基于规则正则。对于极其复杂或模糊的 PII可以考虑其是否集成了基于 ML 的识别器或者自行接入如 Microsoft Presidio、Google DLP 等专业服务再将结果与prompt-scrub的规则引擎结合。6.2 性能问题问题现象处理长文档或高并发请求时速度慢。优化建议缓存 Scrubber 实例在 Node.js 服务中不要每次请求都调用createScrubber。在服务启动时创建实例并复用。// server.js let scrubberInstance; async function initializeScrubber() { if (!scrubberInstance) { scrubberInstance await createScrubber(/* config */); } return scrubberInstance; } // 在处理请求时使用同一个实例精简实体列表在配置中只启用 (enabled: true) 你真正关心的 PII 类型。禁用不必要的检测器可以提升速度。异步处理确保你的scrub调用是异步的 (await)避免阻塞事件循环。对于批量文件处理可以考虑使用流Stream或工作线程Worker Threads。预处理文本如果文本中包含大量无需处理的部分如 base64 图片码、二进制数据可以先将其移除或标记减少待处理文本长度。6.3 集成后 LLM 理解能力下降问题现象PII 被替换为[PHONE_NUMBER]等占位符后LLM 无法基于这些信息进行推理影响了回答质量。解决方案上下文保留策略不要简单地“移除”而是使用“泛化”或“标记化”策略。例如将“张三”替换为“人名1”并在整个会话中保持一致。这需要更复杂的上下文管理。在 System Prompt 中说明在发送给 LLM 的指令中明确告知“用户信息中的敏感部分已被替换为如[PHONE]的标签请理解这些标签代表一类信息并基于此进行回答。”后处理还原高风险仅在绝对安全的内网环境且经过严格法律评估后考虑。将擦除后的文本发送给 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 testdomain.com‘); expect(result.text).toBe(‘Contact me at [EMAIL]‘); expect(result.entities).toContainEqual( expect.objectContaining({type: ‘EMAIL‘, value: ‘testdomain.com‘}) ); });监控与告警监控 PII 擦除服务的错误率、延迟和漏报率。可以设置告警当检测到疑似高敏感信息如完整的信用卡号未被规则覆盖时触发。作为独立服务在高并发或微服务架构中可以考虑将prompt-scrub封装成一个独立的 gRPC 或 HTTP 服务供其他服务调用便于统一升级、扩容和监控。7.3 规则维护与迭代定期回顾规则新的 PII 格式如新型身份证号、虚拟电话号码和业务数据如新的内部工号规则会不断出现。应定期如每季度审查和更新检测规则。利用真实数据脱敏后测试在测试环境中使用脱敏后的真实业务数据流进行测试以发现规则盲区。假阳性处理流程建立渠道让用户或内部测试人员报告误报将正常文本识别为 PII。分析这些案例优化ignorePatterns或调整正则表达式的精确度。prompt-scrub作为一个本地优先的工具为 LLM 应用开发者提供了一个强大、灵活且隐私友好的解决方案。通过本文的讲解你应该已经掌握了从安装、配置到高级集成的全流程。核心在于理解其“数据不出域”的设计哲学并结合自身业务场景精心设计检测规则和集成架构。在实际项目中建议先从最关键的一两类 PII如手机号、邮箱开始实施逐步扩大范围。同时牢记隐私保护是一个持续的过程需要将工具、流程和人的意识相结合。