在浏览器里做 AI 工具台:WebMatte Studio 的前端实践

本文记录一个基于 Vue 3、Vite、Naive UI、Tailwind CSS 和 Transformers.js 的浏览器端 AI 工具台实践。重点不是训练模型,而是如何把公开模型接入前端,并把图片处理、文本处理和交互体验组织成一个可用的产品界面。

请添加图片描述

请添加图片描述

通用模型的效果还是挺不错的

文本处理工作台
请添加图片描述

使用可以直接在浏览器中下载并加载对印模型,无需远程调用第三方api
注意:内存高的伙伴可以尝试下效果,我8g内存已经爆了

背景

传统 AI 工具通常依赖服务端推理。用户上传图片或文本后,服务端调用模型并返回结果。这种方式能力强,但也带来几个问题:

  • 部署和运维成本更高。
  • 用户素材需要上传到服务器。
  • 小工具原型的开发链路较重。
  • 浏览器端交互和模型处理状态容易割裂。

随着 Transformers.js、WebAssembly 和 WebGPU 逐渐成熟,一部分轻量 AI 能力已经可以直接在浏览器中运行。WebMatte Studio 就是基于这个方向做的一次实践。

我一开始并不是想做一个“什么 AI 都能做”的大平台,而是想验证一个更具体的问题:如果把一些足够轻量、足够稳定、对隐私要求又比较高的能力放到浏览器里,能不能做出一个真正可用的工具,而不是一个只能演示一次的 demo?

这个问题看起来简单,但实际做起来会遇到很多产品和工程上的取舍。比如,模型越强,体积通常越大;越想在浏览器里完成全部推理,就越容易遇到加载慢、内存高、网络失败和浏览器兼容问题。反过来,如果所有能力都交给服务端,前端就只是一个上传入口,也失去了浏览器端 AI 的意义。

所以 WebMatte Studio 的定位不是“替代服务端 AI”,而是做一个边界清晰的前端 AI 工具台:适合在浏览器端跑的,就放到浏览器里;明显不适合的,比如大型聊天模型、私有模型、需要复杂鉴权的模型,就不要硬塞进前端。

这个边界意识非常重要。很多 AI 前端项目失败,不是因为模型不能用,而是因为一开始就把产品范围拉得太大。模型列表越堆越多,页面入口越来越乱,最后用户不知道该点哪里,开发者也不知道哪里出了问题。这个项目后面的几次重构,基本都是围绕“把功能收束成清晰的工作流”来做的。

产品形态

当前项目被拆成两个工作区:

  • Image Studio:面向图片处理,核心能力是批量抠图、透明 PNG 导出和手动修边。
  • Text Lab:面向文本处理,集成轻量对话、文本分类、摘要、翻译和问答。

这两个模块共享同一套网站导航、视觉系统和任务处理方式,但业务逻辑保持独立。

为什么要拆成两个工作区,而不是把所有功能都放在首页?因为图片处理和文本处理的操作心智完全不同。

图片处理更像一个素材生产流程。用户的关注点是:我导入了多少图片、哪些图片等待处理、哪些已经完成、效果是否可接受、能否手动修边、最后如何批量下载。它天然需要队列、预览、卡片和编辑器。

文本处理则更像一个工具面板。用户通常一次处理一段文本,或者围绕一段上下文进行问答。它更适合使用标签页、输入区、结果区和状态栏,而不是图片卡片。

如果把这两类体验混在同一个页面里,功能虽然都能放下,但页面会变得很难理解。因此最终的导航只保留两个核心入口:

  • Image Studio
  • Text Lab

这样用户打开网站时,首先看到的是两个明确的工作空间,而不是一堆模型名和 pipeline 名称。模型是实现细节,不应该成为用户理解产品的第一层入口。

技术选型

项目采用的主要技术如下:

技术 用途
Vue 3 构建前端应用
Vite 开发服务器与生产构建
TypeScript 类型约束
Vue Router 页面路由
Naive UI 标准化交互组件
Tailwind CSS 页面布局和品牌化样式
Transformers.js 浏览器端模型推理
JSZip 批量打包下载

