Web 应用中的 Edge AI 推理:适合什么,不适合什么

0阅读10分钟

从一个分类需求说起

我在一个内容平台的项目里接到一个需求:对用户提交的 UGC 内容做实时情感分类,要求从提交到返回结果不超过 100ms。最初方案很直接——请求打到后端,调一个 BERT 模型,返回结果。测下来延迟在 200-400ms 之间,瓶颈在网络往返和模型排队。

团队有人提议把推理搬到边缘。听起来合理:边缘节点离用户近,少了几个 hop,延迟应该能降下来。我开始调研这个方案的可行性,结果发现「搬到边缘」这句话背后藏着一串需要逐个解决的问题——模型体积、运行时环境、冷启动、缓存策略、平台限制。

做完这轮调研,我形成了一个判断:Edge AI 不是把推理服务往边缘一放就完事,它更像是一个需要重新设计的系统。有些任务确实适合放在边缘,有些完全不适合,区别在于任务形态和资源约束是否匹配。

两种完全不同的 Edge

讨论 Edge AI 之前,有一个分类需要先搞清楚。「边缘」在 Web 应用的语境里至少指两种不同的运行环境,它们的约束和适用场景差异很大。

第一种是客户端浏览器。模型直接在用户设备上运行,通过 WebGPU 调用 GPU,或者用 WebAssembly 走 CPU。数据不出设备,隐私天然有保障。代价是模型体积直接影响加载时间,且不同设备、不同浏览器的性能差异巨大。

第二种是边缘函数(Edge Functions)。Cloudflare Workers、Vercel Edge Functions、Deno Deploy 这类平台。它们在全球有大量 PoP 节点,延迟低,但每个请求能用的 CPU 时间和内存都很紧。

维度客户端浏览器边缘函数(Workers)中心 API
延迟来源无网络往返1 个 hop多个 hop + 排队
模型大小限制设备内存(通常 < 500MB)函数内存 128MB-256MB几乎无限制
计算资源GPU(WebGPU)/ CPU(WASM)vCPU 时间片(25-50ms)完整 GPU
浏览器兼容需要 WebGPU/WASM 支持不依赖浏览器能力不依赖
隐私数据不出设备数据经过第三方节点数据经过后端
成本模型用户设备承担按请求/执行时间计费按 GPU 时间计费

这三种环境不是互斥的。实际架构里,更常见的做法是混合部署——轻量任务走客户端或边缘,复杂任务留中心。

量化是边缘推理的基础功

在边缘跑模型,模型体积是第一个需要解决的问题。原始 FP32 的 BERT-base 约 440MB,放到边缘函数里连加载都困难。量化把模型权重从 FP32(32 位浮点)压缩到 INT8(8 位整数)甚至 INT4(4 位整数),模型体积缩小到 1/4 到 1/8。

WebLLM 的实测数据(arXiv 2412.15803)显示,4-bit 量化的 Llama-3.1-8B 在 Apple M3 Max 的浏览器里能跑到 41.1 tokens/s,同设备原生 MLC-LLM 跑 57.7 tokens/s,保留了约 71% 的吞吐量。Phi-3.5-mini(3.8B 参数)的情况更好:浏览器端 71.1 tokens/s,原生 89.3 tokens/s,保留约 80%。

nnJIT 框架(arXiv 2309.08978)走得更远,通过 JIT 自动生成适配浏览器的计算 kernel,在 Llama 2 7B 上跑到 12.16 tokens/s,比同环境基线快约 40%,整体推理加速达到 8.2 倍。

这些数据说明两件事:量化后的小模型(1-8B 参数)在浏览器端跑已经可行;但从浏览器到原生之间仍有 20-30% 的性能损失,不能忽视。

量化方式精度模型体积精度损失适用场景
FP32(原始)32-bit 浮点基准后端服务,不考虑体积
FP1616-bit 浮点约 1/2极小GPU 推理,WebGPU FP16 混合精度
INT8(动态量化)8-bit 整数约 1/4很小边缘函数,分类 / 嵌入任务
INT4(GPTQ/AWQ)4-bit 整数约 1/8可接受浏览器端 LLM,边缘 7B 以下模型

Google 在 2024 I/O 上给出的经验是,2-8B 参数的量化模型是浏览器端推理的实用范围。再大的模型,当前设备的内存和算力都吃不消。

平台限制决定了能做什么

Vercel Edge Functions 和 Cloudflare Workers 是两种主流的 Web 边缘运行时,它们的 AI 推理能力差异明显。

