wllama:把 llama.cpp 编译成 WebAssembly,让模型在浏览器标签页里跑
WebAssembly binding for llama.cpp - Enabling on-browser LLM inference
秒懂
- 它是什么?
- wllama 是 llama.cpp 的 WebAssembly 绑定,用 TypeScript 封装成 npm 包 @wllama/wllama,支持 WebGPU、多模态和工具调用。它的价值在于把推理从前端之外搬回前端之内,代价是 2GB 的 ArrayBuffer 上限和一组必须配置的跨域隔离响应头。
- 适合谁用?
- 适合把推理放在浏览器内的场景:本地优先的工具、离线可用的演示、不想承担推理服务器成本的静态站点,以及需要把用户数据留在设备上的应用。不适合模型超过 2GB 又不愿拆分、或者需要服务端统一管理模型版本与配额的团队。
- 能商用吗?
- 可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 3 天前。
- 用什么语言写的?
- 主要是 TypeScript(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
wllama 解决的是推理位置问题,不是推理速度问题
把大模型放进浏览器这件事,通常有两种做法:一种是前端只做界面,推理请求发到服务端;另一种是把权重和计算都搬到客户端。wllama 属于后者。它是 llama.cpp 的 WebAssembly 绑定,README 的第一行就是这个定位,目标读者是希望在页面内完成推理、不依赖后端或 GPU 的前端工程师。
这个选择带来的直接后果是数据不出设备。用户输入的提示词、上传的图片或音频,都不需要经过服务器。对于本地优先的工具、企业内部演示、以及任何不愿承担推理服务器成本的静态站点,这是一个结构性的优势,而不是性能优势。
README 的 Features 一节列出的能力包括:OpenAI 兼容的 API 且带完整类型、WebGPU 支持、多模态输入(图像与音频文件)、工具调用、基于 WebAssembly SIMD 的推理、无运行时依赖、模型分片并行加载、依据浏览器支持自动在单线程与多线程构建之间切换、推理在 worker 内执行不阻塞 UI 渲染,以及已发布的 npm 包 @wllama/wllama。这些能力里,真正决定它能不能用在生产环境的,是最后几条关于线程和 worker 的描述,因为它们决定了页面的响应性。
数据流:worker 内的 wasm 模块,加一个 OpenAI 形状的接口
从 README 给出的示例可以还原出调用链。构造 Wllama 实例时需要传入一个配置对象,把 default 键映射到 wasm 文件路径:
const CONFIG_PATHS = { default: './esm/wasm/wllama.wasm' }; const wllama = new Wllama(CONFIG_PATHS);
这个映射不是装饰性的。wllama 会依据浏览器能力在单线程与多线程两套 wasm 构建之间选择,配置对象负责告诉它去哪里找这些二进制文件。如果你想强制使用单线程,README 的说法是在 LoadModelConfig 里加上 { "n_threads": 1 }。
模型加载有两个入口。loadModelFromHF 接受 { repo, file } 形式的 Hugging Face 仓库坐标,loadModelFromUrl 用于非 HF 来源的地址。两者都接受一个 progressCallback,回调参数是 { loaded, total },README 的示例用它算出下载百分比。
推理侧用的是 createChatCompletion,参数形状与 OpenAI 的聊天补全一致:messages 数组、max_tokens、temperature、top_k、top_p,返回值从 response.choices[0].message.content 取。这意味着如果你原本调用的是 OpenAI 接口,替换成本主要落在加载模型和初始化这一层,而不是提示词构造那一层。
README 明确写了推理在 worker 内执行,不阻塞 UI 渲染。这一点比 API 形状更重要:模型下载和逐 token 生成都是长任务,放在主线程上会直接冻结页面。
安装与构建:npm 一条命令,或者 docker 里自己编 wasm
常规路径是 npm i @wllama/wllama,然后在 TypeScript 里 import { Wllama } from '@wllama/wllama'。README 提醒,这个 React TypeScript 示例只覆盖补全用法,嵌入向量要看 examples/embeddings/index.html。
如果你要从源码仓库构建,README 的说明是 wasm 二进制文件并不随仓库预编译,需要本机装好 docker:
git submodule add https://github.com/ngxson/wllama.git wllama git submodule update --init --recursive cd wllama npm ci npm run build:wasm && npm run build
README 建议以 git submodule 的形式克隆。这个建议有实际意义:wasm 构建产物体积大,把它作为子模块而不是直接复制进主仓库,能避免每次上游更新都产生巨大的 diff。
还有一条 CDN 路径,import WasmFromCDN from '@wllama/wllama/esm/wasm-from-cdn.js',但 README 自己标注了「不推荐,只有在无法把 wasm 文件嵌入项目时才用」。理由是明确的:把二进制文件放在第三方 CDN 上,等于把推理能力的可用性绑定到那个 CDN 的可用性和版本一致性上。
WebGPU 相关的开关是 loadModel 的 n_gpu_layers 参数。README 说从 V3.1 起 WebGPU 会自动启用,默认把所有层卸载到 GPU;如果模型太大放不进显存,就手动调小这个值,设为 0 则完全关闭 GPU 推理。另有一个兼容模式,wllama.setCompat('default', 'firefox_safari'),README 注明这会让 WebGPU 在 Firefox 上跑起来,但性能会明显下降。
2GB 是硬边界,拆分模型是绕开它的唯一办法
README 的 Limitations 一节写了两条限制,其中一条比另一条更硬。
第一条是跨域隔离。要启用多线程,必须设置 Cross-Origin-Embedder-Policy 和 Cross-Origin-Opener-Policy 响应头。README 链接到一个 ffmpeg.wasm 的讨论作为背景。这两个响应头不是可选项:没有它们,浏览器不会给出 SharedArrayBuffer,多线程构建就无法启用,wllama 会退回单线程。如果你把应用部署在无法控制响应头的托管环境里,多线程这条路在部署层面就被堵死了,这跟代码写得对不对无关。
第二条是文件大小。单个文件上限 2GB,原因是 ArrayBuffer 的长度限制。README 的应对方式是拆分模型,并推荐每片最大 512MB。拆分的收益不止是绕开上限:多个分片可以并行下载,README 说这样下载会略快一些,同时也能避免一些内存不足的问题。
拆分工具用的是 llama-gguf-split,可以从 llama.cpp 的 release 页面下载预编译二进制:
./llama-gguf-split --split-max-size 512M ./my_model.gguf ./my_model
README 还给了量化建议:Q4、Q5、Q6 是性能、体积与质量之间的平衡点;不推荐使用带 imatrix 的 IQ 量化,理由是可能导致推理慢、质量低。这条建议值得认真对待,因为浏览器端的算力预算比服务端紧张得多,量化格式选错,代价会直接体现在用户等待时间上。
什么时候 wllama 是错的工具
最明显的一种情况是模型根本装不进浏览器。2GB 是文件层面的上限,拆分能绕开,但拆分只解决加载问题,不解决运行时的内存占用。一个 7B 的 Q4 模型对桌面浏览器尚可,对移动端浏览器就是另一回事。README 没有给出任何设备兼容性矩阵或内存占用数据,所以这部分只能靠你自己在目标设备上实测。
第二种情况是团队需要集中管理模型版本、调用配额和计费。wllama 把模型放在客户端,意味着每次更新模型都要等用户重新下载,你也拿不到服务端的调用统计。如果这些是刚需,服务端推理更合适。
第三种情况是首次加载体验敏感的产品。模型文件动辄数百 MB 到数 GB,用户第一次打开页面要等下载完成。progressCallback 能让你把进度显示出来,但显示进度不等于消除等待。对于跳出率敏感的落地页,这个成本可能直接吃掉浏览器内推理带来的所有好处。
还有一条容易被忽略:WebGPU 的开启是自动的,默认把所有层卸载到 GPU。这在显存充足的机器上是好事,在显存不足的机器上会失败,需要你通过 n_gpu_layers 手动调低。也就是说,你需要在运行时探测或者提供降级路径,而不是假设默认配置在所有设备上都成立。
与服务端推理相比,差别在于谁承担算力和运维
最常见的替代方案是自己搭一个 llama.cpp 服务端,前端通过 HTTP 调用。两者的差别不在模型本身,而在成本落在谁头上。
服务端方案把算力集中在受控硬件上,模型只存一份,升级一次全体生效,配额和鉴权都在你的掌握之中。代价是你需要 GPU 机器、需要处理并发排队、需要为每个请求付电费和带宽。用户侧的首次体验反而更好,因为不需要下载权重。
wllama 把算力分散到每个用户的设备上,你的服务器只负责分发静态文件。模型升级意味着用户要重新下载,并发能力取决于用户设备而不是你的集群,你也没法在服务端做限流。
值得注意的是 wllama 暴露的是 OpenAI 兼容接口。这个设计降低了从服务端方案迁移过来的改动量:提示词构造、消息数组、采样参数这些代码可以基本不动,改动集中在初始化与模型加载部分。如果你正在评估两条路线,这个兼容层让切换成本比通常的跨方案迁移要低。
另外,如果你的场景只是嵌入向量或者轻量分类,examples/embeddings/index.html 给出了嵌入与余弦距离的用法,这类任务的模型体积远小于对话模型,浏览器内运行的可行性明显更高。
版本节奏、许可与升级成本
仓库最近三个版本是 3.6.1(2026-08-27)、3.6.0(2026-08-16)、3.5.1(2026-06-15)。从 3.5.1 到 3.6.0 间隔约两个月,从 3.6.0 到 3.6.1 只有十一天,说明补丁版本会跟得比较紧。最后一次推送是 2026-09-06,仓库未归档。
README 顶部有一条重要提示:V3 引入了 WebGPU、多模态和工具调用,并指向 guides/intro-v3.md;同时提到兼容性问题要参考 @wllama/wllama-compat。这个 compat 包的存在本身就是一个信号:从 V2 升到 V3 不是无痛升级,项目为此专门维护了一条兼容路径。如果你的项目已经在用早期版本,升级前应该先读那份 V3 指南,而不是直接改版本号。
许可方面,仓库标注的是 MIT。这对商业项目通常友好,但需要你自己确认两件事:一是 wasm 二进制里链接的 llama.cpp 及其依赖的许可条款,二是你下载的模型权重各自的许可,模型许可与代码许可是两回事,wllama 的 MIT 不覆盖权重。这里不构成法律意见,涉及分发时请自行核对。
维护成本上,wllama 没有运行时依赖,这一点 README 明确列出,意味着升级时不用担心传递依赖冲突。真正需要持续投入的地方是 wasm 二进制与浏览器能力的匹配:上游 llama.cpp 在动,浏览器的 WebGPU 实现也在动,这两条线任意一条变化都可能需要你重新构建或者调整 n_gpu_layers 与线程配置。
编辑结论
适合把推理放在浏览器内的场景:本地优先的工具、离线可用的演示、不想承担推理服务器成本的静态站点,以及需要把用户数据留在设备上的应用。不适合模型超过 2GB 又不愿拆分、或者需要服务端统一管理模型版本与配额的团队。上手前先确认三件事:能否为页面设置 Cross-Origin-Embedder-Policy 与 Cross-Origin-Opener-Policy 响应头,否则多线程构建不会被启用;模型是否超过 2GB,超过就得用 llama-gguf-split 拆分;目标浏览器是否支持 WebAssembly SIMD 与 WebGPU,因为 wllama 会依据浏览器支持在单线程与多线程构建之间自动切换,而 WebGPU 层的卸载数量由 loadModel 的 n_gpu_layers 参数决定。
社区笔记