整体策略是:Naive UI 负责控件一致性,Tailwind CSS 负责页面布局和产品气质,Transformers.js 负责模型推理。

这套组合有一个很现实的好处:它允许项目在“产品感”和“开发效率”之间取得平衡。

如果只用组件库,页面很容易变成标准后台系统,虽然稳定,但缺少产品辨识度。如果只用 Tailwind 手写所有按钮、弹窗、Tab、进度条、消息提示,短期很灵活,长期会出现大量重复样式和交互细节问题。

所以这里采用混合方案:

  • 表单、按钮、卡片、进度条、消息提示等标准控件使用 Naive UI。
  • 页面大结构、空间关系、背景、品牌视觉使用 Tailwind CSS 和少量全局 CSS。
  • 业务组件保持自己的语义,比如图片结果卡片、模式选择器、手动修边弹窗。

这种做法比“全手写”更可维护,也比“全组件库默认样式”更容易做出网站气质。

为什么选择 Transformers.js

Transformers.js 的价值在于它把 Hugging Face 生态里的很多模型带到了浏览器端。对于前端开发者来说,这意味着我们可以用相对熟悉的方式创建 pipeline:

const remover = await pipeline('background-removal', model)
const result = await remover(imageUrl)

它屏蔽了很多底层细节,比如模型加载、ONNX Runtime、预处理和后处理。但这不代表接入就完全没有成本。实际项目里依然要处理:

  • 模型首次下载很慢。
  • 模型文件可能很大。
  • 不同模型返回的数据结构可能不同。
  • 浏览器缓存可能失效。
  • Vite 依赖预构建可能过期。
  • WebGPU 和 WASM 的兼容性不同。
  • 部分模型是 gated/private,不适合前端直连。

也就是说,Transformers.js 解决的是“模型如何在浏览器中运行”的问题,但没有替我们解决“产品如何围绕模型组织体验”的问题。后者仍然需要前端自己设计。

为什么用 Web Worker

模型推理不能直接放在主线程里。即使是轻量模型,也可能在加载、预处理、推理和后处理阶段占用明显的 CPU/GPU 资源。如果主线程被阻塞,用户会感觉整个页面卡死,进度条也不会动。

因此图片处理和 NLP 处理都放到了 worker 中。主线程只负责:

  • 接收用户操作。
  • 管理任务状态。
  • 展示进度和结果。
  • 发送任务到 worker。

worker 负责:

  • 初始化 Transformers.js 环境。
  • 缓存 pipeline 实例。
  • 执行模型推理。
  • 把结果传回主线程。

这种结构让界面和计算解耦,也方便后续扩展更多任务。

Image Studio:批量抠图工作流

图片处理的核心流程被设计成三个阶段:

  1. 选择抠图模式。
  2. 批量导入图片。
  3. 点击开始处理。

这里没有采用“上传即处理”的方式。原因很简单:批处理场景下,用户通常需要先选模型、先导入多张图片,再统一开始执行。如果图片一上传就自动跑模型,体验会比较失控,也不方便切换模式。

这个交互改动看似很小,但对批处理工具很关键。

最早的版本是用户一选择图片,系统就立即开始下载模型并处理队列。这个逻辑在 demo 阶段很直接,但在真实使用里会出现几个问题:

  1. 用户还没确认模式,任务已经开始了。
  2. 用户想一次导入多张图,却发现前几张已经开始占用资源。
  3. 如果模型下载失败,用户会误以为是上传失败。
  4. 如果用户误选了文件,只能等任务失败或手动清空。

改成“先入队,再开始处理”后,整个流程变得更可控:

  • 上传只是素材管理。
  • 开始处理才是计算任务。
  • 模型下载发生在明确的用户动作之后。
  • 用户可以先检查队列,再决定是否执行。

这就是典型的工具型产品逻辑:不要替用户过早执行不可逆或高成本动作。

模型模式

