跳到正文
RadarAI· Meta Engineering Blog(RSS)·· 4 小时前AI 评分34

Meta 详解 PrivacyLib:在数据访问边界执行隐私控制

AI 导读

Meta 详解 PrivacyLib,这是一个运行在数百个数据与处理系统客户端内、在数据访问点执行隐私控制的公共库,每秒执行数十亿次检查。系统只需接入一次,新隐私能力即以模块形式复用该集成,此前跨系统实现一项新要求可能耗时数月。PrivacyLib 还新增 Agent Observability 模块,用于观测内部 AI 智能体的数据访问事件。

正文 · AI 翻译

Meta Engineering Blog·2026-10-28 00:00· 1 小时前AI 评分43

PrivacyLib:数据访问边界处的隐私控制

AI 导读

Meta 详解 PrivacyLib,这是一个运行在数百个数据与处理系统客户端内、在数据访问点执行隐私控制的公共库,每秒执行数十亿次检查。系统只需接入一次,新隐私能力即以模块形式复用该集成,此前跨系统实现一项新要求可能耗时数月。PrivacyLib 还新增 Agent Observability 模块,用于观测内部 AI 智能体的数据访问事件。

正文 · 原文

  • 这篇对 Meta Privacy Aware Infrastructure (PAI) 的最新深度解读聚焦于 PrivacyLib。 
  • 当数据流经 Meta 多样化的数据与处理系统时,隐私要求必须始终得到满足。过去,支持一项新要求意味着要在每个系统中分别实现相同的执行逻辑。PrivacyLib 用运行在 Meta 数据与处理系统客户端内的公共库取代了这一做法。
  • 系统只需与 PrivacyLib 集成一次,新的隐私能力即以模块形式接入并复用该集成。PrivacyLib 每秒执行数十亿次检查,变更按模块进行模拟和发布。 
  • 对于数据访问路径可能无法预先知晓的内部 AI 智能体,受支持路径上的工具调用会携带智能体上下文传给 PrivacyLib。传播的上下文将每次访问与其会话关联起来,使模块能够观测数据使用情况并阻止不被允许的操作。

在 Meta,许多隐私要求最终都归结为同一个问题:我们能否证明某条数据只流向被允许的地方?在 Meta 的规模下,与其仅依赖可能因系统变化而失效的人工隐私审计,我们开发了 Privacy Aware Infrastructure (PAI),在基础设施内部回答这一问题。PAI 提供高效可靠的一流隐私构造,并将其嵌入 Meta 的基础设施,使产品能够持续快速演进。

在之前的文章中,我们介绍了 数据理解、 数据血缘 和 Policy Zones(我们的信息流控制技术)在 PAI 中发挥的作用。现在,我们深入探讨 PrivacyLib,这是一个运行在数百个数据与处理系统客户端内、在数据访问点执行的公共库。

PrivacyLib 在更广泛的 PAI 工作流的最后阶段发挥着重要作用:持续执行并监控数据流。

本文重点介绍 PrivacyLib 在 PAI 工作流最后阶段中的作用。

在 PrivacyLib 之前,我们针对每项要求,将隐私执行逻辑分别集成到每个数据与处理系统的客户端中。采用这种方式,跨系统实现一项新要求可能需要数月。每项新要求,无论是目的限制还是数据流发现,都需要同一组能力:覆盖广泛的系统;在数周而非数月内完成部署;在每次数据访问时运行而不成为延迟问题;以及在不使整个集群宕机的情况下发布执行变更。随着要求和系统的演进,这种方式越来越难以扩展。

PrivacyLib 将系统集成与需求实现分离。每个系统我们只集成一次。当某个需求可以在数据访问点满足时,我们将其实现为一个 PrivacyLib 模块,并在已集成的系统间共享,这样新需求就不再需要在每个系统中单独实现。 

模块分为两类: 

  • 强制执行模块可以阻止数据访问。例如,Policy Zones 强制执行 目的限制。
  • 观测模块仅记录数据访问。例如,Privacy Probes 收集用于发现数据流的运行时信号。 

随着内部 AI agent 成为访问数据的新方式,我们还新增了 Agent Observability 模块,用于观测与 agent 相关的数据访问事件,并帮助保护 agent 可触及的数据。已集成 PrivacyLib 的系统无需新的集成工作即可获得该模块,这正是“一次集成”的价值所在:每项新的隐私能力都以模块形式到来,而不是作为一个项目。

