浏览器内置AI能力的开放,算是这两年扩展开发圈子里最让人兴奋的变化之一。以前想在浏览器里跑个摘要、翻译,要么调云端接口烧钱,要么自己塞个模型进去把包体积撑到几十兆。现在Chrome和Edge把Gemini Nano这类端侧模型直接做进了浏览器,通过几个稳定的API就能调用,对于做轻量工具类插件的开发者来说,这扇门一开,很多以前不敢想的小工具都能落地了。我这次做的就是一个典型例子:一个把视频字幕翻译和网页摘要合二为一的浏览器插件,从写第一行代码到提交商店审核,前后6天。这篇文章就把整个过程中的技术选型、踩坑记录、参数调优和发布流程完整拆开讲一遍,适合有前端基础、想尝试浏览器AI能力但还没动手的开发者,也适合已经在做插件、想接入端侧模型提升体验的老手参考。

1. 项目整体设计与技术选型思路

1.1 为什么盯上浏览器内置AI API

先说清楚这个项目要解决什么问题。日常浏览网页时有两类高频需求:一是看外语视频,字幕看不懂,手动复制到翻译工具里来回切换很烦;二是长文章、技术文档、新闻,动辄几千字,想快速知道讲了什么。这两件事本质上都是"把一段文本喂给模型,拿回处理结果",非常适合做成浏览器插件。

那为什么不用云端API?我算过一笔账。假设一个用户每天翻译20条字幕、摘要10篇文章,按主流云端模型的token计费,一个月下来成本不算低,而且还要处理API密钥管理、网络延迟、隐私合规这些麻烦事。端侧模型就绕开了这些问题:数据不出本地,没有网络往返延迟,也没有按量计费。代价是模型能力比云端大模型弱一些,但对于翻译和摘要这种相对成熟的任务,Gemini Nano这个量级的模型已经够用了。

Chrome和Edge的内置AI API(官方叫Prompt API,早期在扩展里通过 chrome.ai 或实验性flag暴露)就是为这种场景设计的。它把端侧模型封装成几个简单的方法调用,开发者不用关心模型加载、内存管理、推理调度这些底层细节。这是它"真香"的核心原因——把最脏最累的活干了,留给你的是干净的接口。

1.2 功能拆解与模块划分

整个插件我拆成三个核心模块,边界划清楚,后面调试和维护都省心。

第一个是 字幕捕获模块 。视频字幕有两种来源:一种是视频自带的外挂字幕文件(比如 .vtt 、 .srt ),可以直接抓取;另一种是硬编码在画面里的,或者平台动态渲染的,这种就得靠Whisper这类语音识别模型从音频转文字。我一开始想全用Whisper本地跑,实测下来发现两个问题:一是模型文件大,打包进插件不现实;二是浏览器里跑Whisper的推理性能不稳定。所以最终方案是优先抓取现成字幕,抓不到再走音频识别,而且音频识别这块做成可选功能,让用户自己决定要不要装本地Whisper服务。

第二个是 翻译模块 。拿到字幕文本后,调用内置AI API做翻译。这里的关键是分句和上下文处理——字幕是一句一句来的,如果每句单独翻译,代词和省略主语会翻得乱七八糟。我的做法是把连续几句拼成一个语义块再翻译,翻完再按时间轴切回去。

第三个是 网页摘要模块 。用户点一下按钮,插件提取当前页面的正文内容(用Readability算法去广告去导航),然后喂给模型生成摘要。摘要长度、语言、详细程度都做成可配置项。

1.3 技术栈与工具链选择

技术栈上我没做太多纠结,直接上最熟的组合:Vite + Vue 3 + TypeScript。Vite的构建速度快,热更新体验好,对于6天要出活的项目来说,开发效率是第一位的。Vue 3的组合式API写逻辑清晰,配合TypeScript能把AI API的返回类型约束住,减少运行时错误。

插件清单用Manifest V3,这是现在Chrome和Edge都强制要求的版本。MV3相比V2最大的变化是后台脚本从常驻的background page变成了按需唤醒的service worker,这对AI调用有影响——service worker随时可能被浏览器回收,所以模型调用的状态管理要格外小心,不能假设它一直活着。

打包和发布用 chrome-webstore-upload-cli 这类工具做自动化上传,省得每次手动点。整个项目结构大致是这样:

src/
  background/       # service worker,处理AI调用
  content/          # 内容脚本,抓字幕、提取正文
  popup/            # 弹窗UI
  options/          # 设置页
  shared/           # 公共类型和工具函数

