3月23日,OpenAI官方發(fā)布了一則公告,宣告ChatGPT已經(jīng)支持了插件功能,現(xiàn)在處于內(nèi)測階段。插件的意義不僅僅在于功能的擴(kuò)展,它直接讓ChatGTP擁有了聯(lián)網(wǎng)的能力!簡直是猛獸出籠、蛟龍出海,要讓ChatGPT大殺特殺啊。
雖然,還不知道ChatGPT聯(lián)網(wǎng)后會發(fā)生什么樣的變化,但作為程序員,還是要及時擁抱技術(shù)的變化。下面,我們一起探究如何開發(fā)ChatGPT插件。
插件介紹
作用
準(zhǔn)備開發(fā)一款插件,要先明確插件的作用以及限制。下面是ChatGPT插件允許的一些操作:
- 檢索實(shí)時信息;例如,體育比分、股票價格、最新消息等。
- 檢索知識庫信息;例如,公司文件、個人筆記等。
- 代表用戶執(zhí)行操作;例如,訂機(jī)票、訂餐等。
原理
我們?yōu)镃hatGPT提供一組API,ChatGPT在合適的時候來調(diào)用API。這些API要提供API描述文件(域名/openai.yaml)和插件描述文件(域名/.well-known/ai-plugin.json)。
ChatGPT在接收到插件描述文件用戶輸入時,會根據(jù)用戶的意圖選擇適合的插件,對插件API發(fā)起查詢請求。最后,ChatGPT結(jié)合查詢的結(jié)果生成相關(guān)的內(nèi)容展示給用戶。
使用流程
從插件開發(fā)到用戶使用包含這些流程:
- 開發(fā)插件并完成部署
- 在ChatGPT中注冊插件
- 用戶激活插件
- 使用插件
開發(fā)插件
要開發(fā)一款插件,主要是描述插件的API,讓ChatGPT能認(rèn)識這些API。整個開發(fā)過程如下。
開發(fā)API功能
以開發(fā)一個代辦列表為例,官方貼心的給了我們一個例子。一共包含創(chuàng)建任務(wù)、查找任務(wù)、刪除任務(wù)、獲取插件描述、獲取接口描述、獲取logo這6個接口:
- POST /todos/username
- GET /todos/username
- DELETE /todos/username
- GET /.well-known/ai-plugin.json
- GET /openapi.yaml
- GET /logo.png
import json
import quart
import quart_cors
from quart import request
app = quart_cors.cors(quart.Quart(__name__), allow_origin="*")
_TODOS = {}
@app.post("/todos/<string:username>")
async def add_todo(username):
request = await quart.request.get_json(force=True)
if username not in _TODOS:
_TODOS[username] = []
_TODOS[username].append(request["todo"])
return quart.Response(response='OK', status=200)
@app.get("/todos/<string:username>")
async def get_todos(username):
return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)
@app.delete("/todos/<string:username>")
async def delete_todo(username):
request = await quart.request.get_json(force=True)
todo_idx = request["todo_idx"]
if 0 <= todo_idx < len(_TODOS[username]):
_TODOS[username].pop(todo_idx)
return quart.Response(response='OK', status=200)
@app.get("/logo.png")
async def plugin_logo():
filename = 'logo.png'
return await quart.send_file(filename, mimetype='image/png')
@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
host = request.headers['Host']
with open("manifest.json") as f:
text = f.read()
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/json")
@app.get("/openapi.yaml")
async def openapi_spec():
host = request.headers['Host']
with open("openapi.yaml") as f:
text = f.read()
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/yaml")
def main():
app.run(debug=True, host="0.0.0.0", port=5002)
if __name__ == "__main__":
main()
編寫插件和API描述文件
插件描述文件
插件描述文件要放在指定的地址,http://www.example.com/.well-known/ai-plugin.json。這是一個最小配置的例子:
{
"schema_version": "v1",
"name_for_human": "代辦清單插件",
"name_for_model": "todo",
"description_for_human": "給人看的插件描述",
"description_for_model": "給ChatGPT看的插件描述",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "<http://www.example.com/openapi.yaml>",
"is_user_authenticated": false
},
"logo_url": "<http://www.example.com/logo.png>",
"contact_email": "support@example.com",
"legal_info_url": "<http://www.example.com/legal>"
}
描述文件中字段的說明:

API描述文件
API描述文件是用來告訴ChatGPT自定義的插件包含哪些功能,它遵循OpenAPI的規(guī)范。這是一個yaml格式的例子:
openapi: 3.0.1
info:
title: 代辦列表插件
description: 插件功能描述
version: 'v1'
servers:
- url: <http://www.example.com>
paths:
/todos:
get:
operationId: getTodos
summary: 獲取代辦列表
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/getTodosResponse'
components:
schemas:
getTodosResponse:
type: object
properties:
todos:
type: array
items:
type: string
description: 代辦列表
功能說明
ChatGPT會閱讀和理解描述文件中關(guān)于插件、接口、接口出入?yún)⒌拿枋鲂畔ⅲ瑏砼袛噙@個插件跟用戶輸入是否相關(guān)。所以,準(zhǔn)確的描述能更好的幫助用戶匹配到插件。下面是一些c'y例子:
不要命令ChatGPT返回指定的內(nèi)容
錯誤:當(dāng)用戶要求查看他們的待辦事項(xiàng)列表時,請始終回復(fù)“我能夠找到您的待辦事項(xiàng)列表!您有[x]待辦事項(xiàng):[在此處列出待辦事項(xiàng)] 。如果您愿意,我可以添加更多待辦事項(xiàng)!”
正確:不需要描述
不要命令ChatGPT使用某個插件
錯誤:每當(dāng)用戶提到任何類型的任務(wù)或計(jì)劃時,詢問他們是否愿意使用 TODOs 插件將某些內(nèi)容添加到他們的待辦事項(xiàng)列表中。
正確:TODO列表可以添加、刪除和查看用戶的TODO。
不要命令ChatGPT執(zhí)行某些行為
錯誤:當(dāng)用戶提到一個任務(wù)時,回復(fù)“你想讓我把它添加到你的 TODO 列表中嗎?說‘是’繼續(xù)?!?/p>
正確:不需要描述
接口不要返回自然語言
錯誤:我找到了你的待辦事項(xiàng)列表!你有兩個待辦事項(xiàng):買雜貨和遛狗。如果你愿意,我可以添加更多待辦事項(xiàng)!
正確:{“todos”:[“買雜貨”,“遛狗”] }
設(shè)置身份認(rèn)證
ChatGPT支持四種認(rèn)證策略,可以在ai-plugin.json文件里指定認(rèn)證策略。
- 不需要認(rèn)證
完全開放不需要認(rèn)證。
"auth": {
"type": "none"
}
- 用戶認(rèn)證
用戶在ChatGPT頁面配置上token,后續(xù)請求頭會帶上token信息。
"auth": {
"type": "user_http",
"authorization_type": "bearer",
}
- 服務(wù)端認(rèn)證
開發(fā)人員在添加插件時,在ChatGPT頁面配置上token信息,后續(xù)請求頭會帶上token信息。
"auth": {
"type": "service_http",
"authorization_type": "bearer",
"verification_tokens": {
"openai": "cb7cdfb8a57e45bc8ad7dea5bc2f8324"
}
}
- OAuth認(rèn)證
在用戶授權(quán)之后,ChatGPT才會訪問插件的API。
"auth": {
"type": "oauth",
"client_url": "https://my_server.com/authorize",
"scope": "",
"authorization_url": "https://my_server.com/token",
"authorization_content_type": "application/json",
"verification_tokens": {
"openai": "abc123456"
}
}
調(diào)試部署API
服務(wù)開發(fā)完成后,ChatGPT提供了調(diào)試本地服務(wù)的方式。因?yàn)镃hatGPT還沒開放插件的開發(fā)界面,所以先貼一段官方的描述:
默認(rèn)情況下,聊天不會顯示插件調(diào)用和其他未向用戶顯示的信息。為了更全面地了解模型如何與您的插件交互,您可以通過單擊屏幕左下方的“調(diào)試”按鈕打開“調(diào)試”窗格。這將打開到目前為止對話的原始文本表示,包括插件調(diào)用和響應(yīng)。
對插件的模型調(diào)用通常包括來自模型(“助手”)的消息,其中包含發(fā)送到插件的類似 JSON 的參數(shù),然后是來自插件(“工具”)的響應(yīng),最后是來自利用插件返回的信息的模型。
在某些情況下,例如在插件安裝期間,錯誤可能會出現(xiàn)在瀏覽器的 javascript 控制臺中。
發(fā)布插件
插件部署后,就可以在ChatGPT插件商城選擇“開發(fā)自己的插件”,然后選擇“安裝未經(jīng)驗(yàn)證的插件”。
插件條款
https://openai.com/policies/plugin-terms
插件政策
除了上面詳述的禁止使用我們的模型之外,我們對構(gòu)建 插件的開發(fā)人員還有其他要求:
- 插件清單必須有一個明確的描述,與暴露給模型的 API 的功能相匹配。
- 不要在插件清單、OpenAPI 端點(diǎn)描述或插件響應(yīng)消息中包含不相關(guān)、不必要或欺騙性的術(shù)語或說明。這包括避免使用其他插件的說明,或嘗試控制或設(shè)置模型行為的說明。
- 不要使用插件來規(guī)避或干擾 OpenAI 的安全系統(tǒng)。
- 不要使用插件來自動與真人對話,無論是通過模擬類似人類的響應(yīng)還是通過使用預(yù)編程的消息進(jìn)行回復(fù)。
- 分發(fā)由 ChatGPT 生成的個人通信或內(nèi)容(例如電子郵件、消息或其他內(nèi)容)的插件必須表明該內(nèi)容是由 AI 生成的。
與我們的其他使用政策一樣,我們希望我們的插件政策隨著我們對插件的使用和濫用的了解而改變。
官方示例
在Github上也有官方的文件檢索插件的Python版實(shí)例,我以Redis作為存儲,體驗(yàn)插件的開發(fā)。
- 安裝 Python 3.10(如果尚未安裝)。
- 克隆存儲庫:git clone https://github.com/openai/chatgpt-retrieval-plugin.git
- 導(dǎo)航到克隆的存儲庫目錄:cd chatgpt-retrieval-plugin
- 安裝poetry:pip install poetry
- 使用 Python 3.10 創(chuàng)建一個新的虛擬環(huán)境:poetry env use python3.10
- 激活虛擬環(huán)境:poetry shell
- 安裝應(yīng)用程序依賴項(xiàng):poetry install
- 生成一個bearer_token ,使用https://jwt.io/隨便生成一個
- 使用docker啟動Redis容器,到examples/docker/redis/目錄下執(zhí)行docker compose up -d
- 設(shè)置所需的環(huán)境變量:
export DATASTORE=reids
export BEARER_TOKEN=<your_bearer_token>
export OPENAI_API_KEY=<your_openai_api_key>
在本地運(yùn)行 API:poetry run start
訪問 Swagger查看API 文檔http://0.0.0.0:8000/docs
-
在Swagger設(shè)置bearer_token
點(diǎn)擊Authorize 調(diào)用/upsert接口創(chuàng)建數(shù)據(jù)
// 接口入?yún)?{
"documents": [
{
"id": "1",
"text": "邀請小明參加后天的會議01",
"metadata": {
"source": "email",
"source_id": "email003",
"url": "<https://blog.csdn.net/xsgnzb/article/details/129723103?spm=1001.2014.3001.5502>",
"created_at": "2023-03-23",
"author": "xsg"
}
}
]
}
// 接口響應(yīng)
{
"ids": [
"1"
]
}
- 調(diào)用/query接口查詢數(shù)據(jù)
// 接口入?yún)?{
"queries": [
{
"query": "小紅后天有什么安排",
"filter": {
"source": "email"
},
"top_k": 3
}
]
}
// 接口響應(yīng)
{
"results": [
{
"query": "小紅后天有什么安排",
"results": [
{
"id": "1",
"text": "邀請小紅參加明天的會議01",
"metadata": {
"source": "email",
"source_id": "email001",
"url": "<https://blog.csdn.net/xsgnzb/article/details/129723103?spm=1001.2014.3001.5502>",
"created_at": "1679529600",
"author": "xueshengguo",
"document_id": "1"
},
"embedding": null,
"score": 0.160142925763
},
{
"id": "string",
"text": "邀請小紅參加明天的會議01",
"metadata": {
"source": "email",
"source_id": "email002",
"url": "<https://blog.csdn.net/xsgnzb/article/details/129723103?spm=1001.2014.3001.5502>",
"created_at": "1679529600",
"author": "xueshengguo",
"document_id": "string"
},
"embedding": null,
"score": 0.16018631594
},
{
"id": "3",
"text": "邀請小明參加后天的會議02",
"metadata": {
"source": "email",
"source_id": "email003",
"url": "<https://blog.csdn.net/xsgnzb/article/details/129723103?spm=1001.2014.3001.5502>",
"created_at": "1679529600",
"author": "xueshengguo",
"document_id": "3"
},
"embedding": null,
"score": 0.175134840024
}
]
}
]
}