当前只保留公开、免费、浏览器可直接访问的模型:

  • Xenova/modnet:更适合人像或简单主体,速度较快。
  • briaai/RMBG-1.4:更适合商品图、玩具、盲盒、普通物体等通用场景。

没有接入 gated/private 模型。浏览器端不适合直接使用需要授权的模型,因为 token 暴露风险高,也容易遇到 401 Unauthorized 或 Xet/CAS 大文件下载失败。

这个地方踩过一个很典型的坑:一开始为了提升效果,尝试加入 briaai/RMBG-2.0。模型能力看起来更强,但浏览器端请求时出现了 401 Unauthorized。进一步排查后发现,它并不适合作为前端公开可直接访问的模型。

这件事给项目定了一个明确原则:只接入公开、免费、无需登录、浏览器可直接访问的模型。

原因并不是技术上完全不能绕,而是产品上不应该这样做。如果前端需要携带 Hugging Face token 才能访问模型,那么 token 就有泄露风险。如果模型依赖 signed URL 或 Xet/CAS 大文件桥接,用户环境中的代理、缓存和时间戳都可能导致下载失败。

浏览器端 AI 工具要想稳定,模型选择比模型能力更重要。一个略弱但稳定可下载的模型,往往比一个效果更好但经常加载失败的模型更适合产品。

模型效果与产品

抠图效果不理想,并不一定是代码错了。很多时候是模型能力边界导致的。

比如 Xenova/modnet 更偏人像和简单主体,如果拿它处理商品图、手办、盲盒、透明物体、低对比背景,效果就可能不稳定。RMBG-1.4 在通用物体上会更合适,但也不是万能的。

因此产品不能假设“模型输出就是最终结果”。更合理的思路是:

  1. 模型负责生成初稿。
  2. 用户负责局部确认和修正。
  3. 工具负责降低修正成本。

这也是为什么后面加入了手动修边工具。

Worker 推理

模型推理放在 Web Worker 中执行,避免阻塞主线程。主线程只负责:

  • 管理图片队列。
  • 展示模型加载进度。
  • 更新每张图片的处理状态。
  • 接收 worker 返回的透明 PNG 数据。

大致流程如下:

worker.postMessage({
  action: 'remove_bg',
  id: item.id,
  imageUrl: item.originalUrl,
  model: selectedMode.value.model,
})

每个任务都会携带当时选择的模型。这样即使用户之后切换模式,已经入队或正在处理的任务也不会被错误模型影响。

任务携带模型信息这个设计也很重要。很多批处理系统容易犯一个错误:只保存当前全局配置,而不是保存任务创建时的配置。这样用户在处理过程中切换模式,就可能影响已经加入队列的任务。

更稳的做法是:任务一旦创建,就应该拥有自己的配置快照。

在这个项目里,每个图片任务至少包含:

  • id
  • fileName
  • fileSize
  • originalUrl
  • resultUrl
  • status
  • error

开始处理时,再把任务对应的模型一起发给 worker。这样 worker 不需要关心页面当前选中了什么模式,只需要处理消息里指定的模型。

状态设计

图片任务的状态被简化成几类:

  • queued:已经导入,等待处理。
  • processing:模型推理中。
  • compositing:结果合成中。
  • done:已完成。
  • error:处理失败。

状态不宜过多。过多状态会让 UI 和代码都变复杂,但用户真正关心的是:有没有开始、是否正在处理、是否完成、是否失败。

因此 UI 上没有展示过多内部细节,只保留用户能理解和能行动的信息。

手动修边:自动结果后的人工

自动抠图很难保证每一张图都完美,尤其是复杂边缘、半透明物体、低对比背景和细碎主体。因此项目里加入了一个轻量手动修边工具。

它不是完整的专业图像编辑器,而是针对抠图结果做局部修正:

  • 擦除多余区域。
  • 恢复被误删的主体。
  • 调整笔刷大小。
  • 撤销和重做。
  • 还原自动结果。
  • 保存修改后的透明 PNG。