提示:MV3的service worker有生命周期限制,长时间不活动会被回收。如果你的AI调用是长任务,一定要用 chrome.alarms 或消息机制保持唤醒,否则任务跑到一半worker没了,回调永远不触发。

2. 内置AI API核心细节与调用要点

2.1 Prompt API的能力边界与初始化

浏览器内置AI API的核心是 LanguageModel 这个接口(不同版本命名可能有差异,早期实验阶段叫过 ai.languageModel )。它的调用模式是典型的"创建会话-发送提示-接收流式响应"三段式。

初始化会话的代码大概长这样:

const session = await LanguageModel.create({
  systemPrompt: "你是一个专业翻译,将用户提供的字幕翻译成简体中文,保持口语化,不要添加解释。",
  temperature: 0.3,
  topK: 3
});

这里有几个参数值得说道。 temperature 控制输出的随机性,翻译任务我设成0.3,偏低,保证译文稳定不跑偏;摘要任务可以设到0.7左右,让模型有点发挥空间。 topK 是采样范围,设小一点能让输出更聚焦。这些参数不是拍脑袋定的,是我反复试出来的——翻译设高了会出现同一句话每次翻得不一样的情况,用户体验很差。

systemPrompt 是整个调用里最重要的东西。它决定了模型的角色和行为边界。我踩过一个坑:一开始system prompt写得太笼统,就一句"帮我翻译",结果模型有时候会自作主张加注释、加拼音,甚至把整段话改写成意译。后来把约束写死——"只输出译文,不要任何额外内容"——才稳定下来。

2.2 流式响应与字幕实时翻译的实现

字幕翻译对实时性要求高,用户不希望等整段翻完才看到结果。内置AI API支持流式输出,通过 session.promptStreaming() 拿到一个可迭代的流,边生成边渲染。

const stream = session.promptStreaming(text);
for await (const chunk of stream) {
  updateSubtitleUI(chunk);
}

这里有个细节要注意:流式返回的chunk在不同版本里语义不一样。早期版本返回的是 累积文本 (每次给你的是从开头到当前的全部内容),后来改成了 增量文本 (只给新增的部分)。如果不做兼容处理,UI上会出现文字重复叠加的bug。我的做法是判断一下:如果新chunk以旧内容开头,就当作累积文本处理,取差值;否则直接追加。这个兼容逻辑虽然丑,但实测能同时适配两种行为。

字幕的时间轴对齐也是个技术活。视频字幕每条都带起止时间,翻译后文本长度会变,如果直接替换原文,可能出现译文比原字幕长很多、显示不全的情况。我的处理是保持时间轴不变,只替换文本内容,同时在UI上做自适应——译文过长时缩小字号或允许两行显示。

2.3 网页正文提取与摘要生成策略

网页摘要的难点不在AI调用,而在 正文提取 。网页里充斥着导航栏、广告、评论区、相关推荐,直接把这些喂给模型,摘要质量会惨不忍睹。我用的是Mozilla的Readability算法(就是Firefox阅读模式用的那套),它能比较准确地识别出文章主体。

提取到正文后,还要做分块。模型有上下文长度限制,超长文章不能一次性塞进去。我的策略是:先按段落切分,然后按token估算(粗略按字符数除以2估算中文token)合并成不超过模型上限的块,每块单独摘要,最后把各块摘要再合并成总摘要。这叫"map-reduce"式摘要,虽然多花点推理时间,但能处理任意长度的文章。

摘要的prompt我调了好几版。最初是"总结这篇文章",结果模型经常输出"这篇文章主要讲了……"这种废话开头。后来改成"用3到5个要点概括以下内容,每个要点不超过30字,直接输出要点,不要开场白",输出质量立刻上来了。这说明prompt工程在端侧小模型上比在大模型上更关键——小模型对指令的遵循能力弱,必须把要求写得极其明确。

2.4 模型可用性检测与降级方案

内置AI API不是所有设备都支持。它依赖设备有足够的算力和存储来跑端侧模型,老设备或者低配设备可能压根没有。所以插件启动时必须做能力检测:

if (!('LanguageModel' in self)) {
  // 不支持,走降级方案
}

降级方案我准备了两条路:一是提示用户当前浏览器版本不支持,建议升级;二是提供一个可选的云端API配置入口,让有需要的用户自己填密钥走云端。这里要强调,云端方案是 可选 的,默认关闭,而且密钥只存在本地,绝不上传。这样既照顾了兼容性,又守住了隐私底线。