PrivacyLib 取代了跨系统的重复实现。

PrivacyLib 在 Meta 规模下的工程挑战与方法

PrivacyLib 在 Meta 运行于广泛的数据访问路径之上:

  • 超过一百个用于访问数据服务的库,从键值存储到 AI 功能。
  • 数千个生产服务,运行着不同版本的已部署库。
  • 每天有数百万个资产被访问。
  • 每秒数十亿次检查。

在这样的规模下运行会带来三项工程挑战:

挑战 方法
覆盖范围: 系统可能拥有多个客户端库和遗留集成,接受直接网络调用,并提供对后端存储的直接数据库访问。所有这些都需要处理,以确保无论访问路径如何,需求都能得到满足。 我们在两个层面进行集成:在类型化客户端库层面,PrivacyLib 拥有完整的请求上下文;在其余 RPC 路径上,则在更低的网络边界处集成,以实现全面覆盖。每个已集成的系统将其原生 API 映射到 PrivacyLib 的通用操作,并提供其已配置模块所需的输入。这些就位后,同一个模块即可在多个系统中启用,而无需针对具体系统单独集成。
规模:在每秒数十亿次检查的情况下,即使每次检查的计算成本很小,在整个机群中也会变得相当可观。热路径中的每个服务都要承担这一成本。 我们在构建时将需求编译为其最简单的可求值形式,并从进程内缓存中回答大多数检查,因此常见路径避免了网络调用。
可靠性:隐私逻辑运行在关键系统的热路径中,而这些系统由各自按自身节奏发布的团队负责。变更必须能够独立测试并独立回滚,因为全机群回滚不能作为恢复手段。 强制执行逻辑作为拥有它的服务的一部分进行部署,因此变更通过该服务自身的流水线到达生产环境,而不是通过全机群配置推送。


演练:保护消息元数据

为了说明这一切如何在 Meta 的规模下协同运作,让我们走一遍在处理消息元数据时需要遵守的一项隐私要求。

用户通过 Messenger 等应用交换消息,该应用支持端到端加密。在 Messenger 路由和投递这些消息的过程中,在适用法律允许的范围内,其后端系统会处理非内容类消息数据(即消息元数据),例如关于消息发送或投递时间的信息,以提升产品性能、检测滥用行为并保护用户免受有害行为侵害。 

这些后端服务中有多种使用 Logger——一个运行在服务进程中的客户端库——将这些元数据写入数据仓库表。在写入记录之前,Logger 内部的 PrivacyLib 集成会调用执行模块 Policy Zones,检查该元数据是否可以流向目标表。这项检查分为两部分:做出决策需要哪些输入,以及该检查在运行时实际如何评估。在考察这两部分之后,我们再看 PrivacyLib 如何跨系统扩展,以及变更如何在不中断路径中服务的情况下推出。

PrivacyLib 运行在消息后端服务的 Logger 客户端内部。它将请求上下文与生成的目标元数据结合,调用为该写入配置的模块,并将执行决策返回给 Logger。

标注消息元数据

每个 Logger 目标表都有一个 LoggerConfig,用于描述写入其中的记录。MetadataTraceLoggerConfig 定义了这样一个针对消息投递追踪的配置,并将其标记为包含消息元数据:

final class MetadataTraceLoggerConfig extends LoggerConfigBase {
  use MessagingTrafficMetadataPolicyTrait;
  public function configure(
    LoggerFieldDefiner $fd,
  ): LoggerFieldDefiner {
    $this->annotateWithTableLevelPolicy();
    // Field and destination definitions
  }
}

一个日志目标可以有多个写入方,因此描述其输出数据的标注属于该目标,而不属于每个向其写入的服务。如果每个服务分别声明该标注,这些声明可能相互不一致,或者新的写入方可能完全遗漏它。在 LoggerConfig 中声明一次,就能让每个写入方获得对数据的相同描述。

对于此目标,MessagingTrafficMetadataPolicyTrait 提供了 MESSAGING_TRAFFIC_METADATA 表级标注,并由 annotateWithTableLevelPolicy() 将其附加到目标上。

LoggerConfig 是 Logger 自身的 schema,而每个系统都以自己的格式描述其数据。为了给 PrivacyLib 提供一种统一的消费形式,Logger 的代码生成流水线会将配置转换为 PrivacyLib 兼容的产物。生成的 MetadataTraceLoggerConfigPrivacyLibArtifact 派生自 MetadataTraceLoggerConfig ,并携带目标标识和消息标注:

/**
 * @codegen-source MetadataTraceLoggerConfig
 */
class MetadataTraceLoggerConfigPrivacyLibArtifact
  extends LoggerInputPrivacyLibArtifact {
  
  final public static function getAssetIDImpl(
  ): LoggerInputAssetID {
    return LoggerInputAssetID::get(
      'MetadataTraceLoggerConfig',
    );
  }

  protected static function getPurposePolicyVars(
  ): TPurposePolicyVars {
    return PurposePolicy::makeVars(
      TPurposePolicyName::MESSAGING_TRAFFIC_METADATA,
    );
  }

  // Other generated fields omitted.
}

上面生成的类由 Logger 的客户端在 Hack(Meta 的类型化 PHP 语言)中使用。对于其 C++ 和 Python 客户端,Logger 将目标标识和注解发布到中央目录。PrivacyLib 利用这些信息生成特定语言的产物。在下一节的 C++ 示例中,PrivacyLib 使用 LoggerConfig 名称来加载该目标的 C++ 产物。  

运行检查

上一节中的产物描述了一个 Logger 目标。PrivacyLib 还需要知道 Logger 写入的含义以及应运行哪些模块。Logger 的 Hack 集成一次性为所有目标定义了这一点:

$config->libraryConfig()->enablePrivacyLib(
  PrivacyLibOperation::write()
    ->input(
      PrivacyLibInput::instance(
        nameof ILoggerInputPrivacyLibPayloadProvider,
      ),
    )
    ->enforcements(
      PrivacyLibWriteEnforcement::zones(
        ZoneDataType::LOGGER,
      )
        ->withPropagation()
        ->enabled(),
      PrivacyLibWriteEnforcement::telemetryLogging()->enabled(),
      // Other modules
    ),
);

PrivacyLibOperation::write() 声明了 PrivacyLib 针对 Logger 的写操作,而 enforcements() 列出了在其上运行的模块。Policy Zones 模块可以影响结果,而 Telemetry Logging 模块记录评估过程但不影响结果。在此处添加模块无需更改各个目标或 Logger 调用点。ZoneDataType::LOGGER 告知 Policy Zones 它正在检查一个 Logger 目标。

在 C++ 服务中,Logger 按如下方式构造 PrivacyLib 请求: 

const auto assetList =
      std::vector<std::string>{configName};

PrivacyLibBuilder builder(
      "LOGGER_WRITE_CLIENT",
      assetList,
      LoggerWriteAPIEnumHelper::apiToHashedString(
          PolicyEnforcedLoggerWriteAPI::log),
      Caller::LOGGER,
      getLoggerWriteImplementation());

auto result =
      PrivacyLibRequest(std::move(builder)).dispatch();

PrivacyLib 使用 configName 来加载目标的产物。经过哈希的 PolicyEnforcedLoggerWriteAPI::log 值将 Logger 的 API 映射到 PrivacyLib 的写操作。其余构建器参数将 Logger 标识为调用方,并提供执行写入的实现。dispatch() 调用已配置的模块并记录其结果。对于 Policy Zones 模块,PrivacyLib 提供来自所生成产物的目标注解以及当前处理上下文。在此示例中,Policy Zones 评估两个条件:

  1. 当前上下文表示对消息传递元数据的允许使用。
  2. 目标产物带有 MESSAGING_TRAFFIC_METADATA 注解。

当启用强制执行时,如果任一条件不满足,Policy Zones 模块将拒绝该写入。PrivacyLib 将各强制执行模块的结果合并为一个响应。拒绝会在 Logger 将记录发送到下游之前停止写入。观测模块发出遥测数据而不影响响应。

深入探讨 Logger 写入时的模块评估。

跨系统扩展隐私检查

PrivacyLib 通过通用执行模型、轻量级评估与遥测,以及对绕过集成客户端的请求的覆盖,使隐私检查能够跨异构系统扩展。 

跨系统的通用执行模型

