Appearance
transformers.js:在浏览器里跑模型
Ollama 解决了"本地",但还需要装一个后台服务。transformers.js 把 HuggingFace 的模型推到浏览器里跑,连 Node 都不需要——纯前端 + WebAssembly + WebGPU。
适合场景
- 用户自己上传文档/图片做处理(隐私敏感);
- 离线 PDF 阅读器、本地翻译插件、浏览器扩展;
- RAG 应用的查询端嵌入(节省服务端调用);
- 需要"零后端"演示页。
不适合场景
- 大型对话模型(>3B 参数)——Web 端跑起来非常慢;
- 重计算长任务——浏览器比原生慢 3-5 倍;
- 需要稳定 throughput 的生产服务。
一、能跑什么
transformers.js(v3+)支持的任务覆盖:
- 嵌入(embedding)——
bge-small-zh、nomic-embed-text等小模型,这是最好的入门点; - 文本生成 ——
Qwen2.5-0.5B、SmolLM2-360M这类小模型,做摘要/分类/简单问答; - 翻译 —— 多语言模型;
- 图像分类 / OCR —— 用
Florence-2、TrOCR; - 语音转文字 ——
whisper-tiny/whisper-base在浏览器里就能跑; - 图像生成 —— 极小的
SD-Turbo之类的,速度感人但能跑。
二、最小可运行:浏览器里嵌入一段中文
bash
npm i @huggingface/transformersts
import { pipeline } from '@huggingface/transformers';
// 第一次会下模型,缓存在 IndexedDB(约 50MB)
const embed = await pipeline(
'feature-extraction',
'Xenova/bge-small-zh-v1.5',
{ device: 'webgpu' }, // 没 WebGPU 自动 fallback 到 wasm
);
const result = await embed('前端 RAG 的核心是检索召回质量', {
pooling: 'mean',
normalize: true,
});
console.log(result.data); // Float32Array(512)512 维向量,可以直接塞进任何向量库做检索。
三、性能实测(M2 Pro / Chrome 130)
我用同一段 1000 字中文做嵌入,比较三种方式:
| 方式 | 加载时间 | 单次推理 | 备注 |
|---|---|---|---|
| transformers.js (wasm) | ~2.5s | 380ms | 无加速 |
| transformers.js (webgpu) | ~2.5s | 95ms | 推荐 |
| Ollama + nomic-embed | (常驻) | 30ms | 本地最快 |
| 调 OpenAI text-embedding-3-small | (网络) | 200ms | 加上网络往返 |
结论:纯客户端跑嵌入,95ms 已经完全可以做实时检索了。文本生成会差很多,3B 以下模型勉强能用。
四、和 Ollama 怎么分工
不要重复,直接看场景:
| 场景 | 用啥 |
|---|---|
| Chat 主对话 | Ollama 或云 API |
| RAG 文档嵌入(离线一次性建索引) | Ollama |
| RAG 查询嵌入(用户每次提问) | transformers.js——省去后端往返 |
| 用户本地文件不上传服务器 | transformers.js |
| 浏览器扩展、Web App 完全离线 | transformers.js |
| 公司内服务、有后台 | Ollama |
实际产品里两者经常配合:服务端用 Ollama 建索引,前端用 transformers.js 做查询,全程不出公司网络。
五、模型加载与缓存
pipeline() 第一次调用会从 HuggingFace CDN 下载模型,存到 IndexedDB。第二次访问直接命中缓存,毫秒级加载。
清缓存(开发时常用):
ts
import { env } from '@huggingface/transformers';
await env.useBrowserCache(false); // 临时禁用或开发者工具 → Application → IndexedDB 删 transformers-cache。
自托管模型避免 HF 不可访问
国内访问 HuggingFace 不稳定。可以:
- 把模型文件下载下来传到自家 CDN;
- 配置
env.remoteHost:
ts
import { env } from '@huggingface/transformers';
env.remoteHost = 'https://your-cdn.com/models';
env.allowRemoteModels = true;- 模型文件结构跟 HF 仓库一致即可。
六、WebGPU vs WASM 怎么选
| 浏览器 | WebGPU 支持 |
|---|---|
| Chrome / Edge 113+ | ✅ |
| Safari 18+ | ✅(macOS 15 起) |
| Firefox | 🚧 实验性,nightly 可用 |
代码里默认 device: 'webgpu',库会自动 fallback:
ts
const embed = await pipeline(
'feature-extraction',
'Xenova/bge-small-zh-v1.5',
{ device: 'webgpu', dtype: 'fp16' }, // dtype 在 webgpu 下可选 fp16,更快
);WASM 模式下用 dtype: 'q8'(8-bit 量化)能让小模型快 2 倍,但精度稍降。嵌入任务通常没差。
七、写一个浏览器内 RAG 最小 Demo
html
<input id="question" placeholder="问点啥…" />
<button id="ask">检索</button>
<ul id="hits"></ul>
<script type="module">
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers';
// 已经预先建好的"知识库"(演示用,真实场景是从 IndexedDB 取)
const docs = [
{ id: 1, text: '前端流式渲染:SSE 与 WebSocket 的选型...' },
{ id: 2, text: 'Ollama 让你在本地跑开发助手...' },
{ id: 3, text: 'MCP 协议把 LLM 工具调用标准化...' },
// ... 假设有 500 条
];
// 准备阶段:把每条 doc 变成向量
const embed = await pipeline(
'feature-extraction',
'Xenova/bge-small-zh-v1.5',
{ device: 'webgpu' },
);
for (const d of docs) {
const r = await embed(d.text, { pooling: 'mean', normalize: true });
d.vec = r.data;
}
document.getElementById('ask').onclick = async () => {
const q = document.getElementById('question').value;
const r = await embed(q, { pooling: 'mean', normalize: true });
const qVec = r.data;
// 余弦相似度(已 normalize,等于点积)
const ranked = docs
.map((d) => ({
...d,
score: d.vec.reduce((sum, v, i) => sum + v * qVec[i], 0),
}))
.sort((a, b) => b.score - a.score)
.slice(0, 3);
document.getElementById('hits').innerHTML = ranked
.map((h) => `<li>${h.score.toFixed(3)} — ${h.text}</li>`)
.join('');
};
</script>这个 demo 全部跑在浏览器里,连接断网都能用。500 条 doc 的查询延迟约 100ms,体验和服务端检索几乎无差。
八、踩坑清单
- 首次加载慢——50MB 模型在弱网下要等十几秒。一定要做加载进度条:
ts
const embed = await pipeline('feature-extraction', model, {
progress_callback: (p) => console.log(p.status, p.progress),
});IndexedDB 配额——浏览器对 IndexedDB 有配额限制,多个模型可能被自动清理。重要场景用 Persistent Storage API 申请持久化。
WebGPU 偶尔 crash——尤其是切换标签页或休眠后。封一层 try/catch,crash 后 fallback 到 wasm。
大模型不要在浏览器跑——3B 以上的模型在 Chrome 上加载基本会卡死。需要更大模型时上 Ollama。
CSP——transformers.js 加载模型时会用 fetch 拿 weights,部署环境的 CSP 要 allowlist
huggingface.co或你自己的 CDN。
九、什么模型值得收藏
按 transformers.js 兼容性 + 体积排:
- 嵌入:
Xenova/bge-small-zh-v1.5(中文)、Xenova/all-MiniLM-L6-v2(英文,22MB) - 小生成:
onnx-community/Qwen2.5-0.5B-Instruct、HuggingFaceTB/SmolLM2-360M-Instruct - OCR:
Xenova/trocr-small-printed - 语音:
Xenova/whisper-tiny.en(39MB,英文够用)
去 HuggingFace 搜带 Xenova/ 或 onnx-community/ 前缀的仓库——这些是社区为 transformers.js 转换好的 ONNX 版本。
