← 返回信息流
技术博客Hugging Face Blog·2026/4/27

如何使用OpenAI隐私过滤器构建可扩展的Web应用

原标题:How to build scalable web apps with OpenAI's Privacy Filter

速览

本文探讨了如何在构建可扩展的Web应用程序时集成OpenAI的隐私过滤器。通过自动识别和屏蔽敏感信息,该工具能有效防止数据泄露,确保用户隐私安全。这对于处理大量用户数据的应用场景具有重要意义,有助于提升合规性与用户信任度。

AI 深度解读

如何利用 OpenAI 的 Privacy Filter 构建可扩展的 Web 应用

背景

随着大语言模型(LLM)在企业级应用中的普及,数据隐私保护成为构建生产级应用的核心挑战。OpenAI 近期发布了 Privacy Filter,这是一个专门用于识别和屏蔽个人身份信息(PII)的模型。与此同时,Hugging Face 社区开发者利用 Gradio 框架中的 gradio.Server 组件,展示了如何基于该模型构建三种不同场景下的可扩展 Web 应用:文档隐私探索器、图像匿名化工具以及智能脱敏粘贴板。

这篇文章深入解析了这三种应用的构建逻辑,重点阐述了 gradio.Server 如何通过统一后端角色,将自定义的前端交互与 Gradio 的队列管理、ZeroGPU 资源分配以及 gradio_client SDK 无缝结合,从而解决并发处理、资源调度和前后端代码复用等工程难题。

核心内容

模型基础:Privacy Filter

Privacy Filter 是一个拥有 15 亿参数但仅激活 5000 万参数的稀疏模型,采用宽松的 Apache 2.0 许可证。该模型支持 128,000 token 的上下文窗口,能够一次性处理长文档而无需分块或拼接,确保跨度偏移量与渲染文本直接对齐。

模型识别的 PII 类别包括:private_person(个人姓名)、private_address(地址)、private_email(邮箱)、private_phone(电话)、private_url(网址)、private_date(日期)、account_number(账号)和 secret(机密信息)。它在 PII-Masking-300k 基准测试中达到了最先进(SOTA)的性能表现。

应用一:Document Privacy Explorer(文档隐私探索器)

用户痛点:用户希望阅读包含大量 PII 的文档(如合同、简历、聊天记录导出),要求以正常阅读体验呈现,同时高亮显示检测到的 PII 跨度,并提供侧边栏过滤和顶部摘要仪表板,而非将其变成复杂的表单。

Privacy Filter 的作用: 整个文件通过单次 128k 上下文的向前传递处理,无需分块。使用 BIOES 解码算法确保在长且模糊的文本段中保持跨度边界的清晰。

gradio.Server 的工程实现: 虽然可以使用 Gradio Blocks 配合 gr.HighlightedText 构建,但为了获得更好的阅读体验(如衬线字体、客户端 CSS 类切换而非重新运行模型、避免页面重渲染),开发者选择了手写 HTML/JS 前端。

gradio.Server 在此扮演了关键的后端角色:

  1. 作为单个 HTML 文件提供阅读器视图。
  2. 通过一个队列化的端点暴露模型逻辑。

核心代码逻辑如下:

import gradio as gr
from fastapi.responses import HTMLResponse
from gradio.data_classes import FileData

server = gr.Server()

@server.get("/", response_class=HTMLResponse)
async def homepage():
    return FRONTEND_HTML # 阅读器视图

@server.api(name="analyze_document")
def analyze_document(file: FileData) -> dict:
    text = extract_text(file["path"]) # 使用 PyMuPDF / python-docx 提取文本
    source_text, spans = run_privacy_filter(text) # 单次 128k 传递
    return {
        "text": source_text,
        "spans": spans, # [{start, end, label}, ...]
        "stats": compute_stats(source_text, spans),
    }