指标Vercel Edge FunctionsCloudflare WorkersCloudflare Workers AI
CPU 时间~25ms / 请求~50ms / 请求平台托管
内存最大 4GB(Serverless)128MB(Free)/ 更大(Paid)平台管理
模型支持需自带运行时需自带运行时托管 Llama、Mistral 等
冷启动V8 isolate,较快V8 isolate,较快无需管理
执行超时10s(Hobby)/ 60s(Pro)30s(Free)更长,支持流式
延迟sub-50ms 级别sub-50ms 级别sub-50ms 级别
适合任务路由、预处理、轻推理路由、预处理、轻推理完整 LLM 推理

核心矛盾在这里:边缘函数的 CPU 时间只有 25-50ms,而一次 LLM token 生成需要大量矩阵乘法。25ms 连一个 token 都可能生成不了。这意味着在边缘函数上跑 LLM 逐 token 生成基本不现实,只能做单次前向传播的轻量任务(分类、嵌入、短文本改写)。

Cloudflare 推出的 Workers AI 是另一条路。模型推理由平台托管,不在 Worker 的 CPU 时间里跑,避开了这个矛盾。代价是灵活度降低——只能用平台支持的模型,自定义模型需要走 Worker 的 CPU 预算。

案例一:边缘函数上的分类模型冷启动

这个需求是实时情感分类。我最初把 ONNX 格式的 BERT-base(约 440MB)直接加载到 Cloudflare Worker 里,结果第一次请求直接超时。

问题出在模型加载。边缘函数没有持久进程,每次冷启动都要重新加载模型。440MB 的文件从 KV 读到内存、解析 ONNX 图、分配推理上下文,整个过程远超 30 秒的执行超时。

换成 sentence-transformers/all-MiniLM-L6-v2(约 80MB,INT8 量化后约 23MB),模型体积问题解决了。但冷启动时的推理上下文初始化仍然需要 200-500ms,第一次请求的延迟远高于后续请求。

最终方案是两步优化:模型从 Worker 的启动阶段预加载,同时用一个后台定时器保持推理引擎的热状态。

// ❌ 每次请求都加载模型,冷启动必然超时
export default {
  async fetch(request, env) {
    const model = await loadModelFromKV('bert-base')  // 440MB,每次冷启动重新读
    const result = await session.run({ input_ids, attention_mask })
    return Response.json(result)
  }
}
// ✅ 启动时预加载 + 后台保活,避免重复初始化
import { InferenceSession, Tensor } from 'onnxruntime-web'
 
// 模块加载阶段即创建 session,利用 isolate 复用
let sessionPromise = null
function getSession(env) {
  if (!sessionPromise) {
    // MiniLM INT8 量化后约 23MB,从 KV 读取一次
    sessionPromise = env.MODEL_KV.get('minilm-int8', 'arrayBuffer')
      .then(buf => InferenceSession.create(buf, {
        executionProviders: ['wasm'],
        //  intra-op 线程数在边缘环境通常固定为 1
      }))
  }
  return sessionPromise
}
 
// 后台定时器保持 isolate 热状态,减少冷启动概率
const keepAlive = setInterval(() => {
  // 每 5 分钟发一个空操作,防止 V8 isolate 被回收
  void 0
}, 300_000)
 
export default {
  async fetch(request, env) {
    const session = await getSession(env)
    const inputIds = new Tensor('int64', tokenize(text), [1, seqLen])
    const result = await session.run({ input_ids: inputIds })
    return Response.json(classify(result))
  }
}

这个案例的核心经验:边缘函数上跑模型,模型体积决定了能不能加载,加载方式决定了冷启动延迟。两个问题都要在上线前解决。

案例二:浏览器端 WebGPU 推理的兼容性现实

另一个团队在用 WebGPU 做浏览器端文本摘要。桌面 Chrome 上测试一切正常,摘要生成只需几百毫秒。上线一周后,用户反馈涌入:Safari 用户完全无法使用,Firefox 用户报 WebGL 上下文丢失,移动端安卓用户的 Chrome 版本低于 113 直接不支持 WebGPU。

团队最初只做了简单的特性检测:

// ❌ 只检查 navigator.gpu,不够用
if ('gpu' in navigator) {
  // 直接加载 WebGPU 模型
  const adapter = await navigator.gpu.requestAdapter()
  const session = await createSession(modelUrl, { backend: 'webgpu' })
}

问题不只是「支不支持 WebGPU」。navigator.gpu 存在不代表能创建 adapter,能创建 adapter 不代表有足够内存加载模型,有足够内存不代表能在合理时间内完成推理。