PrivacyLib 的集成涵盖 API 差异极大的各类系统:键值存储、图数据库、ML 流水线、文件系统以及像 Logger 这样的日志系统。每个集成将其访问点映射到五种通用操作之一,PrivacyLib 在每种操作处调用其模块:

  1. 读取: 在获取数据时运行。
  2. 写入: 在存储数据时运行。
  3. RPC: 在调用另一服务时运行。
  4. 请求入口: 在服务开始处理请求时运行一次。
  5. 内联执行:将特定计算封装在更大的请求内,例如评估某个功能。该计算期间进行的数据访问会使用其内联隐私上下文进行评估。

模块是针对这五种操作编写的,而不是针对各系统的原生 API。读取、写入和 RPC 还有操作后变体,因此观察模块可以在访问决策做出后运行。一旦系统提供了映射,每个兼容模块都可以复用它,这就是为什么在已集成的系统上启用额外模块只需几天。

PrivacyLib 运行在数据和处理系统的客户端内,因为客户端已经拥有模块所需的目标身份和请求上下文。将评估保留在同一进程内,可以避免在每次访问时将这些输入跨进程边界传递。这种设计还有两个优势:

  1. 更丰富的上下文:PrivacyLib 可以在调用方的身份、端点和目的信息仍然可用时使用它们。如果在数据存储上评估检查,这些信息通常会丢失。
  2. 故障隔离:有缺陷的变更被限制在运行该版本代码的服务内,使共享数据存储处于故障边界之外。

保持检查的计算开销低廉

进程内评估还必须在计算上高效,因为它会在每次数据访问时运行。每请求缓存消除了请求内的重复检查,每主机缓存则保留频繁使用的配置和结果。这避免了早期设计所需的远程配置获取(仅该获取就比进程内路径慢数千倍)。结果是,PrivacyLib 在整个集群中每秒执行数十亿次检查,每次检查延迟很低,同时仅消耗 Meta 总计算容量的一小部分。

在 PrivacyLib 的规模下,遥测也必须保持轻量。PrivacyLib 记录每个模块的状态、采样延迟并记录错误,使我们能够全集群了解执行在生产中的表现。完整详细地记录每次调用会使接收这些事件的系统不堪重负,因此目标是去重重复事件,同时保持罕见事件可见。 

两种机制协同实现这一点:每主机上的布隆过滤器以固定速率对高流量模式进行降采样,细粒度采样率则随流量变化而调整。

覆盖直接 RPC 路径

并非所有调用方都通过集成了 PrivacyLib 的客户端库访问数据;有些会发出原始 RPC。RPC 层仍然可以检查这些请求,但它看到的上下文远少于客户端。PrivacyLib 通过加密签名的令牌弥合这一差距:当客户端检查成功时,PrivacyLib 会将令牌附加到请求上,ServiceRouter 会在受支持的 RPC 流量的较低边界处验证它,确认该请求已经通过了更丰富的客户端检查。

当请求到达 RPC 层时如果没有 token,ServiceRouter 会退回到仅基于网络负载中可见上下文的较粗粒度检查。由于该上下文有限,较低层会采用最严格的解释,宁可误报也不留下缺口。两者结合,这两层覆盖了通过 Meta 管理的客户端和受支持的 RPC 路径的访问。超出这些边界的路径不受此机制覆盖,需要它们自己的 PrivacyLib 集成。

综合来看,结果是评估尽可能保持本地化且计算开销低,在本地评估不可用时有一个较粗粒度的后备保障,并且模块有一个统一的位置来跨那些原本毫无共享的系统运行。剩下的就是更棘手的运维问题:如何在不破坏线上流量或做出错误访问决策的情况下改变执行行为。

安全地推出执行变更

隐私需求会随时间变化。目标端注解或执行模块的更新可能会改变写入是否被允许。PrivacyLib 对这两类变更都有安全的推出路径。

更改注解

添加或移除注解会改变 LoggerConfig 中的两行:导入 MessagingTrafficMetadataPolicyTrait 的声明,以及对 annotateWithTableLevelPolicy() 的调用。由于目标端可能有许多写入方,这两行编辑可能同时开始阻止来自许多服务的写入。

因此,评审的设计围绕向作者和评审者展示该编辑实际会产生什么效果。在代码评审期间,Logger 的代码生成流水线会更新目标端的 PrivacyLib 产物。随后,模拟会针对新产物和现有产物重放观测到的数据流,并报告任何结果发生变化的流。源编辑、生成的产物和模拟结果会一起评审,因此决策依据的是变更效果的证据,而不仅仅是阅读代码变更。如果模拟报告了意外变化,评审者会在发布前暂扣该产物。