关键点:使用 @server.api(name="analyze_document") 装饰器而非普通的 @server.post。这使得处理器能够接入 Gradio 的队列系统,确保并发上传被序列化,正确组合 @spaces.GPU 以适配 ZeroGPU,并允许浏览器和 gradio_client 通过同一端点访问,无需重复代码。前端通过 Gradio JS 客户端调用:

const result = await client.predict("/analyze_document", { file: handle_file(file) });

应用二:Image Anonymizer(图像匿名化工具)

用户痛点:用户希望分享包含 PII 的图像或截图(如 Slack 线程、收据、Stripe 仪表板),要求用黑色条块遮盖 PII。同时需要支持开关条块、拖拽调整位置、手动绘制遗漏区域,并导出结果。

Privacy Filter 的作用: 后端使用 Tesseract 进行 OCR 识别,返回每个单词的边界框。后端重建完整文本并建立字符偏移量到边界框的映射,然后对全文运行一次 Privacy Filter。检测到的字符跨度根据单词映射表转换为像素级别的矩形框。

gradio.Server 的工程实现: 虽然 gr.ImageEditor 支持分层注释,但为了满足特定工作流(每个条块的类别元数据、一键切换某类条块、客户端原生分辨率 PNG 导出且无需服务器往返),开发者在自定义 <canvas> 前端上构建了更清晰的逻辑。

gradio.Server 从一个队列化端点返回像素矩形框,并将其他所有交互留在前端:

@server.api(name="anonymize_screenshot")
def anonymize_screenshot(image: FileData) -> dict:
    img = Image.open(image["path"]).convert("RGB")
    full_text, char_to_box = ocr_image(img) # 单词边界框 + 字符映射
    spans = run_privacy_filter(full_text)
    boxes = spans_to_pixel_boxes(spans, char_to_box)
    return {
        "image_data_url": pil_to_base64(img),
        "width": img.width,
        "height": img.height,
        "boxes": boxes, # [{x, y, w, h, label, text}, ...]
    }

前端调用模式与文档应用相同。所有的切换、拖拽、绘制和导出均在浏览器中完成,编辑操作无需往返服务器。

应用三:SmartRedact Paste(智能脱敏粘贴板)

用户痛点:需要一个在分享前自动脱敏的“粘贴板”。用户粘贴日志、邮件或工单后,获得两个 URL:一个是公开的,显示带有 <PRIVATE_PERSON> 等占位符的脱敏版本;另一个是私有的,由令牌保护,显示带有高亮跨度的原始文本。

Privacy Filter 的作用: 将每个检测到的跨度替换为 <CATEGORY> 占位符。该过程支持多语言文本(西班牙语、法语、中文、印地语等),无需额外配置。

gradio.Server 的工程实现: 此应用需要两个不同的 GET 路由用于同一个粘贴 ID:一个公开,一个受令牌保护。由于 gradio.Server 底层基于 FastAPI,因此 @server.api(模型调用)和普通的 @server.get(静态路由)可以在同一个进程中并存。

核心逻辑分为两部分:

  1. 创建粘贴(模型调用):通过 @server.api 暴露,接入队列。
    @server.api(name="create_paste")
    def create_paste(text: str, ttl: str = "never") -> dict:
        source_text, spans = run_privacy_filter(text)
        redacted = redact(source_text, spans) # 生成占位符
        pid, reveal_token = secrets.token_urlsafe(6), secrets.token_urlsafe(22)
        # 存储逻辑...
        return {
            "view_path": f"/view/{pid}",
            "reveal_path": f"/view/{pid}?token={reveal_token}",
        }
    
  2. 查看页面(普通 FastAPI 路由):无需模型或队列,利用 FastAPI 原生特性处理 URL 形状 /view/{pid}?token=...
    @server.get("/view/{pid}", response_class=HTMLResponse)
    async def view_paste(pid: str, token: str | None = None):
        p = _store_get(pid)
        if p is None:
            return HTMLResponse(_not_found(), status_code=404)
        revealed = bool(token) and secrets.compare_digest
    
查看原文 →huggingface.co