注意:能力检测要放在service worker启动时做,而不是等用户点了按钮才检测。提前知道能不能用,UI上就能提前把不可用的功能灰掉,避免用户点了没反应。

3. 从零到发布的完整实操流程

3.1 项目初始化与Manifest配置

第一步是搭架子。用Vite的官方模板起项目,然后手动改造成插件结构。Manifest V3的配置文件是整个插件的入口,权限声明要精确,多要权限会导致审核被拒,少要权限功能跑不起来。

{
  "manifest_version": 3,
  "name": "视频翻译与网页摘要助手",
  "version": "1.0.0",
  "permissions": ["activeTab", "scripting", "storage"],
  "host_permissions": ["<all_urls>"],
  "background": {
    "service_worker": "background.js",
    "type": "module"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"]
  }],
  "action": {
    "default_popup": "popup.html"
  }
}

权限这块我精简过好几轮。 activeTab 是点击插件图标时临时获取当前标签页权限,比 tabs 权限更克制; scripting 用于注入内容脚本; storage 存用户配置。 host_permissions 用 <all_urls> 是因为要支持任意网站的摘要,如果只做特定站点可以收窄,审核会更快。

3.2 字幕捕获与Whisper本地识别接入

字幕捕获分两条路。第一条是DOM抓取:很多视频网站的字幕是渲染在特定容器里的,用 MutationObserver 监听字幕变化,实时读取文本。这条路的难点是每个网站的结构不一样,得针对性地写适配规则。我一开始想写通用规则,后来发现不现实,改成"通用兜底+重点站点特化"。

第二条是音频识别。当抓不到字幕时,用 chrome.tabCapture 捕获标签页音频,送到本地Whisper服务转文字。这里要说明,Whisper是本地部署的,不是插件内置的。用户需要自己装一个本地服务(比如用Python跑一个Whisper的HTTP接口),插件通过 localhost 调用。

# 本地Whisper服务示例(用户自行部署)
import whisper
from flask import Flask, request

model = whisper.load_model("base")
app = Flask(__name__)

@app.route("/transcribe", methods=["POST"])
def transcribe():
    audio = request.files["audio"]
    result = model.transcribe(audio)
    return {"text": result["text"]}

为什么用 base 模型而不是更大的?因为 base 在准确率和速度之间平衡得最好, small 以上虽然更准,但推理慢,实时字幕场景下延迟明显。这是实测出来的取舍。

3.3 翻译与摘要功能的联调

两个功能单独跑通后,联调阶段暴露了不少问题。最典型的是 并发冲突 :用户可能同时开着视频翻译和网页摘要,两个任务都在调AI API,如果共用一个session,输出会串。解决办法是每个任务创建独立的session,用完及时销毁。

async function withSession(config, task) {
  const session = await LanguageModel.create(config);
  try {
    return await task(session);
  } finally {
    session.destroy();
  }
}

session.destroy() 这步千万别漏。端侧模型占内存,session不销毁会一直挂着,开多了直接把浏览器拖卡。我一开始没注意,测试时开了十几个session,浏览器直接卡死,排查了半天才发现是这原因。

另一个问题是 错误处理 。AI调用可能因为各种原因失败:模型没下载完、内存不足、被其他任务抢占。每个调用都要包try-catch,失败时给用户明确提示,而不是静默失败。我整理了一张错误码对照表,方便快速定位。

错误类型 可能原因 处理方式
模型未就绪 首次使用模型还在下载 提示等待,监听下载进度
内存不足 同时开的session太多 销毁闲置session,提示用户
上下文超限 输入文本过长 分块处理
权限拒绝 用户未授权 引导到设置页开启

3.4 打包、测试与商店发布

开发完成后就是打包发布。Vite构建产物要检查几件事:service worker的路径对不对、content script有没有被打包成独立文件、静态资源有没有正确引用。我踩过一个坑:Vite默认会把小图片转成base64内联,但插件里有些资源必须保持独立文件,得在配置里排除。

测试阶段我列了个清单,覆盖主流场景:Chrome最新版、Edge最新版、有无AI能力的设备、长文章、多语言视频、网络断开情况。特别要测 首次使用体验 ——模型首次下载可能要几分钟,这期间用户点功能没反应会以为坏了,所以必须有明确的加载提示。

