💰 点进来就是赚到知识点!本文手把手教你用 Web 和 AI 去实现灵感和创意 ,点赞 、收藏 、评论更能促进消化吸收!
🚀 想解锁更多 Web AI 的强大能力吗?快来订阅专栏「Web AI 进化录」!
为什么要做一款解词插件
需求场景
我最近在学英语,会强迫自己读一些英文资料,比如 GitHub 上的文档、X 上的资讯、arxiv 上的论文等等,但我词汇量太少,所以常常遇到生词。我先后尝试了好多翻译工具,有道词典、Chrome 自带翻译、MacOS 系统词典、沉浸式翻译插件...... 这些工具要么需要中断阅读、切换到其他界面;要么翻译得不够贴切,需要我自己去适配当前的语境。
所以后来我发现,我需要的是一个能够原地给出解释,而且是结合语境用英文解释的工具,这样就能保持专注,不被打断;用英文解释也是很多学英语方法里提到的,用简单基础的词汇去理解高级词汇,咱们中国老话也说,原汤化原食嘛。
于是我的需求已经很明确了,这时候我有两个选择,一个是去找市面上符合我需求的工具,另一个就是遵循咱们程序员的本能,自己造一个轮子。我简单评估了一下,发现自己开发一个会更快,因为我手握两件法宝。
两件法宝
Web 技术和 Chrome 内置 AI,这两样法宝都是灵活强大而简单好用,我甚至感觉这两种技术就是为了帮我学英语而发明的,因为太水到渠成了------
首先我看英文内容基本都是在浏览器里看在线文档,所以把解词工具做成浏览器扩展是再适合不过的,而开发浏览器扩展用 Web 技术就够了。
其次是解词功能,我们很容易想到用大语言模型来做。而 Prompt API 就是 Chrome 浏览器内置大模型的配套 API,让你连服务端代码和第三方 API 都省了,非常方便。
效果演示
这个工具我已经做出来投入使用了,下面我来给大家演示一下效果。
假设我现在正在读一些英文资料,读着读着遇到一个不懂的词,这时候我只需要打开解词工具,然后双击选中生词,解词工具会自动捕获选中的词,而且会抓取这个词的上下文,然后用小学生都能看懂的英文给出解释。全程只需要几秒钟,我就可以继续阅读了。怎么样,是不是很丝滑?
这个插件的完整代码我也放到 GitHub 上了,大家可以把代码拉下来自己试着玩玩。
代码仓库里有两个文件夹,full-version
是刚刚演示的扩展;template
是我为咱们稍后代码实操准备的模板。
本地安装
由于这个扩展还没有上架 Chrome Web Store,所以我们可以在本地导入源码进行安装。
首先我们打开 Chrome 浏览器(最好是最新版),然后访问 chrome://extensions
接着,在右上角打开开发者模式,再到左上角点击导入按钮,选中本地代码仓库的 full-version
目录,这样就安装好了,你会看到扩展列表里这个橘黄色的 icon。
点击 icon 会打开侧边栏,因为大家是首次运行,所以可能会看到这几种状态:
如果是红色的不可用,那你可能需要换其他电脑试试;如果是黄色的下载进度,那就等模型下载完。如果是绿色,那就万事俱备了。
什么是 Prompt API
今年六月份开始,Google 开始在 Chrome 里装载 Gemini Nano 这个大模型。
Gemini 是 Google 推出的多模态模型家族,前不久发布的 Gemini 2 效果非常惊艳。而 Nano 则是家族中的轻量模型,参量有 1.8B 和 3.25B 两个版本,非常小巧,适合用在部署在用户端设备上,也就是端侧智能的概念。而把 Nano 部署到 Chrome 里,让 AI 本地化又迈进了一大步。
有了 Gemini Nano 和其他的一些专家模型,内置 AI 不仅可以给浏览器提供推理能力,而且还有配套的 API,让开发者用简洁的 JavaScript 代码就能把 AI 能力装载到自己的 Web 应用中去。
内置 AI 目前还处于实验阶段,对开发者和少部分用户开放了内测。这是目前支持的 API 的推进进展:
我们看到它在网页和浏览器扩展中都可以使用。其中 EPP 指的是 Early Preview Program,是一种面向开发者的内测群;而 OT 则是 Origin Trials,是一种面向用户开启实验功能的方式。我们今天演示的扩展就是用了 OT token。
在这么多处理特定任务的 API 中,我们今天用到和着重介绍的就是 Prompt API
。
Prompt 意思是提示词,我们和大模型交互,就是通过提示词的形式,而 Prompt API 就是让我们能用自然语言和大模型来交互。比如我们可以基于 Prompt API 开发 Chatbot,用 topK、temperature 等参数去调控生成内容,也可以通过限定系统提示词、角色、示例,来完成特定任务。
这套 API 提供两个方法:
-
capabilities()
方法来检测可用性; -
create()
方法来创建会话session
,我们与模型的交互就是和session
交互。create 方法接收一些配置参数,让我们可以定制角色、初始状态(initialPrompt、systemPrompt、topK、temperature),还能对过程进行控制(monitor、signal)。
创建出的 session
有一些 token 相关的属性和方法,有两个接收提示词、生成文本的方法,有复制和销毁的方法。
看起来有点复杂,但我们今天只需要用到图中白色高亮的这几个,就足够了。
从零搭建
现在正式进入动手写代码环节。
当然我们不会一个字符一个字符地手搓代码。在 GitHub 仓库中,我把核心逻辑拆解成了一个个模块。我们会以搭积木的形式把解词插件拼装起来,在拼装过程中拆解 Prompt API 的应用技巧。
我们还是打开刚刚 pull 到本地的代码仓库,进入 template
目录。在这里你会看到一个浏览器扩展的模板结构:
manifest.json
:配置文件background.js
:浏览器层面的高级逻辑side-panel/html、js
:侧边栏页面的代码文件
在 manifest.json
中,有两个字段需要我们特别注意一下。
key
key
是每个浏览器扩展的唯一 ID。但更常见的是一个短字符串的形式,比如我们打开 chrome://extensions 就可以看到每个扩展都有一个短 ID。但由于我们是通过导入文件夹的形式安装的,所以这个 ID 在你电脑上和在我电脑上是不一样的。为了把这个 ID 固定住,我们可以把代码文件夹打一个压缩包,上传到浏览器扩展的开发者后台,就会生成 key 这个身份标识。把 key
写到 manifest.json
中,就把 ID 固定住了,在你们电脑上和在我电脑上的 ID 就保持一致了。
那让 ID 保持一致有什么用呢?这就要说到第二个字段 trial_tokens
了。
trial_tokens
这个 token 能够让浏览器开启实验性功能。为了得到这个 token,我们需要登录 Google 的 OT 平台,用浏览器扩展的 ID 注册 Prompt API。
所以大家明白了吧,如果不固定住 ID,那么我注册好的 token 在你电脑上就会失效。
如果大家发布自己的浏览器扩展,需要自己去注册 ID 和 token。
搭建步骤 1:UI
我们先来实现「点击 icon 打开侧边栏」这个功能。
在 background.js
中添加以下代码:
JavaScript
// 点击 icon 打开或关闭侧边栏
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
然后,我们给解词插件添加一个最简的 UI 界面。
在 side-panel/index.html
的 <body>
标签中添加如下代码:
html
<div class="label">输入</div>
<div class="input-box">
<textarea id="input-area"></textarea>
</div>
<div class="label">输出</div>
<div class="output-box">
<textarea id="output-area" rows="10"></textarea>
</div>
在 side-panel/index.js
中添加如下代码:
JavaScript
const inputArea = document.getElementById('input-area');
const outputArea = document.getElementById('output-area');
保存后,我们仍然是通过本地导入的方式安装扩展,安装后你会看到一个亮绿色的 icon:
点击 icon,你会看到 Chrome 右侧展开了侧边栏区域:
自动捕获选中文本
接着,我们让侧边栏能够自动捕获到用户在左侧页面选中的文本。
在 background.js
中添加如下代码:
JavaScript
chrome.tabs.onActivated.addListener((activeInfo) => {
chrome.tabs.get(activeInfo.tabId, tab => {
if (tab.url?.startsWith('chrome://')) return;
// 给左侧页面注入脚本
chrome.scripting.executeScript({
target: { tabId: activeInfo.tabId },
function: listenSelection,
});
});
});
function listenSelection() {
document.addEventListener('selectionchange', () => {
const selection = window.getSelection();
if (!selection?.toString?.()) return;
const selectedText = selection.toString().trim();
// 获取选中文本所在的元素
const container = selection.anchorNode?.parentElement;
// 获取上下文
const fullText = container?.textContent || '';
let beforeText = '';
let afterText = '';
if (fullText) {
const selectionStart = fullText?.indexOf?.(selectedText);
if (selectionStart === -1) return;
// 向前向后各取 20 个单词
const halfLength = 20;
beforeText = fullText.slice(0, selectionStart).split(/\s+/).slice(-halfLength).join(' ');
afterText = fullText.slice(selectionStart + selectedText.length).split(/\s+/).slice(0, halfLength).join(' ');
};
// 把选中的文本和上下文发送给 extension
chrome.runtime.sendMessage({
type: 'textSelected',
text: selectedText,
context: {
before: beforeText,
after: afterText
}
});
});
}
这段代码在侧边栏打开时向左侧页面注入了 listenSelection
这段逻辑,用来监听文本选中事件。当用户选中一段文本,注入脚本会自动抓取文本和前后的上下文,然后通过消息机制发送给侧边栏。
在 sendMessage
中,我们自定义了一个 textSelected
消息类型,接着,我们在侧边栏接收这个消息。
侧边栏接收消息
在 side-panel/index.js
中添加如下代码:
JavaScript
chrome.runtime.onMessage.addListener(message => {
if (message.type === 'textSelected') {
const { text, context } = message;
inputArea.value = text;
explain(text, context);
}
});
在这段代码中,我们以 textSelected
为暗号,取得了注入脚本发来的密电,从中提取了数据,显示在 UI 的输入框内。
保存代码并刷新扩展,你会看到自动捕获的效果。
检测 API 是否可用
由于内置 AI 还是实验性功能,并非所有的浏览器都支持,所以我们要检测可用性,保证优雅降级。
在 side-panel/index.js
中添加如下代码:
JavaScript
await check();
async function check() {
if (!chrome?.aiOriginTrial?.languageModel?.capabilities) return Promise.reject('no capabilities');
const capabilities = await chrome.aiOriginTrial.languageModel.capabilities();
const { available } = capabilities;
console.log('available', available); // no, after-download, readily
if (available === 'no') return Promise.reject('no available');
return available;
}
在上述代码中,我们先判断了 API 在用户运行环境中是否存在;如果存在,再去调用 capabilities()
方法,方法返回的 available
有三种取值:
no
:不可用after-download
:需要等模型下载完readily
:可用
创建 session
如果一切顺利,我们就可以开始和内置大模型交互了。我们首先来创建一个会话。
在 side-panel/index.js
中添加如下代码:
JavaScript
let session = null;
await createSession();
async function createSession() {
const config = {
initialPrompts: [
{
role: 'system',
content: `You are a language expert who explains words and phrases in their proper context. When given a text and its surrounding context, explain the meaning while considering how the context affects the interpretation. If there is no context, explain the word or phrase in isolation. Use simple terms, relatable examples, and engaging analogies. If you don't understand the content, say so and provide suggestions for finding the answer. Format output in markdown and keep responses under 50 words.`
}
],
}
session = await chrome.aiOriginTrial.languageModel.create(config);
}
我们首先需要在 initialPrompts
中设置系统提示词,然后把配置参数传入 create()
,这样就生成了一个专门用来解词的 session。
生成解释
在 side-panel/index.js
中添加如下代码:
JavaScript
async function explain(input, context) {
if (!input) return;
const prompt = `
Here is the text that needs to be explained:
${input}
Here is the context where this text appears:
${context.before} [${input}] ${context.after}
Please explain the text above using simple terms, relatable examples, and engaging analogies. Keep your explanation under 30 words and format the response in plain text, not markdown.
`
const stream = await session.promptStreaming(prompt);
for await (const chunk of stream) {
outputArea.textContent = chunk;
}
}
这里我们实现了 explain()
方法,我们在「侧边栏接收消息」的部分调用了它。
我们首先组装了一下提示词,把捕获到的词语和上下文嵌入到提示词,告诉大模型如何处理。
然后我们把提示词传入 promptStreaming()
方法,大模型会以数据流的形式输出词语解释,显示在输出文本框中。
保存后刷新扩展,在左侧页面选中文字后,侧边栏会自动生成解释,并显示在输出区域。
于是,我们成功地开发出了基于内置 AI 的浏览器扩展!撒花!
结语
今天我们实践了 Chrome 内置的 Prompt API,用它来开发了一款 AI 解词插件。Web 技术和内置 AI 联手,让开发者能以最低成本、最高效率实现自己的灵感和创意,大家快快玩起来!
📣 我是 Jax,在畅游 Web 技术海