WebAssembly AI 插件项目回顾:理想很丰满,浏览器兼容性很骨感的现实

AI 驱动代码审查实战

Claude code-review 插件深度解析,把 AI 智能审查接进 CI/CD 流水线

WebAssembly AI 插件项目回顾:理想很丰满,浏览器兼容性很骨感的现实

一、为什么选择 WASM + Rust 做 AI 插件

初始想法很简单:用户在 Figma 或 VS Code Web 里选中一段文字,按快捷键,本地 AI 直接给出改写建议。全程离线,不经过任何服务器,隐私保护拉满。

技术栈选型很自然:Rust 写的推理引擎编译到 WebAssembly,运行在浏览器里。理想中的架构:

第一步很顺利——用 wasm-pack 把一个 200 行的 Rust 示例编译成 .wasm,在浏览器里成功运行了。

// ============================================================
// 第一个 WASM 测试:文本处理功能(编译成功!)
// ============================================================
use wasm_bindgen::prelude::*;

/// 暴露给 JavaScript 的文本预处理函数
/// 在 WASM 中运行,负责清洗和标准化用户输入的文本
#[wasm_bindgen]
pub fn preprocess_text(input: &str) -> String {
    // 去掉多余空白字符,统一换行符
    input
        .lines()
        .map(|line| line.trim())
        .filter(|line| !line.is_empty())
        .collect::<Vec<_>>()
        .join("\n")
}

/// 简单的 token 计数,用于评估文本长度是否超过模型限制
#[wasm_bindgen]
pub fn count_tokens(text: &str) -> usize {
    // 简化版:按空格分词(实际应该用 tokenizer)
    text.split_whitespace().count()
}

JS 调用端也很简洁:

import init, { preprocess_text, count_tokens } from './pkg/my_wasm.js';
await init();

const result = preprocess_text("  hello   world  \n  rust  ");
console.log(result); // "hello world\nrust"

到这里,我甚至觉得"WASM 项目真简单,两周搞定"。

二、第一道墙:WASM 内存限制

真正的噩梦从加载模型文件开始。一个最轻量的 ONNX 推理模型(比如 tiny-bert)也要 15MB。在 WASM 里,内存默认限制是 4GB——听起来很大,但浏览器的限制远不止这个:

  1. 每个 tab 的内存预算通常在 200~500MB 之间(取决于设备和浏览器)。
  2. WASM 的线性内存不能动态扩展超过一定量。
  3. 模型推理过程中的临时张量会额外占用大量内存。

实际测试:15MB 的模型加载后占用约 60MB WASM 堆内存,推理一个 256 token 的文本时峰值内存跳到 180MB。在 Chrome 里还能接受,但在移动端 Safari 上直接在 tab 崩溃。

三、第二道墙:浏览器兼容性地狱

WASM 本身兼容性还可以,但多线程SIMD 是两个巨大的坑:

特性ChromeFirefoxSafari我们的依赖
WASM 基础全部
WASM 线程⚠️ 需要 COOP/COEPwasm-bindgen-rayon
WASM SIMD❌ 不支持推理加速
SharedArrayBuffer⚠️ 需要特殊 header线程间共享模型

最关键的问题是 SharedArrayBuffer。要在 WASM 中启用多线程,必须用到它,但 Safari 要求服务端设置特定的 HTTP header:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

作为浏览器插件,我们没有能力控制目标网站的 HTTP header。这意味着:多线程 WASM 在 Safari 上直接无法使用,只能退回到单线程模式,推理速度慢了 3~4 倍。

// ============================================================
// 条件编译:根据平台决定使用多线程还是单线程
// ============================================================
#[cfg(all(target_feature = "atomics", feature = "threads"))]
mod inference {
    // 多线程推理实现:利用 wasm-bindgen-rayon + SharedArrayBuffer
    pub fn run_parallel(model: &Model, input: &[f32]) -> Vec<f32> {
        use rayon::prelude::*;
        // 并行计算各层的注意力分数
        input.par_chunks(64).map(|chunk| {
            model.forward(chunk)
        }).flatten().collect()
    }
}

#[cfg(not(all(target_feature = "atomics", feature = "threads")))]
mod inference {
    // 单线程回退方案:虽然慢但兼容性更好
    pub fn run_parallel(model: &Model, input: &[f32]) -> Vec<f32> {
        input.chunks(64).map(|chunk| {
            model.forward(chunk)
        }).flatten().collect()
    }
}

四、重新思考:WASM AI 的正确打开方式

经过两周的碰壁,我重新审视了这个项目的可行性。结论是:纯浏览器端的 AI 推理在现阶段还不成熟,但有一些可行的折中方案:

  1. WASM 做预处理,推理交给服务端:浏览器端只负责文本清洗、tokenization,通过加密通道把 token 发给服务端做推理。隐私性有折中,但可用性大幅提升。
  2. WebGPU 推理:Chrome 113+ 支持 WebGPU,可以用它来加速推理。但 Firefox 和 Safari 的支持还在路上。
  3. 等 Rust + WASM 生态再成熟一点candle/burn 等 Rust 深度学习框架正在积极支持 WASM 目标,可能半年后情况会好很多。

我们最终选择了路径 1——WASM 预处理 + 服务端推理。用户文本在浏览器端完成清洗和 tokenization,只把 token ID 发到服务端,服务端不存原始文本。这是目前技术条件下最务实的方案。

隔了半年再看,这个折中方案确实是最稳的选择——最近 WebGPU 在 Firefox 上的支持已经 beta 了,可能再等一年就能切到纯浏览器端。

五、总结

WASM AI 插件项目是一次"理想照进现实"的典型经历:

  1. WASM 不是银弹。它在浏览器里有严重的内存和多线程限制,做轻量功能可以,跑 15MB+ 的模型就吃力了。
  2. 浏览器兼容性是绕不过去的坎。Safari 对 SharedArrayBuffer 的限制直接杀死了多线程 WASM。
  3. 混合架构是务实选择。WASM 做预处理 + 服务端做重计算,兼顾了隐私和性能。

这次项目的代码我没扔掉——WASM 文本预处理模块被复用到另一个项目中,算是没白忙活。如果你也在考虑用 WASM 做 AI 推理,希望我的经历能让你少走点弯路。

AI 驱动代码审查实战

Claude code-review 插件深度解析,把 AI 智能审查接进 CI/CD 流水线

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值