发布到Chrome Web Store和Edge Add-ons的流程类似,都需要开发者账号、填写商店信息、上传zip包、等待审核。审核时间从几小时到几天不等,隐私政策是重点审查项——因为涉及AI和用户数据,必须写清楚数据如何处理、是否上传、存哪里。我的隐私政策里明确写了"所有AI处理均在本地完成,不上传任何用户数据",这条是过审的关键。

提示:提交审核前,务必在 chrome://extensions/ 和 edge://extensions/ 里用开发者模式完整跑一遍,确认没有控制台报错。审核被拒最常见的原因就是功能异常或权限声明与实际使用不符。

4. 常见问题排查与避坑经验

4.1 AI调用相关的典型故障

问题一:模型一直显示"未就绪"。 这种情况多半是模型还在后台下载。端侧模型体积不小,首次使用需要下载,下载进度可以通过API监听。如果长时间没进展,检查设备存储空间是否充足,或者浏览器版本是否支持。

问题二:翻译结果时好时坏。 大概率是temperature设太高,或者system prompt约束不够。把temperature降到0.3以下,system prompt里把"只输出译文"这类硬约束写死,稳定性会明显提升。

问题三:长文章摘要只总结了开头。 这是上下文超限的典型表现,模型只处理了能装下的部分。必须做分块,别指望模型自己处理超长输入。

4.2 插件运行环境的坑

service worker被回收。 前面提过,MV3的service worker随时可能被回收。如果你的AI任务跑得久,要在任务期间通过定期发消息或 chrome.alarms 保持唤醒。我试过在service worker里跑一个长摘要任务,跑到一半worker被回收,回调直接丢失,用户那边一直转圈。后来改成把长任务拆成多个短任务,每个任务完成后重新触发下一个,就稳了。

内容脚本注入失败。 有些网站有严格的内容安全策略(CSP),会阻止内容脚本注入。这种情况要么改用 chrome.scripting.executeScript 动态注入,要么放弃对该站点的支持。别硬刚,成本不划算。

Edge和Chrome的行为差异。 虽然两者都基于Chromium,但内置AI API的可用性和版本节奏不完全一致。Edge有时候会慢半拍,或者API命名有细微差别。开发时两个浏览器都要测,别只测一个就发布。

4.3 性能与体验优化技巧

懒加载模型会话。 不要一打开插件就创建session,等用户真正触发功能再创建。这样能减少内存占用,也能避免不必要的模型加载。

缓存翻译结果。 同一段字幕或同一篇文章,用户可能反复看。把结果缓存到 chrome.storage.local ,下次直接读缓存,省去重复推理。缓存要设过期时间,避免占满存储。

UI反馈要即时。 AI推理有延迟,哪怕只有一两秒,用户也会觉得卡。所以点击后要立刻给反馈——按钮变loading、显示"正在处理",让用户知道系统在工作。

摘要长度可调。 不同用户需求不一样,有人要一句话概括,有人要详细要点。把摘要长度做成滑块或选项,让用户自己控制,体验会好很多。

4.4 发布后的维护要点

发布不是终点。上线后要盯着用户反馈,尤其是AI相关的bug——端侧模型在不同设备上表现差异大,用户设备五花八门,总会遇到你没测到的情况。我上线第一周就收到几个反馈,都是特定设备上模型加载失败,后来加了更详细的能力检测和提示才好。

另外,浏览器内置AI API还在快速迭代,接口可能变。要留好版本兼容的代码,别把API调用写死在业务逻辑里,抽一层适配层,接口变了只改适配层。

常见问题 排查方向 解决手段
功能点击无反应 检查AI能力检测、session创建 加loading提示,完善错误捕获
译文重复叠加 流式chunk语义判断 兼容累积/增量两种模式
浏览器卡顿 session未销毁、并发过多 及时destroy,限制并发数
审核被拒 权限、隐私政策 精简权限,写清数据处理说明

这个项目从想法到上线6天,说快也快,但真正花时间的不是写代码,而是调prompt、处理各种边界情况、适配不同设备。内置AI API确实把门槛降得很低,但要把体验做扎实,该踩的坑一个都少不了。我个人的体会是,端侧AI插件的机会窗口就在现在——能力够用、成本为零、隐私友好,谁先把体验打磨好,谁就能占住这个位置。后续我打算把摘要功能扩展成支持自定义prompt模板,让用户能按自己的需求定制输出格式,这个方向应该还有不少可挖的空间。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