在 Transformers.js 中尝试提议的跨源存储 API

Hugging Face Blog 工具

摘要

这篇客座文章探讨了提议的跨源存储API,用于改进Transformers.js中AI模型资源的缓存,从而实现跨源的高效复用,同时保持浏览器内推理的隐私和完整性。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/23 19:41

在 Transformers.js 中试验提议中的跨源存储 API

来源:https://huggingface.co/blog/cross-origin-storage 返回文章列表 (https://huggingface.co/blog)

https://huggingface.co/login?next=%2Fblog%2Fcross-origin-storage-

Thomas Steiner 的头像 (https://huggingface.co/tomayac)

  • 缓存挑战 (https://huggingface.co/blog/cross-origin-storage#the-cache-challenge)- 模型资源 (https://huggingface.co/blog/cross-origin-storage#model-resources) - Wasm 运行时资源 (https://huggingface.co/blog/cross-origin-storage#wasm-runtime-resources) - 缓存隔离 (https://huggingface.co/blog/cross-origin-storage#cache-isolation)
  • 进入跨源存储 API (https://huggingface.co/blog/cross-origin-storage#enter-the-cross-origin-storage-api)- 控制谁能读取什么 (https://huggingface.co/blog/cross-origin-storage#control-who-can-read-what) - 设计保证完整性 (https://huggingface.co/blog/cross-origin-storage#integrity-by-design) - 不牺牲实用性的隐私 (https://huggingface.co/blog/cross-origin-storage#privacy-without-sacrificing-utility) - 这对 Transformers.js 示例意味着什么 (https://huggingface.co/blog/cross-origin-storage#what-this-means-for-the-transformersjs-example) - 今天就能试用 (https://huggingface.co/blog/cross-origin-storage#try-it-today)
  • 行动号召 (https://huggingface.co/blog/cross-origin-storage#call-to-action)

(这是一篇由 Google Chrome 团队的开发者关系工程师 Thomas Steiner (https://blog.tomayac.com/) 撰写的客座文章。)

Transformers.js 为 Web 开发者提供了一种简单的方式,通过特定任务的管道(pipeline)在 Web 应用中运用 Transformer 的强大能力。为了在浏览器中运行推理,开发者创建 pipeline() (https://huggingface.co/docs/transformers.js/en/api/pipelines) 的实例,并指定要使用的任务。作为一个具体示例,以下代码片段展示了如何设置一个自动语音识别(ASR)管道。

`` import { pipeline } from ‘https://cdn.jsdelivr.net/npm/@huggingface/[email protected]’;

const asr = await pipeline( ‘automatic-speech-recognition’, ‘Xenova/whisper-tiny.en’, { device: ‘webgpu’ }, ); const result = await asr(‘jfk.wav’); console.log(result); ``

自动语音识别管道的极简示例。(https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cross-origin-storage/87a91qnbicf.png)

https://huggingface.co/blog/cross-origin-storage#the-cache-challenge 缓存挑战

你会注意到在源代码中我指定了 Xenova/whisper\-tiny\.en (https://huggingface.co/Xenova/whisper-tiny.en) 作为模型,这对于常见的英语自动语音识别任务来说是一个非常合适的选择。事实上,它甚至 Transformers.js 默认模型解析 (https://github.com/huggingface/transformers.js/blob/main/packages/transformers/src/pipelines/index.js) 中的 默认 模型,正如链接的片段 (https://github.com/huggingface/transformers.js/blob/bc9cf7400f4f2c8695016699f879e31026ff0313/packages/transformers/src/pipelines/index.js#L151-L158) 所示。

https://huggingface.co/blog/cross-origin-storage#model-resources 模型资源

当你 在浏览器中运行这个示例 (https://googlechrome.github.io/samples/transformersjs-automatic-speech-recognition/index.html) 时,Transformers.js 会自动处理相关模型资源和 Wasm 文件的下载与缓存。以下截图展示了访问应用后的 Chrome DevTools 缓存存储 (https://developer.chrome.com/docs/devtools/storage/cache) 部分。当你重新加载页面时,资源将从 Cache API (https://developer.mozilla.org/en-US/docs/Web/API/Cache) 中提供,模型几乎立即返回结果。

访问应用后,Chrome DevTools 缓存存储部分显示 Whisper AI 模型资源和 Wasm 运行时文件。(https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cross-origin-storage/otd8tt1gusb.png)

然而,Xenova/whisper\-tiny\.en 是一个流行的模型(如前所述,甚至是 Transformers.js 中的 默认 ASR 模型),你可以想象,不止一个你访问的应用会使用它。为了模拟这种情况,下面是之前的同一个示例应用,但来自一个 不同的源 (https://rawcdn.rawgit.net/GoogleChrome/samples/c4192bd7a3c66fc288a7b22b77acb935df00b8a1/transformersjs-automatic-speech-recognition/index.html)。当你访问这个不同源的应用时,浏览器并不会几乎立即可用,而是必须再次下载并缓存所有模型资源,即使它们与之前逐字节相同。即使在这个玩具示例中,这也导致了 177 MB 的重复下载和存储,你可以在 Chrome DevTools 的 Application 面板 (https://developer.chrome.com/docs/devtools/application#open_the_application_panel) 的 存储 部分查看。你可以想象这会迅速累积。

Chrome DevTools 存储概览显示使用了 177 MB 的存储空间。(https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cross-origin-storage/9byoniem0pw.png)

https://huggingface.co/blog/cross-origin-storage#wasm-runtime-resources Wasm 运行时资源

但情况更糟。让我们向玩具示例添加第二个管道:情感分析。情感分析 默认情况下 (https://github.com/huggingface/transformers.js/blob/bc9cf7400f4f2c8695016699f879e31026ff0313/packages/transformers/src/pipelines/index.js#L65) 使用 Xenova/distilbert\-base\-uncased\-finetuned\-sst\-2\-english (https://huggingface.co/Xenova/distilbert-base-uncased-finetuned-sst-2-english) 模型。通过不指定模型,Transformers.js 的默认模型解析会自动为你选择它。

const classifier = await pipeline('sentiment-analysis'); const sentiment = await classifier(result.text); pre.append('\n\n' + JSON.stringify(sentiment, null, 2));

图片 (https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cross-origin-storage/le7l1km7o4g.png)

两个完全不同的 AI 模型,但它们依赖于相同的 4,733 kB 的 ort\-wasm\-simd\-threaded\.asyncify\.wasm WebAssembly (Wasm) 运行时文件,该文件来自 Transformers.js 所基于的底层 ONNX Runtime 库 (https://onnxruntime.ai/docs/api/js/interfaces/Env.WasmFilePaths.html#wasm)。打开 扩展后的演示 (https://rawcdn.rawgit.net/GoogleChrome/samples/d47114a15637383015c274e7bdcd81e1a17b0ccf/transformersjs-automatic-speech-recognition/index2.html),你会注意到在 Network 面板 (https://developer.chrome.com/docs/devtools/network#load) 中,Wasm 运行时也会被再次下载和缓存。

Chrome DevTools Network 面板显示 Wasm 运行时资源的下载。(https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cross-origin-storage/pz12g20fqeg.png)

因此,即使你运行的应用不共享相同的 AI 模型,你的浏览器仍然会对你已经拥有的共享 Wasm 资源发出冗余请求,并且还会再次缓存它们,从而消耗硬盘空间。

https://huggingface.co/blog/cross-origin-storage#cache-isolation 缓存隔离

https://huggingface.co/blog/cross-origin-storage#ai-model-resources-serving AI 模型资源服务

默认情况下,AI 模型资源 来自 Hugging Face Hub (https://huggingface.co/docs/hub/en/models-the-hub),最终来自 Hugging Face CDN。浏览器会请求像 https://huggingface\.co/Xenova/distilbert\-base\-uncased\-finetuned\-sst\-2\-english/resolve/main/config\.json (https://huggingface.co/Xenova/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/config.json) 这样的资源,然后该请求会被重定向到最终的 CDN URL,例如 https://huggingface\.co/api/resolve\-cache/models/Xenova/distilbert\-base\-uncased\-finetuned\-sst\-2\-english/0b6928efcb76139cae2c6881d49cda67fe119f42/config\.json?%2FXenova%2Fdistilbert\-base\-uncased\-finetuned\-sst\-2\-english%2Fresolve%2Fmain%2Fconfig\.json=&etag=%223c36342ef1f74de2797d667c68c6b7b988d0b87c%22 (https://huggingface.co/api/resolve-cache/models/Xenova/distilbert-base-uncased-finetuned-sst-2-english/0b6928efcb76139cae2c6881d49cda67fe119f42/config.json?%2FXenova%2Fdistilbert-base-uncased-finetuned-sst-2-english%2Fresolve%2Fmain%2Fconfig.json=&etag=%223c36342ef1f74de2797d667c68c6b7b988d0b87c%22)。

https://huggingface.co/blog/cross-origin-storage#wasm-runtime-resources-serving Wasm 运行时资源服务

默认情况下,Wasm 运行时资源 来自 jsDelivr CDN (https://www.jsdelivr.com/)。例如,在撰写本文时,ort\-wasm\-simd\-threaded\.asyncify\.wasm 来自 https://cdn\.jsdelivr\.net/npm/onnxruntime\-web@1\.26\.0\-dev\.20260416\-b7804b056c/dist/ort\-wasm\-simd\-threaded\.asyncify\.wasm (https://cdn.jsdelivr.net/npm/[email protected]/dist/ort-wasm-simd-threaded.asyncify.wasm)。

现在你可能会说,如果不同的应用,即使运行在不同的源上,最终都从相同的 CDN URL 提供资源,只要最终的 URL 相同,缓存就不应该是问题。不幸的是,长期以来浏览器的缓存机制并非如此。文章《通过分区缓存获得安全性和隐私》 (https://developer.chrome.com/blog/http-cache-partitioning) 详细介绍了所有细节,但本质上,缓存是按源隔离的,以防止定时攻击:网站响应 HTTP 请求所花费的时间可以揭示浏览器过去是否访问过相同的资源,这会使浏览器容易受到安全性和隐私泄露的攻击。

https://huggingface.co/blog/cross-origin-storage#chromes-implementation Chrome 的实现

具体实现可能因浏览器而异,但在 Chrome 中,缓存的资源除了使用 资源 URL 外,还使用网络隔离密钥 (Network Isolation Key) 作为键。网络隔离密钥由 顶级站点当前帧站点 组成。以前面托管在 https://googlechrome\.github\.iohttps://rawcdn\.rawgit\.net 的玩具示例为例。如果它们都使用来自 https://cdn\.jsdelivr\.net/npm/onnxruntime\-web@1\.26\.0\-dev\.20260416\-b7804b056c/dist/ort\-wasm\-simd\-threaded\.asyncify\.wasm 的 Wasm 运行时,它们的缓存键将如下表所示。

网络隔离密钥资源 URL
顶级站点当前帧站点
https://googlechrome.github.iohttps://googlechrome.github.io
https://cdn.jsdelivr.net/npm/[email protected]/dist/ort-wasm-simd-threaded.asyncify.wasm
https://rawcdn.rawgit.nethttps://rawcdn.rawgit.net
https://cdn.jsdelivr.net/npm/[email protected]/dist/ort-wasm-simd-threaded.asyncify.wasm

因此,即使资源 URL 完全相同,由于网络隔离密钥不匹配,也不会产生缓存命中,这意味着重复下载和重复存储。这就是跨源存储提案旨在解决的挑战。

https://huggingface.co/blog/cross-origin-storage#enter-the-cross-origin-storage-api 进入跨源存储 API

💡 注意: 跨源存储 API 是一个早期阶段的提案,尚未最终确定。虽然提议的 API 尚未在任何浏览器中原生实现,但你无需等待即可进行试验。安装 跨源存储扩展 (https://chromewebstore.google.com/detail/cross-origin-storage/denpnpcgjgikjpoglpjefakmdcbmlgih) 可在所有页面上注入 navigator\.crossOriginStorage polyfill,并测试完整的流程。

提议的 跨源存储(Cross-Origin Storage)(https://github.com/WICG/cross-origin-storage)(COS)API 引入了一个专用的 navigator\.crossOriginStorage 接口,通过该接口,Web 应用可以跨源边界存储和检索大文件,文件的标识不是通过 URL,而是通过加密哈希。

跨源存储 API 的标志:一个风格化的行人,就像通常在人行横道标志上看到的那样。

关于加密哈希的最后一点是关键。因为 COS 通过文件的 哈希 而不是 URL 或源来标识文件,所以你在访问 https://googlechrome\.github\.io 时下载的同一个 ort\-wasm\-simd\-threaded\.asyncify\.wasm Wasm 运行时,会被识别为与 https://rawcdn\.rawgit\.net 即将请求的那个相同,无论这两个源中的哪一个获取了它。请参见以下说明基本流程的代码片段。

`` const hash = { algorithm: ‘SHA-256’, value: ‘8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4’, };

try { const handle = await navigator.crossOriginStorage.requestFileHandle(hash); // 缓存命中!以 Blob 形式获取文件并直接使用。 const fileBlob = await handle.getFile(); } catch (err) { // 缓存未命中。从网络下载,然后为下次使用存储。 const fileBlob = await fetch(‘https://cdn.jsdelivr.net/…/ort-wasm-simd-threaded.asyncify.wasm’) .then(r => r.blob()); const handle = await navigator.crossOriginStorage.requestFileHandle( hash, { create: true, origins: ‘*’ }, ); const writableStream = await handle.createWritable(); await writableStream.write(fileBlob); await writableStream.close();
} ``

如果资源在 COS 中,你将返回一个 FileSystemFileHandle (https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle),你可以通过 getFile() (https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/getFile) 直接读取 blob(生成的 File (https://developer.mozilla.org/en-US/docs/Web/API/File) 继承自 Blob (https://developer.mozilla.org/en-US/docs/Web/API/Blob))。如果资源不在 COS 中,你将回退到网络,并将资源写入 COS,以供下一个需要它的应用使用,这可能是你的应用,也可能是另一个无关的应用,潜在地来自完全不同的源。

该 API 有意模仿你可能熟悉的 文件系统标准 (https://fs.spec.whatwg.org/) 中的 FileSystemDirectoryHandle\.getFileHandle() (https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getFileHandle) ,该标准来自 源私有文件系统 (Origin Private File System, OPFS) API (https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)。hash 参数扮演着与 OPFS 中 name 参数相同的角色:唯一标识一个资源。options\.create 标志的工作方式相同:缺失或 false 表示只读访问,true 表示你打算写入。

https://huggingface.co/blog/cross-origin-storage#control-who-can-read-what 控制谁能读取什么

并非每个资源都应该全局共享。COS 允许开发者在存储文件时通过 origins 选项精确控制可见性。

  • 设置 origins: '\*' 使文件 全局可用。任何源都可以通过哈希找到它。对于 Transformers.js 示例中的 AI 模型资源或 Wasm 运行时来说,这是正确的选择:关键是 Web 上的每个应用都能从单个缓存副本中受益。
  • 传递一个特定的源列表,例如 origins: \['https://write\.example\.com', 'https://calculate\.example\.com'\]限制 了访问仅限于这些站点。这对于公司自有属性之间共享的专有资源效果很好,这些资源不应被任何人发现,比如商业办公套件中使用的专有校对 AI 模型。
  • 完全省略 origins 会使文件仅对 同站点 (same-site) (https://web.dev/articles/same-site-same-origin#same-site-cross-site) 源 可用。这是组织内所有子域之间共享资源的合理默认设置,但不打算跨越组织边界。

一个重要

相似文章

如何在 Chrome 扩展中使用 Transformers.js

Hugging Face Blog

本文提供了一份技术指南,介绍如何将 Transformers.js 集成到基于 Manifest V3 的 Chrome 扩展中,详细阐述了后台 Service Worker 架构、模型缓存和 Agent 循环的设计。

意图原型:嵌入API

Lobsters Hottest

Chromium团队为Web平台提出了一种新的嵌入API,允许开发者利用Chrome的AI基础设施在设备端生成向量嵌入,从而实现保护隐私的语义搜索、检索增强生成和内容聚类,同时降低延迟和成本。