ChatGPT x LINE Bot指南:打造智能對話助手

TomatoSoup
拯救世界的一千零一種方法
23 min readMar 30, 2023

從基礎設置到Prompt策略,一步步整合ChatGPT與LINE Bot

久違的開新坑,緣起是想幫公司客服弄個小幫手之類的東西,最後就變成了標題的LINE Bot + chatGPT的組合了,順勢被要求應該被寫成文章記錄一下,於是有了這篇文章XD

這篇適合誰?

這篇預期是給已有基本Node.js開發環境的開發者參考用,並有GitHubrenderngrok帳號供後續的開發與部署,若有錯誤還煩請各位大大們指教(合掌

大綱

- 註冊LINE開發者帳號
- 建立LINE Bot用Channel
- 初始化express專案
- LINE Bot基本的應答功能實作
- 使用ngrok進行本地端LINE Bot開發
- 串接ChatCompletion api
- 優化prompt
- 其他prompt範例
- 部署於render

▍註冊LINE開發者帳號

前往LINE Developers官方網站並點選右上角的「Log In」。

申請一般的Line應該不用特別說了,登入就是了XD

登入後,點選「建立LINE商用ID」,填寫必要的個人資訊,並同意相關條款。點選「Create my account」,就正式成為LINE開發者啦!

▍建立LINE Bot用Channel

成功以LINE開發者帳號進入後台後,就先點選「Create」建立一個新的Provider,用於管理接下來要開發的Bot。

輸入Provider名稱(例如:測試初號機),點選「Create」。接著就會進入Provider頁面

接著我們要建立新的Channel作為line bot與LINE平台的通道,這次由於是要用來製作LINE bot,需使用Messaging API,所以點選中間的「 Create a Messaging API channel」。

接著把所有欄位的必填填寫完成,按下Create,就會提醒你接下去就會創建同名的官方帳號了,務必確認名稱不要撞到別人或是取錯啊XD

同意資訊也給他按下去…

到此就完成基本的Channel設定了,之後度開發過程中,會再回到這裡取得Channel secret和Channel Access Token來進行串接,就先擺著囉!

▍初始化express專案

萬事起頭難,先來啟個express專案:

mkdir line-bot-with-gpt
cd line-bot-with-gpt
pnpm init
git init
code .

先修改一下`package.json`,滿足下列幾點:

  • 讓本專案可以使用ES modules
  • 讓進入點改為`app.js`
  • 新增dev script,使用nodemon來啟動`app.js`
{
"name": "line-bot-with-gpt",
"version": "1.0.0",
"description": "",
- "main": "index.js",
+ "main": "app.js",
+ "type": "module",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "dev": "nodemon app.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}

安裝express套件:

pnpm i express

新建`app.js`,並參考express範例引入套件,做基礎伺服器設置:

import express from 'express'

const app = express()

app.get('/', (req, res) => {
res.send('Hello World!')
})

const port = process.env.PORT || 3000

app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}/`)
})

啟動伺服器看看有沒有問題:

pnpm run dev
順利啟動歐!

▍LINE Bot基本的應答功能實作

先按照官方文件範例新增.env檔案並增加兩個環境變數:

CHANNEL_ACCESS_TOKEN=你的CHANNEL_ACCESS_TOKEN
CHANNEL_SECRET=你的CHANNEL_SECRET

回頭前往LINE developer尋找兩個需要引入的環境變數:

CHANNEL_ACCESS_TOKEN:Basic settings > Channel secret
CHANNEL_SECRET:Channel access token > Channel access token > 按下issue生成

安裝dotenv來讀取環境變數:

pnpm i dotenv

引入dotenv及啟動:

  import express from 'express'
+ import dotenv from 'dotenv'
+ dotenv.config()

const app = express()

安裝line-bot-sdk-nodejs

pnpm i @line/bot-sdk

參考官方文件範例引入line sdk並撰寫基本路由:

  import express from 'express'
import dotenv from 'dotenv'
+ import line from '@line/bot-sdk'

dotenv.config()

const app = express()

+ const lineConfig = {
+ channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
+ channelSecret: process.env.CHANNEL_SECRET,
+ }

+ app.post('/webhook', line.middleware(lineConfig), (req, res) => {
+ Promise
+ .all(req.body.events.map(handleEvent))
+ .then((result) => res.json(result))
+ })

- app.get('/', (req, res) => {
- res.send('Hello World!')
- })

+ const client = new line.Client(lineConfig)
+ function handleEvent(event) {
+ if (event.type !== 'message' || event.message.type !== 'text') {
+ return Promise.resolve(null)
+ }
+
+ return client.replyMessage(event.replyToken, {
+ type: 'text',
+ text: event.message.text
+ })
+ }

const port = process.env.PORT || 3000

app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}/`)
})