一旦获批,该产物会通过各服务通常的构建和部署流水线到达服务。这在变更部署前提供了两种可见性:模拟报告的决策变化,以及生成的代码准确展示将要部署的内容;外加一种控制手段:每个服务自己的部署决定变更何时到达生产环境。它还限定了影响范围:部署后发现的问题仅限于运行该构建的服务。

缺点是变更不会一次性到达整个集群,使得变更传播比全局配置更新更慢。在实践中,需求负责人使用显式跟踪来在要求的截止日期前更新范围内的服务,而需要更快获得更新的服务可以使用其紧急部署流程。

模拟在发布前揭示决策变化,而各服务独立部署控制时机以限制生产影响。

推出模块

一项注解变更适用于一个 Logger 目的地。添加到 Logger 写入路径的模块会对每个 Logger 目的地运行。因此,我们在所有流量上启用该模块之前先部署它。

举例来说,以下是在此路径上引入 Policy Zones 的方式。我们首先在 Logger 的 PrivacyLib 写操作配置中将 Policy Zones 模块设置为 rollingOut() :

PrivacyLibWriteEnforcement::zones(ZoneDataType::LOGGER)
    ->rollingOut()

发布键 logger.input.write.zones,标识了 Logger 输入、写操作和 Policy Zones 模块的这一组合。 rollingOut() 将模块执行置于运行时控制之后。随后,我们在监控拒绝、错误和延迟的同时,将该控制从 0 分阶段提升到 100%。如果有效写入开始失败,我们可以在不重建或重新部署服务的情况下降低百分比。Telemetry Logging 模块继续运行,因为它是独立配置的。 

一旦 Policy Zones 模块在 Logger 流量中稳定下来,我们便将配置从 rollingOut() 改为 enabled()。此时,Logger 集成运行该模块时不再参考发布百分比。

部署后,工程师使用运行时信号来增加、减少或停止模块发布,而无需重建或重新部署。

在上述两条变更路径中,防护措施适用于不同阶段。如果模拟显示意外的流变化,我们会在发布前暂扣注解产物。如果模块在发布开始后行为异常,我们使用运行时控制降低其发布百分比。

这些安全机制改变了在 Meta 满足隐私要求所需的条件。PrivacyLib 现在运行在数百个数据处理系统的客户端中,每秒评估数十亿次检查,同时只消耗 Meta 计算资源的一小部分。构建在其之上的要求,例如通过 Policy Zones 实现目的限制,以及通过 Privacy Probes 实现数据流发现,会复用这些集成而不是重新构建,因此在已覆盖的系统上启用一个模块只需数天,而不是过去定制集成所需的数月。当变更确实出错时,故障会留在运行已部署变更的服务内部。我们现在可以将这一基础用于更新的一类工作负载:AI 代理。

面向 AI 代理的 PrivacyLib 控制

AI 代理比传统服务更难推理,因为它们的行为是动态的,且它们访问的数据在运行时才确定。因此,我们必须预先为代理安全进行设计,通过强制执行 Policy Zones 等控制,不仅针对我们在构建代理时预期它们会访问的内容,也针对它们在运行时尝试访问的内容。在已启用的情况下,这些控制可以在返回任何数据之前阻止不允许的访问。

智能体调用工具来执行任务。单次工具调用可能以嵌套方式跨越多个服务,最终才到达数据。为应对这一新挑战,我们构建了 AgentLib——一个基于 PrivacyLib 设计原则的通用运行时,用于观测和治理内部智能体发起的工具调用。在受支持的路径上,AgentLib 会将每次工具调用与其智能体会话关联起来,下游客户端和服务会持续传递该上下文,直到调用到达集成了 PrivacyLib 的系统。在该数据访问边界处,PrivacyLib 会结合上下文、所请求的操作以及目的地的注解,然后将它们传递给各个模块以做出最终决策。

在 PrivacyLib 内部,Agent Observability 模块会记录智能体会话和工具调用标识符,以及目的地、客户端 API、集成点和任何错误。工程师可以按会话查询这些事件,以调查某次工具调用涉及的服务和目的地。当我们引入 Agent Observability 模块时,PrivacyLib 已有的广泛覆盖使我们能够为已集成该库的系统添加观测能力,而无需将该模块手动集成到数百个系统中。即便借助 AI 辅助开发,这也会是一项艰巨的任务。 