这种设计更贴近真实使用场景:模型先做 80% 的工作,用户只修最后 20%。

Canvas 编辑器的基本思路

手动修边工具基于 Canvas 实现。自动抠图完成后,结果已经是一张带 alpha 通道的 PNG。编辑器打开时,会把结果图绘制到 canvas 上,同时加载原图作为“恢复”工具的数据来源。

擦除的核心是使用 Canvas 的合成模式:

context.globalCompositeOperation = 'destination-out'

这个模式会把画笔经过的区域从当前图层中擦掉,也就是让它变透明。

恢复则相反,需要从原图中把对应区域画回来:

context.globalCompositeOperation = 'source-over'
context.drawImage(originalImage, 0, 0, canvas.width, canvas.height)

不过恢复时不能直接把整张原图画回来,否则会把背景也带回来。因此实际实现里会先用圆形笔刷区域进行裁剪,只恢复笔刷覆盖范围。

撤销与重做

撤销和重做并没有做复杂的操作栈,而是采用快照方式。每次开始绘制前,把当前 canvas 保存成 data URL 放进 undo stack。撤销时恢复上一张快照,重做时再把当前快照压回去。

这种方式实现简单,适合轻量工具。但它也有代价:

  • 大图会占用更多内存。
  • 快照过多会增加浏览器压力。
  • 不适合无限历史记录。

如果要继续优化,可以改成只保存局部区域,或者限制最大历史步数。

为什么不做完整图像编辑器

完整图像编辑器需要处理图层、缩放、平移、选区、羽化、蒙版、历史记录、快捷键、导出参数等大量细节。这个项目的目标不是替代 Photoshop,而是解决自动抠图后的局部修正。

因此当前编辑器只保留最关键的几项能力:

  • 擦掉多余背景。
  • 恢复误删主体。
  • 控制笔刷大小。
  • 撤销和重做。
  • 保存回结果图。

对于 WebMatte Studio 这种工具台来说,这个范围更实际,也更容易维护。

Text Lab:浏览器端 NLP 工具

Text Lab 被设计成工具型工作台,而不是单独的聊天页面。它包含:

  • 聊天入口。
  • 文本分类。
  • 文本摘要。
  • 文本翻译。
  • 文档问答。

需要注意的是,大型聊天模型没有直接放到浏览器端下载。原因是浏览器直拉大型 ONNX 文件很容易遇到网络、缓存、代理、Xet/CAS 签名链接等问题。更合理的方案是:

  • 小模型和公开模型可以放在前端。
  • 大模型聊天应该走服务端代理或 API。

因此当前聊天入口是轻量模式,避免把整个产品卡在大模型下载体验上。

为什么聊天不直接上大模型

浏览器端跑聊天模型是一个很有吸引力的方向,但在实际产品中要谨慎。

大模型通常意味着:

  • 文件体积大。
  • 首次下载慢。
  • 内存占用高。
  • 移动端体验差。
  • 容易受代理和缓存影响。
  • 不同浏览器的 WebGPU 支持不一致。

项目里曾经尝试过接入轻量 ONNX 聊天模型,但实际下载会经过 Hugging Face 的 Xet/CAS 链路,生成很长的签名地址。只要代理、时间戳、缓存、跨域或网络环境有一点问题,用户就会看到很难理解的下载错误。

如果用户只是想试一个工具,这种失败体验非常糟糕。更合理的路线是:

  • 浏览器端保留小模型 NLP 工具。
  • 大模型聊天放到服务端。
  • 前端通过 API 调用,展示流式结果。

这不是技术退让,而是产品稳定性的选择。

NLP 工具的边界

Text Lab 当前适合做轻量文本处理,比如:

  • 对英文评论做情绪分类。
  • 对英文长文本做摘要。
  • 做简单中英翻译。
  • 基于上下文抽取答案。

这些能力并不等于完整的智能助手。它们更像一组工具,每个工具解决一个明确的小问题。

在前端 AI 产品里,越早承认能力边界,体验越容易做好。反过来,如果把所有能力都包装成“智能助手”,用户就会期待它无所不能,而模型实际输出一旦不稳定,产品信任度会迅速下降。