可以看到其中進行了幾項操作

  • 宣告了lineConfig引入兩個環境變數。
  • 設置了一個POST路由`/webhook`,並會經過line middleware進行驗證,最後將所有的events交由handleEvent處理。
  • handleEvent會驗證event.type,若非message且message.type非text就回傳null,否則就拿著event.replyToken去回傳一樣的event.message.text給使用者。

到此就是基本的LINE Bot內容了,接著就是要實際串接看看了!

▍使用ngrok進行本地端LINE Bot開發

因為還需要在本機進行開發,就先不部署到render上,但因為串接需要https協定,所以要使用ngrok,讓http://localhost:3000/可以被外網找到。

啟動ngrok,將3000 port曝露出來:

ngrok http 3000

複製Forwarding的網址(https://xxxxxxx.jp.ngrok.io),並貼到開發者後台的Webhook settings > Webhook URL上,記得要添加POST的路徑!

按照上面的範例,最後會是https://xxxxxxx.jp.ngrok.io/webhook

儲存後按下Verify測試設置是否有效:

沒意外的話會出現Success,若是404則可能是後面的POST路徑有誤,而500則有可能是環境變數設置錯誤,讓middleware驗證失敗。

接著記得把下面的use webhook開啟,才會真的生效:

另外為了不讓測試受到干擾,也順道把下面的幾個官方帳號功能停用,點擊Edit後會另開分頁供設定:

留下Webhook即可

接著就掃描後台提供的QR code,把LINE Bot加入自己的LINE吧!

對話起來和我們預期的一樣,就是個鸚鵡學舌的狀態,不過至少路打通了!

接著就是重點的串接GPT啦!

▍串接ChatCompletion api

前往openai官網後台生成自己的api key

點下去記得複製出來,之後就看不到啦XD

把api key加到`.env`中:

  CHANNEL_ACCESS_TOKEN=xxxxx
CHANNEL_SECRET=xxxxx
+ OPENAI_API_KEY=xxxxx

安裝OpenAI Node.js Library

pnpm install openai 

接著串接的對象是Create chat completion api,Model則是選用基於GPT-3.5的gpt-3.5-turbo,畢竟就官方說法其費用更加低廉,且對chat功能有最佳化,之前實際測試起來也確實如此。

參考官方文件範例來串接Create chat completion

  import express from 'express'
import dotenv from 'dotenv'
import line from '@line/bot-sdk'
+ import { Configuration, OpenAIApi } from 'openai'

dotenv.config()

const app = express()

const lineConfig = {
channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
channelSecret: process.env.CHANNEL_SECRET,
}

+ const openaiConfig = new Configuration({
+ apiKey: process.env.OPENAI_API_KEY,
+ })
+ const openai = new OpenAIApi(openaiConfig);

app.post('/webhook', line.middleware(lineConfig), (req, res) => {
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result))
})

const client = new line.Client(lineConfig)
- function handleEvent(event) {
+ async function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null)
}

+ try {
+ const completion = await openai.createChatCompletion({
+ model: "gpt-3.5-turbo",
+ messages: [{ role: "user", content: "Hello world" }],
+ });
+
+ console.log(completion.data.choices[0].message)
+ } catch (error) {
+ console.error(error)
+ }
+
return client.replyMessage(event.replyToken, {
type: 'text',
text: event.message.text
})
}

const port = process.env.PORT || 3000

app.listen(port, () => {
console.log(`Example app listening on http://localhost:${port}/`)
})