为了执行某些策略,我们未做修改地使用了现有的 Policy Zones 模块,因为它依赖相同的输入来执行。在启用该控制的路径上,它会在允许操作之前考虑目的地注解和智能体上下文。当 Policy Zones 阻止某项操作时,PrivacyLib 会将该结果沿调用链回传给智能体环境。即使智能体选择了其开发者未曾预料的工具,Policy Zones 也会应用此检查。访问决策不依赖于智能体是否按预期行事。

AgentLib 会将内部智能体发起的受支持工具调用与智能体上下文关联起来,服务会在数据访问边界处将该上下文传递给 PrivacyLib。PrivacyLib 利用该上下文进行观测和执行。

构建和运营 PrivacyLib 的经验教训

在 Meta 各系统中构建和运营 PrivacyLib 让我们学到了几条重要经验。

为不断演进的系统和需求而设计:在 PrivacyLib 之前,我们为满足每项新需求而在各个调用点添加隐私控制。随着时间推移,Meta 构建了新的数据处理系统,服务获得了新的数据处理方式,隐私要求也不断扩展。每一次变更都带来更多集成工作,而新的访问路径有时出现得比我们覆盖的速度还快。教训是:应在稳定的数据访问边界处集成,定义系统共享的少量操作,并将注解与数据放在一起。模块可以复用相同的集成,从而随时间推移减少工程投入。

可靠性赢得采用:早期,一个无效配置导致整个机群的进程崩溃,违背了我们自己的安全发布原则。其他事故则源于注解变更——它们编译成功,却改变了访问决策并导致产品故障。这些事故表明,只有当服务所有者能够在发布前看到变更如何影响其流量,并且能在不进行全机群回滚的情况下从有问题的变更中恢复时,他们才会信任 PrivacyLib 处于关键数据访问路径中。 

覆盖范围包括部署: 当新需求需要对模块或受支持的策略与注解进行更改时,较旧的服务需要部署具备更新后 PrivacyLib 能力的版本。我们已确保建立明确的清单、跟踪和发布机制来协调这些部署,以便需求负责人能够通过仅更新范围内的服务来安全地满足截止日期。

展望未来

这些经验塑造了 PrivacyLib 接下来的发展方向,因为 AI 正将数据访问推向日益新颖的环境和调用路径。我们继续在两个方面丰富 PrivacyLib 的能力:

  1. AI 训练: 用于训练的数据从各种环境进入我们的系统。其中大部分数据在不同地点产生或转换。我们正在将 PrivacyLib 风格的方法扩展到这一领域,以便同样的保障能够随之扩展。有关 Meta 如何处理 AI 训练数据的更多信息,请参阅 AI at Meta:关于我们训练数据的透明度。
  2. 内部 AI 代理: 观察尝试的数据访问并阻止不允许的访问,对于 Meta 内部用于工程和生产力场景的代理而言,只是一个起点,而非完整的图景。更困难的问题随之而来:在内部代理行为发生时对其进行评估,以及推理这些代理之间的通信——其中一次数据访问可能距离提出请求的人相隔多个代理。

致谢

作者谨此感谢许多现任和前任 Meta 员工,他们多年来在开发 PrivacyLib 中发挥了关键作用。我们特别感谢(按字母顺序)Adwait Sathe、Andrew Vaslas、Darrien Glasser、Diana Marsala、Hanzhi Zhang、Kousik Nandy、Matthew Hillman、Matthieu Martin、Michael Dietz、Nathan Maue、Ramy Wassef、Sen Luo、Spencer Baumgardner、Vince Yang 和 Zhong Hu。我们还要感谢本博文的所有审阅者,包括(按字母顺序)Aleksandar Ilic、Alex Kagan、Alex Ponomarenko、Arash Nikkar、Avtar Brar、Chloe Lu、Chris Wiltz、Emil Vazquez、Emily Van Deuren、Rituraj Kirti、Sushaant Mujoo 和 Wenlong Dong。我们特别感谢 Jonathan Bergeron 监督这项工作并提供所有指导和宝贵反馈,以及 Chloe Lu 汇集所需支持使本博文得以发布。

文章 PrivacyLib:数据访问边界处的隐私控制 首次发表于 Engineering at Meta。

来源:Meta Engineering Blog · engineering.fb.com

来源:RadarAI · aihot.news