// ✅ 多层降级:WebGPU → WASM → 中心 API
async function detectCapability() {
  if (!('gpu' in navigator)) return 'api-fallback'
 
  const adapter = await navigator.gpu?.requestAdapter()
  if (!adapter) return 'api-fallback'
 
  // 检查设备内存(粗略判断)
  const memoryGB = navigator.deviceMemory || 4
  if (memoryGB < 8) return 'wasm-fallback'
 
  // 实际尝试分配 GPU 内存,验证可用性
  try {
    const device = await adapter.requestDevice()
    // 用一个小型 tensor 验证 GPU 计算是否正常
    const buffer = device.createBuffer({ size: 1024, usage: GPUBufferUsage.STORAGE })
    buffer.destroy()
    device.destroy()
    return 'webgpu'
  } catch {
    return 'wasm-fallback'
  }
}
 
async function summarize(text) {
  const capability = await detectCapability()
 
  switch (capability) {
    case 'webgpu':
      return runBrowserSummarize(text, { backend: 'webgpu' })
    case 'wasm-fallback':
      // WASM 后端速度慢,但功能一致
      return runBrowserSummarize(text, { backend: 'wasm' })
    case 'api-fallback':
    default:
      return fetch('/api/summarize', {
        method: 'POST',
        body: JSON.stringify({ text })
      }).then(r => r.json())
  }
}

WebGPU 在不同环境下的实际表现:

环境WebGPU 状态建议方案
Chrome 113+(桌面)完整支持WebGPU 推理
Edge 113+(桌面)完整支持WebGPU 推理
Safari 18+(macOS 15+)实验性支持,部分 API 缺失WASM 降级或 API 降级
Firefox(桌面)需手动开启 flagWASM 降级
Chrome Mobile 121+支持,但内存受限INT4 量化 + 小模型
iOS Safari不支持 WebGPUAPI 降级

这个案例的教训:浏览器端推理不能只看「能不能跑」,还要考虑跑得好不好、回退路径顺不顺畅。

案例三:混合架构的一致性难题

案例二的延伸。浏览器端推理跑通之后,团队希望把更多任务搬到客户端——关键词提取、意图分类、短文本改写。这些任务在支持 WebGPU 的设备上延迟只有 30-50ms,比走边缘 API 的 200-300ms 快了一个量级。

但新的问题出现了:同一个任务,浏览器端和后端用的是同一个 ONNX 模型的不同版本(浏览器端是 INT4 量化版,后端是 FP16 版),输出结果不完全一致。用户在桌面浏览器看到关键词是 A、B、C,切换到 API 降级后看到 A、B、D。

// ❌ 浏览器端和后端用不同精度模型,输出不一致
// 浏览器端
const browserResult = await browserEngine.run(text, { quantization: 'int4' })
// 后端
const serverResult = await serverModel.run(text)  // FP16
// browserResult !== serverResult,用户感知到结果跳变
// ✅ 统一模型版本 + 一致性校验层
class HybridInference {
  constructor() {
    // 浏览器端用 INT8 量化,后端也用 INT8 部署
    // 精度损失更小,两端输出更接近
    this.browserEngine = new BrowserEngine({
      modelUrl: '/models/classifier-int8.onnx',
      quantization: 'int8',
      // 固定随机种子以保证确定性输出
      seed: 42
    })
    this.apiEndpoint = '/api/classify'
  }
 
  async classify(text) {
    const browserResult = await this.browserEngine.run(text)
 
    if (!this.browserEngine.isReliable()) {
      // 浏览器端不可靠时(WebGPU 不可用、内存不足等),直接走 API
      return this.callApi(text)
    }
 
    // 10% 的请求同时走 API,对比两端结果
    if (Math.random() < 0.1) {
      const apiResult = await this.callApi(text)
      const consistent = this.compareResults(browserResult, apiResult)
      // 记录一致性指标,不一致时触发告警
      this.metrics.recordConsistency(consistent)
      if (!consistent) {
        // 不一致时以后端结果为准
        return apiResult
      }
    }
 
    return browserResult
  }
 
  compareResults(a, b) {
    // 余弦相似度 > 0.95 视为一致
    const similarity = cosineSimilarity(a.embeddings, b.embeddings)
    return similarity > 0.95
  }
 
  async callApi(text) {
    const res = await fetch(this.apiEndpoint, {
      method: 'POST',
      body: JSON.stringify({ text, model: 'classifier-int8' })
    })
    return res.json()
  }
}

混合架构的代价是维护成本翻倍——同一个模型要维护两个版本,要有一致性校验机制,灰度发布时两端要同步。

案例四:把多轮对话放到边缘函数的误判

最后一个案例是一个反面教材。团队想把一个多轮对话功能部署到 Cloudflare Workers,认为边缘节点离用户近、响应快。