一開始引入兩個function:Configuration和OpenAIApi,主要就是將api key放入後new一個openai實體出來。

接著改寫一下原有的handleEvent,因為範例中有使用await語法,所以將其加上async,並用try catch包裹官方文件呼叫createChatCompletion的範例。

範例中的payload是:

{
model: "gpt-3.5-turbo",
messages: [{role: "user", content: "Hello world"}],
}

其中‘‘gpt-3.5-turbo’’就是前面提到要使用的模型,而messages是個Array,其中的role為‘‘user’’,並有content:‘‘Hello world’’。最後則是把回傳內容直接在伺服器端印出來。

就向機器人發個訊息,戳看看會得到什麼吧!

按照上面的撰寫方式,只要機器人鸚鵡學舌回來時,就表示GPT的回應也來了!

串上啦!有成功讓AI回應 “Hello world!”的內容了!

接著修改一下handleEvent的內容,讓使用者的訊息真的有傳入GPT中,並且將GPT的回應也使用client.replyMessage回應給使用者。

  async function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null)
}

try {
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
- messages: [{ role: "user", content: "Hello world" }],
+ messages: [{ role: "user", content: event.message.text }]
})

- console.log(completion.data.choices[0].message)
+ if (completion.data.choices[0]?.message?.content) {
+ return client.replyMessage(event.replyToken, {
+ type: 'text',
+ text: completion.data.choices[0].message.content
+ })
+ }
} catch (error) {
console.error(error)
}

- return client.replyMessage(event.replyToken, {
- type: 'text',
- text: event.message.text
- })
}

整體很簡單,就是把event.message.text塞進user content中,並且把原來鸚鵡學舌的內容直接改成AI的回應內容。

\成功在LINE Bot上取得AI的回應啦/

接下來就是修改prompt的內容,讓AI符合指定情境並上線囉!

▍優化prompt

觀察先前的回應內容中可以發現,AI回覆的role為‘assistant’,而在官方文件中也有提到,message role是用來標記對話中不同參與者身份的關鍵參數,該身份共有三種:

  • system:可以視為系統層級的prompt,不會因為後續的內容而變更的指示(當然部分模型有較不注重system的傾向),通常類似於chatGPT中的第一句話,用來為整個對話設定語境和指導 AI 的行為。
  • user:可以視為真實用戶的對話內容,AI會將user的內容視作實際的提問和對話,若事先給予定義好的用戶對話內容,也可以視為給AI參考用的上下文。
  • assistant:可以視為AI的回應內容,這裡的內容會作為接下來model回應的參考指標,讓AI了解之後的回應是否有特定格式或語氣等。

知道這些規則後,就可以嘗試發想要請AI擔任的角色和情境了。

以星座專家為例

若想要AI作為星座專家,我們可以將message寫成:

{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "你是一位星座專家,請你按照使用者告知的星座來進行本日星座運勢分析。若使用者提供的內容不是十二星座,請拒絕回答。分析內容包含:事業、感情、財運、健康,請描述分析內容並以星數表示好壞,滿分五顆星,最好五顆,最壞一顆星。"
},
{
role: "assistant",
content: "你好,讓我來看看你的星座運勢,請告訴我你的星座為何?"
},
{ role: "user", content: event.message.text }
]
}

用system身份將AI設定為星座專家,並要求AI回覆事業、感情、財運、健康等面向的運勢,並且加上不回答的條件。

用assistant身份讓AI知道上文已經詢問過使用者其星座為何。

下文便是user在LINE中實際傳入的星座了!

馬上試看看…

左圖:成功扮演星座大師回答運勢 |右圖:因為有system限制,所以不會回答非星座以外的問題。

接著我們可以更加擴充system的回答範本,讓AI了解我們預期的回答格式為何:

{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "你是一位星座專家,請你按照使用者告知的星座來進行本日星座運勢分析。若使用者提供的內容不是十二星座,請拒絕回答。分析內容包含:事業、感情、財運、健康,請條列各項分析內容並以星數表示好壞,滿分五顆星(★★★★★),最低一顆星(★☆☆☆☆)。"
},
{
role: "system",
content: "回答範本為:以下是<星座>的今日運勢:\n 事業運勢為 <星數> \n <解析> \n 感情運勢為 <星數> \n <解析> 財運運勢為 <星數> \n <解析> 健康運勢為 <星數> \n <解析> \n 整體運勢為<星數> \n <解析>。"
},
{
role: "assistant",
content: "你好,讓我來看看你的星座運勢,請告訴我你的星座為何?"
},
{ role: "user", content: event.message.text }
]
}

這裡追加了一段system級的回答範本,並用<變數>告知可以填入特定變數,在搭配換行符\n做些排版上的變化,最後呈現的結果會是這樣:

看起來確實有按照範例的格式來生成呢!

當然,越詳盡的message內容可以帶來更接近預期的回答格式,但是所消耗的token也是隨之上升,也就意味著每次呼叫的金額成本更高,在還沒用完免費額度之前多多嘗試吧!

其他的常用參數:

  • temperature:一個0 ~ 2的數值,數字越小可以讓回答更趨於保守,反之則是更自由發揮,如果想要更加符合上下文的回答範例,便可以考慮將其調低。
  • max_tokens:AI回應的tokens上限,開越小越省錢,但是回應內容有可能被截斷,可以嘗試在system中要求其回應應低於多少字來簡化長度。

其他prompt範例

英文老師

{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "你是一名英文文法老師,負責校驗學生的英文文法,請你檢查學生給予的文句是否有文法或拼寫錯誤,並提供修改後的結果與為何這樣修改。"
},
{
role: "system",
content: "輸出範本:修改後的文句為: \n <修改後的文句> \n 修改理由為: \n <修改理由>"
},
{
role: "assistant",
content: "嗨,請你提供一段英文文句,我來幫你檢查文法或拼寫錯誤。"
},
{ role: "user", content: event.message.text }
]
}

文章摘要

{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "你是一名文章摘要翻譯人員,請你根據使用者提供的文章,條列出5項內的文章重點與短文總結,並翻譯成台灣繁體中文。"
},
{
role: "system",
content: "輸出範本:- <文章重點> \n - <文章重點> \n <短文總結>"
},
{
role: "assistant",
content: "請提供原文。"
},
{ role: "user", content: event.message.text }
]
}

▍部署於render

先將專案推到githib上,待等等部署用。

註冊並進入render dashboard,建立一個新的Web service。

接著會看到連接專案的介面:

上方為連接github專案的介面,下面則是直接搜尋連接公開專案,如圖中所述,連接github專案可以很簡單的有自動部署,所以這裡就簡單紀錄連接github的部分。

點擊“Configure account”,會跳轉到Github要求授權,這裡可以選擇要讓render可以存取所有專案或是部分專案。

這裡只選擇了這次的LINE-Bot專案,按下Install後,會再度回到render dashboard並看到專案出現在清單中了。

點擊Connect開始進行部署設定。

Build command記得修改成安裝package的指令,接著展開Advanced填寫環境變數。

填寫三個.env中的環境變數,並打開Auto-deploy。

Create Web Service就按下去吧!

稍等一下就會部署完成了!

往上看就會發現已經有實際的部署位置了,複製起來後前往LINE developer後台,替換掉Webhook settings > Webhook URL原本連往ngrok的網址,並驗證是否成功:

看起來妥妥的

實際回到LINE上面戳看看:

有正確運作了,唯獨就是render免費方案是會休眠的,太久沒戳會要等他啟動就是了,不過真的要隨時使用就不能當免費仔啦XD

以上就是本次LINe-Bot串接chatGPT的實作紀錄,從各項服務的帳號申請、express的架設、串接chatGPT、優化prompt、再到部署上線,說長不長,說短也不短呢!

若文章中間有誤還煩請指教,如果想看repo的話,Github repo在此供參考,喜歡的話也歡迎送我一顆星XD

若本篇文章對您有所幫助,您可以:

幫這篇文章拍個手,讓我感受到您的肯定 👏🏻
點擊下方的「Follow」來追蹤我 🔔

當然也歡迎留言敲碗其他內容歐!🥁

--

--