大模型端侧推理的前端落地:WebGPU 与 ONNX Runtime 的浏览器部署
一、为什么把大模型塞进浏览器:云端那套账算不平了
去年底,一个面向律师的合同审查工具上线首日就挂掉了。问题不在模型,是云端推理的 P99 延迟从 800 毫秒飙到 6 秒,弱网下甚至超时。客服接到的投诉是一句「点完按钮我以为死机了」。这事我见过太多团队栽进去------首字体验比模型准确率更影响留存。
更麻烦的是成本账单。把大模型 API 直接暴露给前端,单次会话消耗的 token 折算下来,单用户月度成本比会员费还高。某 SaaS 团队复盘过一次,仅一周的免费试用就吃掉两个月预算。
于是端侧推理被推到台前。WebGPU 让浏览器首次拥有了接近原生的并行计算能力,配合 ONNX Runtime Web 这类推理引擎,量化到 INT8 的小模型完全可以在笔记本甚至手机上实时跑通。把延迟、成本、隐私这三块同时压下去,收益是看得见的。
但这没这么简单。端侧受限于设备内存、算力与电量。把 70B 模型塞进浏览器既不现实也无必要。适用的只是参数小、对延迟敏感、数据不宜出端的轻量任务,本地翻译、文本分类、语音唤醒、轻量对话。其余的,仍要留给云端。
二、从模型文件到张量计算:端侧推理的完整链路
端侧推理的完整链路,是把训练框架导出的模型转成开放格式(ONNX),再由浏览器推理引擎加载、用 WebGPU 后端执行。前端的核心职责是:获取模型、准备输入张量、调度推理、回收资源,并在不支持 WebGPU 时降级到 WASM。
WebGPU 之所以关键,是它提供了 GPUComputePass 与显存缓冲区,让矩阵乘法这类张量运算跑在 GPU 上,吞吐远超 CPU。但浏览器里的 GPU 显存有限(通常数百 MB 到数 GB),模型权重与中间激活值必须精打细算。量化能把模型体积与显存占用砍掉三到四倍,是端侧可行的前提。
另一个常被忽略的是「预热」。WebGPU 首次创建管线涉及着色器编译,可能耗数百毫秒。应在应用空闲时预先 session.run 一次空输入做 warmup,避免用户首次使用时被卡顿吓退。
三、生产级端侧推理封装:缓存、超时与后端降级
下面给出一个基于 ONNX Runtime Web 的端侧推理封装。它处理了生产环境三大隐患:模型大文件断点续传与缓存、WebGPU 不可用时的 WASM 降级、以及推理超时保护,避免低端设备上的「假死」。
typescript
import * as ort from 'onnxruntime-web';
// 带缓存与超时的模型加载:大模型权重动辄数十 MB,必须复用
async function loadSession(modelUrl: string): Promise<ort.InferenceSession> {
// 优先用 Cache Storage 缓存模型,避免重复下载浪费流量与等待
const cache = await caches.open('ort-models');
let resp = await cache.match(modelUrl);
if (!resp) {
// 用 fetch 自带 Range 支持,弱网中断可借 service worker 续传
resp = await fetch(modelUrl);
if (!resp.ok) throw new Error(`模型下载失败: ${resp.status}`);
await cache.put(modelUrl, resp.clone());
}
const buf = await resp.arrayBuffer();
// 后端优先级:WebGPU 优先,失败回退 wasm,保证可用性是第一原则
try {
return await ort.InferenceSession.create(buf, { executionProviders: ['webgpu'] });
} catch {
return await ort.InferenceSession.create(buf, { executionProviders: ['wasm'] });
}
}
// 带超时与取消的推理调用:低端设备推理可能极慢,必须设上限
async function infer(
session: ort.InferenceSession,
input: ort.Tensor,
timeoutMs = 5000
): Promise<ort.Tensor> {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
try {
// 用 Promise.race 包裹,超时即抛错,让上层降级到云端或提示
const result = await Promise.race([
session.run({ input }),
new Promise<never>((_, rej) =>
ctrl.signal.addEventListener('abort', () => rej(new Error('端侧推理超时')))
),
]);
return result.output as ort.Tensor;
} finally {
clearTimeout(timer);
}
}
这段实现的三处关键:第一,caches 把大模型缓到本地,二次加载近乎瞬时;第二,WebGPU 初始化包在 try/catch 里,失败自动退到 WASM,把「可用性」置于「性能」之前;第三,Promise.race + AbortController 给推理设硬超时,防止低端设备无限阻塞主线程,超时后上层可平滑切回云端 API。
四、端侧架构的边界:何时不该上浏览器
端侧推理有清晰的适用边界,越界反而带来灾难。
第一是模型规模。通常 5 亿参数以内、INT8 量化后 < 200MB 的模型才适合端侧;更大的模型显存放不下,加载也慢得不可用。这类任务应留在云端,端侧只做轻量预处理。
第二是设备差异。同一套 WebGPU 代码,在集显笔记本与旗舰手机上性能可能差十倍。必须做运行时能力探测与分级:探测到算力不足就主动降级到 WASM 或直接云推理,而不是让用户盯着转圈。某电商客户端曾因此被投诉------导购助手在中端安卓机上要等 12 秒,比人工还慢。
第三是更新与一致性。端侧模型缓存在用户设备,模型升级后旧版本会长期滞留。需要版本号校验,发现缓存模型版本不匹配就强制刷新,否则会出现「同输入不同结果」的诡异问题。
第四是电量与发热。持续 GPU 推理会显著耗电发热,移动端长时间使用会触发降频,反而变慢。应对策略是把推理做成「按需触发」而非「常驻」,并在后台标签页暂停。
最后是安全边界。端侧模型权重完全暴露在浏览器,容易被提取逆向。若模型本身是核心资产,端侧化等于公开权重,这类场景必须留在服务端。
五、总结
端侧大模型推理用 WebGPU 与 ONNX Runtime 把部分 AI 能力下沉到浏览器,换取低延迟、低成本与数据不出端。落地要点有三:第一,仅对小规模、低延迟、高隐私要求的任务端侧化,大模型仍留云端;第二,生产封装必须包含模型缓存、WebGPU→WASM 降级、推理超时三道保险,把可用性放在性能前面;第三,警惕设备差异、模型版本滞留、电量发热与权重泄露四道边界,越界场景应回退云端。端云协同、按能力分级,才是可持续的浏览器 AI 架构。这条路在千万级调用下能跑通,回报是值得的。