这个功能用的是 Llama 2 13B,KV 缓存需要 2GB 以上的内存。Workers 的 CPU 时间只有 50ms,而生成一个 token 需要数百毫秒的矩阵计算。即使把 KV 缓存存到 D1 或 R2,跨存储的读写延迟也会把 token 生成时间拉到不可接受的水平。

边缘函数的定位是路由和预处理层,不是推理引擎。需要跑完整的 LLM 推理,应该用 Cloudflare Workers AI 这类专用服务——模型推理由平台托管,不受 Worker CPU 时间约束。

// ❌ 在 Worker 里直接跑 LLM 推理,CPU 时间远远不够
export default {
  async fetch(request, env) {
    const session = await InferenceSession.create('/models/llama-13b.onnx')
    // 生成一个 token 就需要 200-500ms,远超 50ms CPU 限制
    const output = await session.run({ input_ids })
    return Response.json(output)
  }
}
// ✅ 用 Workers AI 托管推理,Worker 只做路由和流式传输
export default {
  async fetch(request, env) {
    const { messages } = await request.json()
    // Workers AI 由平台管理 GPU,不受 Worker CPU 时间约束
    const response = await env.AI.run('@cf/meta/llama-2-13b-chat-int8', {
      messages,
      stream: true
    })
    // 流式返回,用户体验接近实时
    return new Response(response, {
      headers: { 'content-type': 'text/event-stream' }
    })
  }
}
方案适用场景主要限制
Worker 内自跑推理单次前向传播(分类、嵌入)CPU 25-50ms,只能跑 < 100MB 量化模型
Workers AI 托管推理LLM 推理、图像生成只支持平台提供的模型,自定义受限
Worker + 外部 API需要自定义模型 + 边缘路由多一跳延迟,需要管理 API key
浏览器端推理隐私敏感、低延迟轻量任务依赖设备性能和浏览器支持

决策路径

把上面的经验整理成一个判断流程:

流程图画布 · 115%
Mermaid 流程图加载中...

上线前的评估清单

需求阶段

  1. 是否量化过延迟收益:有 P50/P95 基线数据,预期改善 > 30% 才值得引入边缘
  2. 是否确认隐私收益:数据合规要求是否强制数据不出设备或减少传输节点
  3. 是否评估了成本变化:边缘平台的请求计费 vs 中心化 GPU 成本,算清楚总账
  4. 是否考虑了维护复杂度:边缘架构增加运维成本,团队是否准备好

场景匹配阶段

  1. 任务是否适合边缘:单次前向传播(分类、嵌入、轻改写)适合,多轮生成不适合
  2. 模型体积是否可接受:量化后能否放进目标环境的内存限制
  3. 是否有可接受的降级方案:WebGPU 不可用、边缘超时时用户不会感知到断裂

技术验证阶段

  1. 是否测试过冷启动:用真实模型和真实数据测冷启动延迟,不要只看热路径
  2. 是否测过目标浏览器和设备的 WebGPU 兼容性:特别是 Safari 和移动端
  3. 是否验证过量化模型的精度损失:和 FP32 基线对比关键指标
  4. 是否评估了缓存复杂度:KV 缓存、模型分发、配置同步的一致性方案
  5. 是否在低端设备上测试过:内存 < 4GB 的设备是否还能正常工作

部署准备阶段

  1. 边缘平台的 CPU 时间和内存限制是否已确认并留有余量
  2. 回退链路是否已就绪:边缘不可用时自动切换到中心 API
  3. 推理延迟和错误率的监控是否已配置
  4. 模型分发和版本管理方案是否已确定:边缘节点的模型更新如何灰度
  5. A/B 测试方案是否就绪:边缘路径和中心路径的效果对比

参考资料

  1. ONNX Runtime Web: Unleashes Generative AI in the Browser Using WebGPU — Microsoft Open Source Blog, Feb 2024. 链接

  2. WebLLM: A High-Performance In-Browser LLM Inference Engine — arXiv 2412.15803, Dec 2024. 链接

  3. I/O 2024 Web AI Wrap Up: New Models, Tools, and APIs — Google Developer Chrome Blog, May 2024. 链接

  4. Empowering In-Browser Deep Learning Inference on Edge Devices (nnJIT) — arXiv 2309.08978, Jul 2024. 链接

  5. Edge AI: Evaluation of Model Compression Techniques — arXiv 2409.02134, Sep 2024. 链接

  6. Advances to Low-Bit Quantization Enable LLMs on Edge Devices — Microsoft Research, Feb 2025. 链接

  7. Edge Inference Showdown: Cloudflare vs Vercel AI SDK — Suhas Bhairav, 2024. 链接

  8. ONNX Runtime Web Tutorials — ONNX Runtime Official Docs. 链接

评论 0

0 / 1000