去年 11 月,美国的 OpenAI 公司推出了 ChatGPT 产品,它在发布后的 5 天内用户数就突破了 100 万,两个月后月活用户突破了 1 个亿,成为至今为止人类历史上用户数增长最快的消费级应用。ChatGPT 之所以能在全球范围内火出天际,不仅是因为它能以逼近自然语言的能力和人类对话,而且可以根据不同的提示语解决各种不同场景下的问题,它的推理能力、归纳能力、以及多轮对话能力都让世人惊叹不已,让实现通用人工智能(AGI,Artificial General Intelligence)变成为了现实,也意味着一种新型的人机交互接口由此诞生,这为更智能的 AI 产品提供了无限可能。
很快,OpenAI 推出了相应的 API 接口,所有人都可以基于这套 API 快速实现一个类似 ChatGPT 这样的产品,当然,聊天对话只是这套 API 的基本能力,OpenAI 官方网站有一个 Examples 页面,展示了结合不同的提示语 OpenAI API 在更多场景下的应用:
OpenAI 提供了很多和 AI 相关的接口,如下:
- Models - 用于列出所有可用的模型;
- Completions - 给定一个提示语,让 AI 生成后续内容;
- Chat - 给定一系列对话内容,让 AI 生成对应的回复,使用这个接口就可以实现类似 ChatGPT 的功能;
- Edits - 给定一个提示语和一条指令,AI 将对提示语进行相应的修改,比如常见的语法纠错场景;
- Images - 用于根据提示语生成图片,或对图片进行编辑,可以实现类似于 Stable Diffusion 或 Midjourney 这样的 AI 绘画应用,这个接口使用的是 OpenAI 的图片生成模型 DALL·E;
- Embeddings - 用于获取一个给定文本的向量表示,我们可以将结果保存到一个向量数据库中,一般用于搜索、推荐、分类、聚类等任务;
- Audio - 提供了语音转文本的功能,使用了 OpenAI 的 Whisper 模型;
- Files - 文件管理类接口,便于用户上传自己的文件进行 Fine-tuning;
- Fine-tunes - 用于管理你的 Fine-tuning 任务,详细内容可参考 Fine-tuning 教程;
- Moderations - 用于判断给定的提示语是否违反 OpenAI 的内容政策;
关于 API 的详细内容可以参考官方的 API reference 和 Documentation。
其中,Completions
、Chat
和 Edits
这三个接口都可以用于对话任务,Completions
主要解决的是补全问题,也就是说用户给出一段话,模型可以按照提示语续写后面的内容;Chat
用于处理聊天任务,它显式的定义了 system
、user
和 assistant
三个角色,方便维护对话的语境信息和多轮对话的历史记录;Edit
主要用于对用户的输入进行修改和纠正。
要调用 OpenAI 的 API 接口,必须先创建你的 API Keys,然后请求时像下面这样带上 Authorization
头即可:
Authorization: Bearer OPENAI_API_KEY
下面是直接使用 curl 调用 Chat
接口的示例:
$ curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "你好!"}],
"temperature": 0.7
}'
我们可以得到类似下面的回复:
{
"id": "chatcmpl-7LgiOhYPcGGwoBcEPQmQ2LaO2pObn",
"object": "chat.completion",
"created": 1685403440,
"model": "gpt-3.5-turbo-0301",
"usage": {
"prompt_tokens": 11,
"completion_tokens": 18,
"total_tokens": 29
},
"choices": [
{
"message": {
"role": "assistant",
"content": "你好!有什么我可以为您效劳的吗?"
},
"finish_reason": "stop",
"index": 0
}
]
}
如果你无法访问 OpenAI 的接口,或者没有 OpenAI 的 API Keys,网上也有很多免费的方法,比如 chatanywhere/GPT_API_free。
OpenAI 官方提供了 Python 和 Node.js 的 SDK 方便我们在代码中调用 OpenAI 接口,下面是使用 Node.js 调用 Completions
的示例:
import { Configuration, OpenAIApi } from "openai";
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
const response = await openai.createCompletion({
"model": "text-davinci-003",
"prompt": "你好!",
"max_tokens": 100,
"temperature": 0
});
console.log(response.data);
由于 SDK 底层使用了 axios 库发请求,所以我们还可以对 axios 进行配置,比如像下面这样设置代理:
const response = await openai.createCompletion({
"model": "text-davinci-003",
"prompt": "你好!",
"max_tokens": 100,
"temperature": 0
}, {
proxy: false,
httpAgent: new HttpsProxyAgent(process.env.HTTP_PROXY),
httpsAgent: new HttpsProxyAgent(process.env.HTTP_PROXY)
});
从上面的例子可以看出,OpenAI 提供的 Completions
或 Chat
只是一套用于对话任务的接口,并没有提供翻译接口,但由于它的对话已经初步具备 AGI 的能力,所以我们可以通过特定的提示语让它实现我们想要的功能。官方的 Examples 页面有一个 English to other languages 的例子,展示了如何通过提示语技术将英语翻译成法语、西班牙语和日语,我们只需要稍微修改下提示语,就可以实现英译中的功能:
async function translate(text) {
const prompt = `Translate this into Simplified Chinese:\n\n${text}\n\n`
const openai = createOpenAiClient();
const response = await openai.createCompletion({
"model": "text-davinci-003",
"prompt": prompt,
"max_tokens": 100,
"temperature": 0
}, createAxiosOptions());
return response.data.choices[0].text
}
上面我们使用了 Translate this into Simplified Chinese:
这样的提示语,这个提示语既简单又直白,但是翻译效果却非常的不错,我们随便将一段官方文档丢给它:
console.log(await translate("The OpenAI API can be applied to virtually any task that involves understanding or generating natural language, code, or images."));
OpenAI API 可以应用于几乎任何涉及理解或生成自然语言、代码或图像的任务。
看上去,翻译的效果不亚于 Google 翻译,而且更神奇的是,由于这里的提示语并没有明确输入的文本是什么,也就意味着,我们可以将其他任何语言丢给它:
console.log(await translate("どの部屋が利用可能ですか?"));
这些房间可以用吗?
这样我们就得到了一个通用中文翻译接口。
我在很久以前写过一篇关于 Chrome 插件的博客,我的第一个 Chrome 扩展:Search-faster,不过当时 Chrome 扩展还是 V2 版本,现在 Chrome 扩展已经发展到 V3 版本了,并且 V2 版本不再支持,于是我决定将 Chrome 扩展的开发文档 重温一遍。
每个 Chrome 插件都需要有一个 manifest.json
清单文件,我们创建一个空目录,并在该目录下创建一个最简单的 manifest.json
文件:
{
"name": "Chrome Extension Sample",
"version": "1.0.0",
"manifest_version": 3,
"description": "Chrome Extension Sample"
}
这时,一个最简单的 Chrome 插件其实就已经准备好了。我们打开 Chrome 的 管理扩展程序 页面 chrome://extensions/
,启用开发者模式,然后点击 “加载已解压的扩展程序”,选择刚刚创建的那个目录就可以加载我们编写的插件了:
只不过这个插件还没什么用,如果要添加实用的功能,还得添加这些比较重要的字段:
background
:背景页通常是 Javascript 脚本,在扩展进程中一直保持运行,它有时也被称为 后台脚本,它是一个集中式的事件处理器,用于处理各种扩展事件,它不能访问页面上的 DOM,但是可以和content_scripts
和action
之间进行通信;在 V2 版本中,background
可以定义为scripts
或page
,但是在 V3 版本中已经废弃,V3 版本中统一定义为service_worker
;content_scripts
:内容脚本可以让我们在 Web 页面上运行我们自定义的 Javascript 脚本,通过它我们可以访问或操作 Web 页面上的 DOM 元素,从而实现和 Web 页面的交互;内容脚本运行在一个独立的上下文环境中,类似于沙盒技术,这样不仅可以确保安全性,而且不会导致页面上的脚本冲突;action
:在 V2 版本中,Chrome 扩展有browser_action
和page_action
两种表现形式,但是在 V3 版本中,它们被统一合并到action
字段中了;用于当用户点击浏览器右上角的扩展图标时弹出一个 popup 页面或触发某些动作;options_page
:当你的扩展参数比较多时,可以制作一个单独的选项页面对你的扩展进行配置;
接下来,我们在 manifest.json
文件中加上 action
字段:
"action": {
"default_popup": "popup.html"
}
然后,编写一个简单的 popup.html
页面,比如直接使用 iframe
嵌入我的博客:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<iframe src="https://www.aneasystone.com" frameborder="0" style="width: 400px;height:580px;"></iframe>
</body>
</html>
修改完成后,点击扩展上的刷新按钮,将会重新加载扩展:
这样当我们点击扩展图标时,就能弹出我的博客页面了:
如果我们把页面换成 ChatGPT 的页面,那么一个 ChatGPT 的 Chrome 插件就做好了:
在嵌入 ChatGPT 页面时发现,每次打开扩展都会跳转到登录页面,后来参考 kazuki-sf/ChatGPT_Extension 这里的做法解决了:在
manifest.json
中添加content_scripts
字段,内容脚本非常简单,只需要一句"use strict";
即可。
注意并不是所有的页面都可以通过
iframe
嵌入,比如当我们嵌入 Google 时就会报错:www.google.com 拒绝了我们的连接请求,这是因为 Google 在响应头中添加了X-Frame-Options: SAMEORIGIN
这样的选项,不允许被嵌入在非同源的iframe
中。
我们现在已经学习了如何使用 OpenAI 接口实现翻译功能,也学习了 Chrome 扩展的基本知识,接下来就可以实现划词翻译功能了。
首先我们需要监听用户在页面上的划词动作以及所划的词是什么,这可以通过监听鼠标的 onmouseup
事件来实现。根据前面一节的学习我们知道,内容脚本可以让我们在 Web 页面上运行我们自定义的 Javascript 脚本,从而实现和 Web 页面的交互,所以我们在 manifest.json
中添加 content_scripts
字段:
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content_script.js"]
}
]
content_script.js
文件的内容很简单:
window.onmouseup = function (e) {
// 非左键,不处理
if (e.button != 0) {
return;
}
// 未选中文本,不处理
let text = window.getSelection().toString().trim()
if (!text) {
return;
}
// 翻译选中文本
let translateText = translate(text)
// 在鼠标位置显示翻译结果
show(e.pageX, e.pageY, text, translateText)
}
可以看到实现划词翻译的整体脉络已经非常清晰了,后续的工作就是调用 OpenAI 的接口翻译文本,以及在鼠标位置将翻译结果显示出来。先看看如何实现翻译文本:
async function translate(text) {
const prompt = `Translate this into Simplified Chinese:\n\n${text}\n\n`
const body = {
"model": "text-davinci-003",
"prompt": prompt,
"max_tokens": 100,
"temperature": 0
}
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + OPENAI_API_KEY,
},
body: JSON.stringify(body),
}
const response = await fetch('https://api.openai.com/v1/completions', options)
const json = await response.json()
return json.choices[0].text
}
这里直接使用我们之前所用的提示语,只不过将发请求的 axios
换成了 fetch
,fetch
是浏览器自带的发请求的 API,但是不能在 Node.js 环境中使用。
接下来我们需要将翻译后的文本显示出来:
function show(x, y, text, translateText) {
let container = document.createElement('div')
container.innerHTML = `
<header>翻译<span class="close">X</span></header>
<main>
<div class="source">
<div class="title">原文</div>
<div class="content">${text}</div>
</div>
<div class="dest">
<div class="title">简体中文</div>
<div class="content">${translateText}</div>
</div>
</main>
`
container.classList.add('translate-panel')
container.classList.add('show')
container.style.left = x + 'px'
container.style.top = y + 'px'
document.body.appendChild(container)
let close = container.querySelector('.close')
close.onclick = () => {
container.classList.remove('show')
}
}
我们先通过 document.createElement()
创建一个 div
元素,然后将其 innerHTML
赋值为提前准备好的一段 HTML 模版,并将原文和翻译后的中文放在里面,接着使用 container.classList.add()
和 container.style
设置它的样式以及显示位置,最后通过 document.body.appendChild()
将这个 div
元素添加到当前页面中。实现之后的效果如下图所示:
至此,一个简单的划词翻译 Chrome 插件就开发好了,开发过程中参考了 CaTmmao/chrome-extension-translate 的部分实现,在此表示感谢。
当然这个扩展还有很多需要优化的地方,比如 OpenAI 的 API Keys 是写死在代码里的,可以做一个选项页对其进行配置;另外在选择文本时要等待一段时间才显示出翻译的文本,中间没有任何提示,这里的交互也可以优化一下;还可以为扩展添加右键菜单,进行一些其他操作;有兴趣的朋友可以自己继续尝试改进。
- OpenAI API reference
- OpenAI Documentation
- Documentation for Chrome extensions developers
- 有手就行,从零开始的V3版本Chrome创意插件开发攻略
- GoogleChrome/chrome-extensions-samples - Chrome Extensions Samples
- openai-translator/openai-translator - 基于 ChatGPT API 的划词翻译浏览器插件和跨平台桌面端应用