PromptMask:用本地小模型守护隐私,云端大模型处理任务
1. 项目概述在云端AI与本地AI之间架起隐私桥梁如果你和我一样既眼馋GPT-4o、Claude这些云端大模型的强大能力又对把公司代码、客户个人信息、医疗记录这些敏感数据直接喂给它们感到如坐针毡那你肯定也纠结过。云端AI聪明但隐私堪忧本地AI安全但模型能力尤其是小参数模型和响应速度往往又差强人意。难道就没有一个两全其美的办法吗最近我在一个开源项目里找到了一个非常巧妙的思路它叫PromptMask。这个项目的核心目标就一句话让你的敏感数据永远留在你的机器上。它不是另一个本地大模型而是一个“隐私中间层”。你可以把它想象成一个高度智能的“数据脱敏员”在你和云端AI服务商之间工作。所有出站的数据都会先经过它由它识别并替换掉里面的敏感信息比如人名、身份证号、电话号码生成一份“匿名版”的提示词发给云端AI等AI返回结果后这个“脱敏员”再根据自己记录的替换表把结果中的匿名标记精准地还原成原始数据。这样一来云端AI处理的是“干干净净”的文本它根本不知道“张三”和“他的银行卡号123456”具体是什么但它基于匿名文本生成的建议、代码或分析经过本地还原后对你来说依然是完整可用的。这相当于用一个小而专的本地模型负责识别敏感信息撬动了一个大而全的云端模型负责复杂任务在享受云端强大算力的同时守住了数据的最后一道防线。2. 核心原理与架构设计拆解2.1 双模型协作的“隐私管道”PromptMask 的核心工作流是一个清晰的“掩码-处理-解掩码”管道。我拆解了一下整个过程可以分为四个关键步骤本地敏感信息识别与掩码当你提交一段包含敏感信息的文本例如“帮我为患者李四病历号P2024001起草一份出院小结。”时PromptMask 首先会调用你配置的本地LLM API。这个本地模型的任务不是回答问题而是进行命名实体识别NER。它会找出文本中的敏感实体如人名“李四”、病历号“P2024001”并用预定义的、无意义的占位符如{PERSON_NAME}、{MEDICAL_RECORD_ID}替换它们同时生成并保存一份“掩码映射表”。这个步骤完全在本地完成敏感数据不出境。匿名文本发送至云端经过掩码处理后的文本变成了“帮我为患者{PERSON_NAME}病历号{MEDICAL_RECORD_ID}起草一份出院小结。” 这个“干净”的文本被发送到你指定的云端AI服务如 OpenAI、Anthropic 或任何兼容 OpenAI API 的终端。云端模型处理匿名请求云端大模型接收到这个匿名请求它基于“患者”和“病历号”这些抽象概念进行理解和创作生成回复“已为患者{PERSON_NAME}病历号{MEDICAL_RECORD_ID}草拟出院小结如下...”。由于它从未见过真实数据因此从根本上避免了隐私泄露。本地响应解掩码PromptMask 收到云端的匿名回复后利用第一步保存的“掩码映射表”将占位符{PERSON_NAME}和{MEDICAL_RECORD_ID}准确地还原为原始的“李四”和“P2024001”最终将一份完整的、包含真实信息的结果呈现给你。这个设计的精妙之处在于职责分离对计算资源要求不高但隐私敏感的“识别”任务交给本地小模型对智力要求高的“生成”任务交给云端大模型。它不试图让本地模型变得全能而是让它做好最关键的“守门人”角色。2.2 技术栈与组件角色为了支撑上述流程PromptMask 的架构包含了几个关键组件核心掩码引擎 (PromptMask类)这是库的大脑负责协调整个掩码/解掩码流程管理配置并调用本地LLM API。OpenAI SDK 适配器 (OpenAIMasked类)这是对开发者最友好的部分。它实现了与openai库一模一样的接口。你只需要把代码中的from openai import OpenAI改成from promptmask import OpenAIMasked as OpenAI之后的调用方式完全不变但所有通信都会自动经过隐私过滤层。它同时处理了普通请求和流式响应在流式输出中解掩码是实时进行的用户体验无缝。API 网关服务器 (promptmask-web)这是一个独立的Web服务提供了一个标准的 OpenAI 兼容 API 终端。任何支持自定义 OpenAI API 基地址的工具如 LangChain、AutoGPT、ChatGPT Next Web 等都可以通过简单修改配置将请求指向这个本地网关从而无代码集成隐私保护。配置系统采用 TOML 格式的配置文件允许你精细控制本地模型的选择、敏感信息的定义、掩码占位符的格式等。配置优先级从高到低为环境变量 代码传入字典 用户配置文件 默认配置提供了极大的灵活性。2.3 与纯本地化方案的权衡肯定有人会问既然都用本地模型做识别了为什么不干脆所有事情都用本地模型做完这里涉及到几个现实的权衡。首先成本与性能一个能高质量完成复杂任务如长篇写作、代码生成、深度分析的本地大模型需要强大的GPU和显存部署和维护成本很高。而 PromptMask 方案中的本地模型只需要具备不错的实体识别能力通常一个 7B 甚至更小的模型就足够了可以在消费级硬件上流畅运行。其次效果与质量当前顶尖的闭源或开源大模型在创意、逻辑、知识广度上仍有明显优势。PromptMask 让你在特定隐私场景下依然能利用这些顶尖模型的能力。最后是流式支持与延迟许多云端API提供优秀的流式响应体验顺畅。PromptMask 的适配器在流式处理上做了精心设计保证了还原的实时性而纯本地方案在流式体验和响应速度上可能难以媲美。3. 从零开始环境部署与配置详解3.1 本地LLM基础设施搭建PromptMask 本身不包含模型它需要一个提供 OpenAI 兼容 API 的本地 LLM 服务作为“隐私过滤器”。最省心的选择是Ollama。Ollama 安装与模型拉取Ollama 的安装极其简单官网提供了各系统的安装包。安装后你需要拉取一个适合做实体识别的小模型。根据我的经验qwen2.5:7b、llama3.2:3b或gemma2:9b都是不错的起点在准确度和速度之间取得了良好平衡。# 拉取模型 (以 qwen2.5:7b 为例) ollama pull qwen2.5:7b # 启动 Ollama 服务通常安装后会自动运行 # 检查服务是否运行在 http://localhost:11434 curl http://localhost:11434/api/chat -d { model: qwen2.5:7b, messages: [{ role: user, content: hello}], stream: false }关键验证点你需要确认 Ollama 的v1 API 终端可用。PromptMask 默认连接的就是http://localhost:11434/v1。运行上面的 curl 命令能收到一个 JSON 格式的回复即说明环境就绪。注意如果你使用llama.cpp或vLLM等其他推理框架请确保它们启动了 OpenAI 兼容的 API 服务通常对应--api参数并将终端地址如http://localhost:8080/v1在后续配置中告知 PromptMask。3.2 PromptMask 的安装与基础配置安装 PromptMask 核心库很简单pip install promptmask如果你想使用 Web UI 和 API 网关则需要安装完整套件pip install promptmask[web]安装后最重要的步骤是配置。虽然 PromptMask 有默认配置但为了获得最佳效果我强烈建议创建用户级配置文件。在你的项目目录或家目录下创建文件promptmask.config.user.toml# promptmask.config.user.toml [llm_api] # 指定用于掩码的本地模型。此处填写 Ollama 中的模型名或你的推理引擎对应的模型ID。 model qwen2.5:7b # 如果你的本地API不在默认地址可以在这里或通过环境变量覆盖 # base_url http://192.168.1.100:8080/v1 [sensitive] # 定义什么是“敏感信息”。这是一个给本地模型的指令你可以让它更关注你的领域。 # 默认指令已不错但你可以微调例如 include person names, identification numbers (ID card, passport, patient ID), phone numbers, email addresses, home addresses, bank account and credit card numbers, and any confidential project code names or internal identifiers. [mask_wrapper] # 定义掩码占位符的格式。默认是 ${TYPE}如 ${PERSON_NAME}。 # 你可以自定义确保它不会和你的正常文本冲突。 left ${ right } [web] # 如果你使用网关功能并希望代理到非OpenAI的云端服务如Google Gemini需要设置此上游地址 # upstream_oai_api_base https://generativelanguage.googleapis.com/v1beta/openai配置优先级解读PromptMask 的配置加载顺序非常清晰。最高优先级是代码中直接传入的参数字典其次是环境变量LOCALAI_API_BASE和LOCALAI_API_KEY然后是当前目录下的promptmask.config.user.toml文件最后才是库内置的默认配置。这让你可以在不同项目、不同环境中灵活切换配置。4. 三种集成方式实战与避坑指南根据你的使用场景PromptMask 提供了三种不同集成粒度的方式。4.1 方式一无缝替换 OpenAI SDK推荐开发者这是最优雅的方式几乎零成本集成。假设你有一段现有的调用 OpenAI 的代码# 原代码 from openai import OpenAI import os client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) response client.chat.completions.create( modelgpt-4, messages[ {role: user, content: 患者张三身份证号110101199001011234主诉头痛三天。请生成问诊记录。} ] ) print(response.choices[0].message.content)你只需要修改一行导入语句并将客户端指向你的云端服务或继续使用OpenAI官方终端# 修改后的代码 - 自动获得隐私保护 from promptmask import OpenAIMasked as OpenAI # 关键改动替换导入 import os # 注意这里 base_url 指向你真正想用的云端AI服务 client OpenAI( api_keyos.getenv(OPENAI_API_KEY), # 你的云端API Key base_urlhttps://api.openai.com/v1 # 或 Anthropic、DeepSeek 等兼容终端 ) response client.chat.completions.create( modelgpt-4, messages[ {role: user, content: 患者张三身份证号110101199001011234主诉头痛三天。请生成问诊记录。} ] ) # 打印出的结果中敏感信息已被安全地还原 print(response.choices[0].message.content)实操心得流式处理OpenAIMasked完美支持流式响应 (streamTrue)。掩码映射表会在请求时生成并缓存在接收到流式返回的每个片段时实时进行解掩码用户感知到的就是最终的真实文本流体验无缝。访问原始内容有时你可能想查看被掩码后的、发送给云端的原始文本用于调试。可以通过response.choices[0].message.original_content属性获取。注意上下文长度掩码过程会增加一些标记占位符对于超长上下文模型影响微乎其微。但如果你本地模型上下文窗口很小如2K而你的提示词很长可能会在掩码阶段出错。选择上下文窗口足够的本地模型很重要。4.2 方式二使用本地API网关推荐非开发者或现有工具如果你不想写代码或者正在使用像ChatGPT-Next-Web、Open WebUI这类支持自定义API终端的图形化客户端那么API网关模式是你的最佳选择。首先确保安装了Web组件并启动服务pip install promptmask[web] promptmask-web # 输出 INFO: Uvicorn running on http://0.0.0.0:8000服务启动后你本地就拥有了一个运行在http://localhost:8000的网关。这个网关提供了两个关键终端Web UIhttp://localhost:8000/用于手动测试掩码/解掩码功能和网关。OpenAI 兼容网关http://localhost:8000/gateway/v1/chat/completions。集成现有工具以ChatGPT-Next-Web为例你只需要在它的设置中将 “OpenAI API Endpoint” 修改为http://localhost:8000/gateway/v1并在 “API Key” 处填写你真实的云端AI API Key。之后所有通过该客户端发起的请求都会先经过本地的 PromptMask 网关进行隐私处理再转发到云端。使用 cURL 测试网关curl http://localhost:8000/gateway/v1/chat/completions \ -H Authorization: Bearer sk-your-real-openai-key \ -H Content-Type: application/json \ -d { model: gpt-4, messages: [ { role: user, content: 我的客户是阿里巴巴集团他们的内部项目代号‘悟空’遇到了数据库性能瓶颈。请分析可能的原因。 } ] }在这个例子中“阿里巴巴集团”和项目代号“悟空”在发出前会被本地模型识别并掩码云端GPT-4看到的是“我的客户是{COMPANY_NAME}他们的内部项目代号{PROJECT_CODE_NAME}遇到了...”但它给出的性能分析建议在返回给你的结果中又会被完美还原。避坑指南网关模式下model参数通常是你希望调用的云端模型如gpt-4PromptMask 网关会忽略这个参数用于自身路由但会将其原样传递给上游。确保你的上游服务支持该模型名。4.3 方式三直接调用核心掩码器用于自定义流程如果你需要将隐私过滤嵌入到更复杂的、非标准的工作流中可以直接使用PromptMask核心类手动控制掩码和解掩码的时机。import asyncio from promptmask import PromptMask async def custom_workflow(): # 1. 初始化掩码器会加载你的配置文件 masker PromptMask(config_file./my_config.toml) original_prompt 请审阅以下合同条款并指出对Acme Corp不利的地方。 甲方Acme Corp (统一社会信用代码91110108MA12345678) 乙方John Smith 条款乙方需在2024年12月31日前将最终交付物上传至甲方指定的FTP服务器地址ftp.acme-internal.com账号delivery密码TemporaryPass123!。 # 2. 异步执行掩码推荐不阻塞事件循环 masked_text, mask_map await masker.async_mask_str(original_prompt) print(f[发送给云端的匿名文本]:\n{masked_text}\n) print(f[本地保存的掩码映射表]:\n{mask_map}\n) # 3. 模拟调用远程AI服务此处为模拟响应 # 在实际应用中这里你会将 masked_text 发送给 OpenAI、Claude 等API simulated_ai_response 经审阅发现以下对Acme Corp${COMPANY_NAME}潜在不利的条款 1. **密码明文传输风险**合同要求乙方将密码 TemporaryPass123! 明文写入文档这违反了基本的安全实践。建议改为通过安全通道单独发送临时密码或使用预生成的访问令牌。 2. ...其他分析 # 4. 将AI的匿名响应还原 unmasked_response masker.unmask_str(simulated_ai_response, mask_map) print(f[还原后的最终结果]:\n{unmasked_response}) if __name__ __main__: asyncio.run(custom_workflow())这种方式提供了最大的灵活性例如你可以将mask_map存入数据库与特定的会话ID关联实现跨多次交互的隐私上下文保持。5. 性能调优、问题排查与安全边界5.1 如何选择与优化本地掩码模型本地模型的选择是平衡精度、速度和资源消耗的关键。PromptMask 项目提供了一个基准测试套件eval/目录你可以运行它来评估不同模型在你硬件上的表现。选择模型的核心考量实体识别精度这是首要指标。可以先用一段包含多种实体人名、地址、号码、日期的文本测试观察模型是否能正确识别并分类。qwen2.5:7b、gemma2:9b在这方面的综合表现较好。推理速度掩码操作会增加请求的延迟。延迟 本地模型推理时间 网络往返时间 云端模型推理时间。选择一个速度快的本地模型至关重要。较小的模型如llama3.2:3b速度更快但可能漏掉一些复杂或罕见的实体。上下文长度确保本地模型的上下文窗口足以处理你最长的提示词。如果提示词过长掩码操作会失败。指令遵循能力模型需要准确理解你在[sensitive]配置中定义的“敏感信息”范围。指令遵循能力强的模型微调效果更明显。优化技巧量化模型使用 GGUF 量化格式的模型通过 Ollama 或 llama.cpp 加载可以显著减少内存占用并提升推理速度而对精度影响很小。例如qwen2.5:7b-q4_K_M是不错的选择。调整系统提示词虽然 PromptMask 有默认的系统提示词来指导模型识别敏感信息但你可以在配置中通过[sensitive]部分的include字段进行强化。描述越具体模型表现越好。例如加入“项目内部代号”、“服务器IP地址”、“软件许可证密钥”等。使用GPU加速如果本地有GPU确保 Ollama 或你的推理框架启用了 GPU 推理Ollama 默认会尝试使用 GPU这能极大提升掩码速度。5.2 常见问题与排查清单在实际部署中你可能会遇到以下问题。这里是我的排查清单问题现象可能原因解决方案启动promptmask-web或调用时连接被拒本地LLM服务未运行或地址错误。1. 运行ollama serve确保服务启动。2. 检查LOCALAI_API_BASE环境变量或配置文件中的base_url是否正确应为http://主机:端口/v1。3. 用curl http://localhost:11434/v1/models测试本地API是否可达。掩码失败返回原始文本本地模型未能识别出任何敏感实体。1. 检查配置的本地模型是否支持中文NER如果处理中文。2. 在配置文件的[sensitive]部分用更详细、具体的语言描述你需要识别的信息类型。3. 尝试换一个指令遵循能力更强的模型。掩码结果错误如将非敏感词掩码本地模型过度敏感或理解偏差。1. 同样优化[sensitive]的指令明确排除一些非敏感信息。2. 这是一个精度召回率的权衡。目前方案更倾向于“召回”宁可错杀以避免泄露。对于固定场景可以考虑后处理规则进行过滤。流式响应中断或还原错乱网络不稳定或本地模型响应超时。1. 增加OpenAIMasked或网关的超时设置。2. 确保本地模型推理稳定。如果本地模型服务中断整个流程会失败。使用网关时返回“模型不支持”错误网关未正确配置上游API地址或传递的模型名不被上游支持。1. 如果使用非OpenAI官方服务务必在配置文件的[web]部分设置upstream_oai_api_base。2. 确认你请求中model参数的值是上游服务支持的模型名。5.3 理解安全边界与局限性没有任何方案是银弹理解 PromptMask 的边界至关重要隐私保护的粒度它保护的是离散的、可识别的敏感实体PII。如果隐私蕴含在文本的风格、逻辑结构、或非常规表达中例如通过独特的写作风格推断作者本地模型可能无法将其抽象为占位符这些信息仍可能泄露。对本地模型的信任整个方案的安全前提是你信任本地模型和其运行环境。你需要确保运行本地模型的机器是安全的模型权重来源可信且推理过程中数据不会被窃取。元数据泄露PromptMask 保护了提示词和响应正文的内容但通信的元数据如请求时间、频率、大致长度仍然可能被云端服务商获取。对于极高安全要求的场景需要结合其他匿名化网络技术。侧信道攻击理论上一个强大的攻击者如果拥有海量数据可能通过分析匿名文本的语义和上下文结合外部知识库对某些通用实体如“美国总统”进行概率性猜测。但这需要极强的能力和动机对于绝大多数商业和个人应用风险极低。最佳实践建议将 PromptMask 视为你数据安全策略中的重要一环而非全部。对于绝密信息最安全的方式仍然是完全离线的本地处理。对于大多数需要兼顾智能与隐私的场景PromptMask 提供了一种非常实用且优雅的折中方案。