欢迎光临
我们一直在努力

WebView里跑端侧AI模型——浏览器内LLM推理实战

最近几年,端侧AI越来越火,从手机上的NPU到浏览器的WebGPU,都在努力把AI能力带到离用户更近的地方。但你有没有想过:在App内嵌的WebView里,也能直接跑大语言模型(LLM)吗?答案是肯定的。本文将带你实操,在Android/iOS的WebView中运行本地LLM推理,无需服务器,完全离线。


一、为什么要在WebView里跑LLM?

在移动端App中,Hybrid架构依旧流行,很多页面直接内嵌WebView展示。如果我们能直接在WebView里完成AI推理,将带来几点好处:

  • 完全离线:不依赖网络,用户隐私数据不会离开设备。
  • 降低服务器成本:推理发生在客户端,无需维护昂贵的GPU集群。
  • 跨平台复用:一套Web代码,同时跑在Android、iOS甚至桌面端的WebView中。
  • 快速迭代:模型和UI更新无需发版,走Web发布流程即可。

当然,挑战也很明显:WebView的算力、内存受限,JS单线程容易阻塞UI。但随着WebGPU的普及和WebAssembly的成熟,浏览器内运行小型LLM(如LLaMA-2-7B的量化版本)已成为可能。 我这里只是简单介绍下,我的尝试体验,乐意和大家做进一步的交流,欢迎评论区了见。


二、技术选型:让浏览器跑起LLM的三驾马车

要在WebView里跑LLM,当前主流技术栈有三个:

1. WebGPU + ONNX Runtime Web

ONNX Runtime Web 支持通过WebGPU后端执行ONNX模型,能利用GPU进行加速。你需要将LLM转换为ONNX格式,借助其提供的JavaScript API加载并推理。优点是成熟度高,支持多种模型;缺点是需要自行处理分词器(Tokenizer)逻辑。

2. Transformers.js

Hugging Face推出的Transformers.js 让你直接在浏览器里使用和Python版接口几乎一致的库。底层基于ONNX Runtime Web,并内置了Tokenizer、Pipeline等组件。一行代码即可加载模型并开始生成文本,极大降低了门槛。

3. WebLLM / MLC-LLM

这类方案专门针对浏览器内LLM推理优化,比如WebLLM直接通过WebGPU运行LLaMA、Mistral等模型,内部使用Apache TVM编译,性能很强。但生态相对封闭,需要模型按照其格式转换。

综合考量:对于大多数开发者,推荐从Transformers.js入手,它隐藏了复杂的转换细节,上手最快。本文实战也将基于此库。


三、实战:在WebView里运行Gemma-2B模型

我们选择Google的轻量模型 gemma-2-2b-it 的量化版本(约1.3GB),目标是:用户在WebView的输入框提问,模型实时流式输出回答,就像和服务端通信一样,但全程在本地运行。

3.1 环境准备

确保你的App内嵌的WebView支持WebGPU。以Android为例,需要系统WebView版本 ≥ 121,并开启相关flag。iOS同理,需要15.0+,WKWebView默认支持。

首先,创建一个简单的HTML页面,引入Transformers.js:

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>WebView LLM Demo</title>
<script type="module">
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.2';
// 你的代码将写在这里
</script>
</head>
<body>
<textarea id="input" placeholder="输入你的问题…"></textarea>
<button id="submit">发送</button>
<div id="output"></div>
</body>
</html>

3.2 加载模型并创建生成器

Transformers.js提供了 pipeline API,类似Python版的体验。我们使用 text-generation pipeline,并指定模型为 Xenova/gemma-2-2b-it(一个ONNX量化版本)。同时,为了能在WebView的有限内存中跑起来,需要做一些配置:

const generator = await pipeline(
'text-generation',
'Xenova/gemma-2-2b-it',
{
// 使用WebGPU加速,若不可用则回退到CPU(极慢)
device: 'webgpu',
// 模型缓存到浏览器IndexedDB,下次加载更快
cache_dir: './models',
// 量化类型,进一步减少内存
dtype: 'q4f16',
}
);

首次加载会下载约1.3GB的模型文件,需要给用户一个进度提示:

const generator = await pipeline(…, {
progress_callback: (progress) => {
console.log(`加载中: ${progress.loaded}/${progress.total}`);
// 更新UI进度条
}
});

3.3 流式生成(关键)

为了做到打字机效果,我们需要开启流式输出。Transformers.js支持在 generate 时传入回调函数,不断获取新token:

document.getElementById('submit').addEventListener('click', async () => {
const inputText = document.getElementById('input').value;
const outputDiv = document.getElementById('output');
outputDiv.textContent = '';

// 构建对话模板(Gemma-2-it格式)
const messages = [
{ role: 'user', content: inputText }
];

const stream = await generator(messages, {
max_new_tokens: 512,
temperature: 0.7,
do_sample: true,
// 关键:流式回调
callback_function: (token, tokens, generationComplete) => {
// token是当前生成的文本片段
outputDiv.textContent += token;
// 自动滚动到底部
window.scrollTo(0, document.body.scrollHeight);
}
});

// 完成后,stream是完整的生成文本
console.log('生成完成:', stream[0].generated_text);
});

注意:不同模型的对话模板不同,这里的Gemma-2-it模型需要 messages 数组格式,库内部会自动拼接成Prompt。

3.4 优化体验:Service Worker缓存模型

模型文件较大,每次打开页面都下载是不可接受的。我们可以注册Service Worker,拦截模型请求并缓存到CacheStorage,实现“一次下载,永久离线”。Transformers.js内部使用fetch加载模型,所以SW可以透明接管:

// sw.js
const CACHE_NAME = 'llm-models-v1';
self.addEventListener('fetch', (event) => {
const url = event.request.url;
if (url.includes('huggingface') || url.includes('onnx')) {
event.respondWith(
caches.open(CACHE_NAME).then(cache =>
cache.match(event.request).then(res =>
res || fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
})
)
)
);
}
});

这样,即使App进入后台或被杀死,再次打开WebView时模型依然从本地缓存加载,速度飞快。


四、WebView特定适配与性能调优

在原生App的WebView中,我们还能做一些特殊处理来提升体验:

  • Android WebView 可开启硬件加速,并在 onPageFinished 中注入JavaScript调优参数,比如增大GPU内存限制(尝试 –enable-unsafe-webgpu flag)。
  • iOS WKWebView 默认开启WebGPU,但建议将模型放在 IndexedDB 中持久化,而非内存缓存,避免被系统清理。
  • 温度控制:移动端容易发热,可适当降低生成速度(如调整 max_new_tokens 和 do_sample),或在检测到温度升高时暂停推理。

实测:在骁龙8Gen2手机上,Gemma-2B-q4的推理速度约10-15 token/s,完全满足对话交互。


五、避坑指南

  • 不支持WebGPU怎么办? 可回退到WebAssembly(CPU)模式,但速度慢5-10倍。建议检测支持性,给用户明确提示。

  • 内存溢出 2B量化的模型占用内存约1.5GB,加上WebView本身开销,低端机可能闪退。可在加载前评估可用内存,或提供更小的模型(如Phi-3-mini)。

  • Token生成乱码 很可能是Tokenizer配置不匹配,确保使用模型卡上 config.json 和 tokenizer.json 对应的版本。

  • 跨域问题 本地开发时,WebView加载本地HTML文件可能遇到跨域限制,建议通过原生代码将HTML注入或使用本地服务器(如 http://localhost:8080)。


  • 六、总结与展望

    通过 Transformers.js,我们轻松在 App 的 WebView 里跑起了 LLM,实现了真正的离线智能。这种方案尤其适用于隐私敏感的问答助手、本地知识库检索等场景。未来,随着 WebGPU 规范的进一步成熟和更小更强的模型涌现(如 MobileLLM、Phi 系列),浏览器端 AI 将不再只是玩具,而会成为混合架构的关键一环。

    趁现在,赶紧在你的App里尝试吧!如果觉得有帮助,欢迎点赞收藏~ 🚀


    本文首发于CSDN,转载请注明出处。

    赞(0)
    未经允许不得转载:171主机测评 » WebView里跑端侧AI模型——浏览器内LLM推理实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址