UI 重构思路

最初的界面更像 demo:功能堆在一起,文案偏说明书,导航也只是功能列表。后来整体重构成标准网站结构:

  • 顶部统一品牌导航。
  • Image Studio 和 Text Lab 分为两个主模块。
  • 每个页面都有清晰的标题区、操作区和结果区。
  • 控件统一使用 Naive UI。
  • 页面布局和视觉层次由 Tailwind CSS 控制。

这样做的好处是后续扩展能力时不会继续堆页面,而是按照工作区组织功能。

从 demo 到网站

一开始的页面更像一个开发过程中的功能展示:顶部导航列出很多 pipeline,首页既有抠图工具,又有能力库说明,还有各种未来规划。这个结构适合开发者理解项目,但不适合用户使用。

后来改成标准网站结构后,页面心智更清楚:

  • 顶部是品牌和主导航。
  • 首页是 Image Studio。
  • NLP 独立成 Text Lab。
  • 结果区和操作区分离。
  • 不再把未开发的能力放在主流程里。

这次重构的关键不是“变好看”,而是把信息层级理顺。很多界面问题本质上不是颜色或圆角的问题,而是用户不知道当前页面最重要的动作是什么。

Naive UI 与 Tailwind 的分工

Naive UI 负责的是交互控件的标准化,例如:

  • Button
  • Card
  • Tabs
  • Progress
  • Tag
  • Message

Tailwind 负责的是页面层级,例如:

  • 页面宽度。
  • 网格布局。
  • 间距系统。
  • 背景氛围。
  • 响应式结构。

两者混用时要避免一个问题:不要让 Naive UI 和 Tailwind 同时控制同一个控件的细节样式。否则后期会出现样式覆盖、状态不一致和维护困难。

比较稳定的做法是:

  • 控件内部交给 Naive UI。
  • 控件外部布局交给 Tailwind。
  • 少量全局 class 只做容器级风格。

文案调整

技术产品的文案很容易写成说明书,比如“支持某某 pipeline”“模型文件首次运行会下载到缓存”。这些内容对开发者有用,但对普通用户不一定友好。

最终页面里更强调动作:

  • 添加素材。
  • 选择模式。
  • 开始处理。
  • 下载结果。
  • 编辑图片。

技术细节仍然保留,但放在次级说明里。这样既不会隐藏重要信息,也不会让页面一开始就被技术术语占满。

常见问题

为什么有些模型下载会失败?

常见原因包括:

  • 模型不是公开可直接访问。
  • 模型需要登录或接受协议。
  • 浏览器端无法安全使用私有 token。
  • 大文件走 Xet/CAS 签名链接,可能被代理、缓存或过期时间影响。

所以前端直连 Hugging Face 时,优先选择公开、轻量、可直接访问的模型。

为什么不用上传即处理?

批量任务里,用户通常需要先选模式、导入多张图,再统一执行。手动点击开始处理更符合批处理工具的使用逻辑,也能避免误操作。

为什么还需要手动修图?

自动抠图结果受模型、图片质量、背景复杂度影响很大。手动修边可以把模型结果变成真正可交付的素材。

为什么构建后体积变大?

引入 Naive UI 和 Transformers.js 后,前端包体积一定会增加。尤其是 Transformers.js 和 ONNX Runtime 的 wasm 文件,本身就比较大。

这类项目不能只看 JS bundle 的大小,还要看:

  • 模型是否按需加载。
  • worker 是否拆包。
  • wasm 是否缓存。
  • 首屏是否必须加载模型。

当前设计里,模型不会在打开页面时立即下载,而是在用户点击开始处理或运行文本任务时才加载。这比首屏直接初始化模型更合理。

为什么不把所有模型都列出来?

Hugging Face 上可用模型很多,但不是每个都适合浏览器端产品。列出太多模型会带来几个问题:

  • 用户不知道怎么选。
  • 模型兼容性不稳定。
  • 下载失败概率增加。
  • 页面变成模型目录,而不是工具。

因此当前只保留少数经过明确定位的模型。后续如果增加模型,也应该以“场景模式”的方式出现,而不是直接暴露一堆模型名称。

为什么不用后端?

不是不用后端,而是这个项目刻意探索浏览器端能做到什么。对于轻量、公开、无需鉴权的模型,前端运行可以降低部署成本,也能减少素材上传。

但对于大型模型、私有模型和高质量生产推理,后端仍然是更稳的方案。一个成熟产品最终很可能是混合架构:

  • 前端跑轻量即时工具。
  • 后端跑大模型和重任务。
  • 两边共享统一的任务和结果体验。

本地运行

如果要在本地运行项目:

pnpm install
pnpm dev

生产构建:

pnpm build

如果 Vite 出现依赖缓存问题,可以清理优化缓存:

rm -rf node_modules/.vite
pnpm dev --force

架构复盘

从工程角度看,这个项目可以分成四层:

  1. 页面层views 负责组织完整工作区。
  2. 组件层components 负责可复用 UI 和业务组件。
  3. 配置层data 负责模型、模式和工具定义。
  4. 计算层workers 负责模型推理。

这种分层让页面不直接关心模型初始化细节,worker 也不关心页面布局。比如 Image Studio 只需要知道当前选择了哪个模式,具体模型如何加载、如何缓存、如何推理,都交给 worker。

配置层也很重要。模型信息不要散落在页面里,否则后期替换模型会很痛苦。把模型配置集中到 data 目录后,页面可以只关心“模式”,worker 可以只关心“model id”。

当前仍然可以优化的地方

这个项目还不是终点,仍然有不少可以继续改的地方:

  • 图片编辑器可以增加缩放和平移。
  • 手动修边可以加入边缘羽化。
  • 图片结果可以增加多模型对比。
  • NLP 可以把大模型聊天迁移到服务端。
  • 任务队列可以增加暂停、重试和取消。
  • 模型下载可以增加更明确的缓存状态。
  • UI 可以继续统一弹窗和编辑器风格。

这些优化里,最值得优先做的是“取消任务”和“多模型对比”。前者能提升批处理控制感,后者能帮助用户判断哪个模型更适合自己的素材。

经验总结

做浏览器端 AI 工具时,有几条经验非常明显。

第一,模型能力不是产品体验的全部。一个模型效果再好,如果下载失败、初始化慢、状态不可见,用户仍然会觉得产品不可用。

第二,公开模型也要筛选。不是 Hugging Face 上能搜到,就适合放进前端。是否 gated、是否大文件、是否依赖特殊格式、是否适配 Transformers.js,都要提前验证。

第三,AI 输出必须有防范手段。抠图需要手动修边,文本需要允许用户编辑结果。把模型输出当作最终答案,会让产品很脆弱。

第四,工作流比模型列表重要。用户不是来体验 pipeline 的,用户是来完成任务的。图片用户要的是导入、处理、修正、下载;文本用户要的是输入、运行、拿到结果。

第五,前端 AI 项目要控制野心。能在浏览器里跑,不代表都应该在浏览器里跑。合适的能力放前端,不合适的能力交给服务端,才是更稳的架构。

总结

WebMatte Studio 的核心思路不是把所有 AI 能力都塞进浏览器,而是选择适合前端运行的模型和场景,把它们包装成可靠、可控、可修正的工作流。

浏览器端 AI 更适合做轻量工具、隐私友好的素材处理、快速原型和低成本工作台。对于大模型聊天、私有模型和高质量生产推理,服务端仍然是更稳的选择。

不要把浏览器端 AI 做成模型展示页,而要做成任务工作台。

模型只是底层能力,产品真正要解决的是用户如何把输入变成可用结果。围绕这个目标,上传、队列、进度、错误、编辑、导出和界面结构都同样重要。

这也是 WebMatte Studio 后续继续演进的方向:少堆模型,多打磨工作流;少做炫技,多做可交付结果。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