├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── dialogflow-ES-agent-OpenAi-GPT.zip ├── function-OpenAI-GPT.zip ├── img ├── LINE-OA-demo.png └── System architecture.png ├── index.js └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hank199599/openGPT-with-Dialogflow/1dcfe63273cf2c7dea8a7afd04e86cb0b983390f/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .env 4 | temp/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 YU SHAO-HUNG (游紹宏) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openGPT-with-Dialogflow 2 | a project to intergrade OpenAI ChatGPT API to Dialogflow ES 3 | 4 | ### 系統架構圖 5 | ![img/System%20architecture.png](https://github.com/hank199599/openGPT-with-Dialogflow/raw/main/img/System%20architecture.png) 6 | 7 | # 如何建立自己的版本? 8 | 9 | ## STEP 1 :申請OpenAI 的API金鑰 10 | 1. 前往 [OpenAI的API keys頁面](https://beta.openai.com/account/api-keys)並登入帳號 11 | 2. 點擊 `Create new secret key` 12 | > 請記住這組金鑰,會在稍後的流程中使用到 13 | 14 | 15 | ## STEP 2 :修改本專案的參數 16 | 1. 解壓縮本專案的`function-OpenAI-GPT.zip` 17 | 2. 打開`.env`,你會看到以下的內容 18 | ``` 19 | OPENAI_API_KEY= 20 | ``` 21 | 3. 把方才申請到的參數填入,如下所示 22 | ``` 23 | OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 24 | ``` 25 | 4. 儲存更改後的檔案,並以ZIP形式包裝。重新命名為`function.zip` 26 | 27 | ## STEP 3 :部署到GCP 28 | 1. 建立 [Google Cloud Platform](https://cloud.google.com/free?hl=zh-tw)專案 29 | 2. 建立專案 e.g. `OpenAi-GPT-Linebot` 30 | 3. 於該專案內,前往[Cloud Function](https://console.cloud.google.com/functions/list?authuser=0&hl=zh)頁面 31 | 4. 建立新的Cloud Function 32 | 1. 點擊『創建函式』 33 | 2. 基本 > 環境 > 選擇『`第1代`』 34 | 3. 觸發條件 > 驗證 > 選擇「`允許未經驗證的叫用`」 35 | 1. 選擇『下一步』 36 | 2. `進入點` 更改為 `chatGPT` 37 | 3. `原始碼` 更改為 `上傳ZIP檔`,並上傳剛剛建立的`function.zip` 38 | 5. 等待部署完成後,點擊畫面中的「觸發條件」。複製在畫面中的「`觸發網址`」 39 | e.g. `https://us-central1-.cloudfunctions.net/chatGPT` 40 | 41 | ## STEP 4 :建立 LINE官方賬號 42 | 1. 前往[LINE開發者網頁](https://developers.line.biz/en/) 43 | 2. 登入或建立帳號 44 | 3. 點選上方橫幅的「Products」 45 | 4. 選擇頁面中的「Messaging API」,按新頁面中的『Start Now』。 46 | 依照網頁提示填入以下資訊: 47 | * Provider Name (如已建立可跳過該步驟直接選取) 48 | * Channel name 49 | * Channel description 50 | * Category 51 | * Subcategory 52 | * Email address 53 | 54 | 閱讀 [LINE Official Account Terms of Use](https://terms2.line.me/official_account_terms_tw?lang=zh-Hant) 以及 [LINE Official Account API Terms of Use](https://terms2.line.me/official_account_api_terms_tw?lang=zh-Hant),並同意LINE官方帳號條款後。 55 | 點擊「`Create`」建立你的官方帳號 56 | 5. 至此,建立`LINE官方賬號`作業已完成! 57 | 58 | ## STEP 5 :建立 DialogFlow ES 專案 59 | 1. 前往 [DialogFlow ES](https://dialogflow.cloud.google.com/)頁面,點擊畫面中的「`CREATE AGENT`」建立新的聊天機器人專案, 60 | 2. 選擇先前在[Google Cloud Platform](https://cloud.google.com/free?hl=zh-tw)建立的專案 e.g. `OpenAi-GPT-Linebot` 61 | 3. `DEFAULT LANGUAGE` 選擇 `Chinese (Traditional) — zh-tw` 62 | 4. 專案建立完成後,依序點擊: 左方選單「齒輪」 > 「Export and Import」 > 「IMPORT FROM ZIP」 63 | ![pic1](https://github.com/hank199599/Assistant_demo_devfest/raw/master/pic/description-1.png) 64 | 5. 選擇要上傳的檔案`dialogflow-ES-agent-OpenAi-GPT.zip`,並點擊「IMPORT」即可 65 | ![pic2](https://github.com/hank199599/Assistant_demo_devfest/raw/master/pic/description-2.png) 66 | 6. 串接 Fulfillment 67 | 1. 點選畫面左側的「Fulfillment」 68 | 2. `Webhook`右側的開關打開 69 | 3. 在`URL*`貼上在[ Cloud Function](https://console.cloud.google.com/functions/list?authuser=0&hl=zh)頁面所得到的網址 e.g. `https://us-central1-.cloudfunctions.net/chatGPT` 70 | 4. 按下下方的`DONE`即可 71 | 72 | 73 | ## STEP 6 :串接 DialogFlow ES 與 LINE官方賬號 74 | 1. 一樣在 [DialogFlow ES](https://dialogflow.cloud.google.com/)頁面進行操作 75 | 2. 點選畫面左側的「Integrations」 76 | 3. 滾輪往下尋找「LINE」,並點擊展開浮動面板(以下稱`LINE串接頁面面板`) 77 | 4. 在[LINE開發者網頁](https://developers.line.biz/en/)方才建立的官方帳號頁面中,尋找以下參數。並複製到[DialogFlow ES](https://dialogflow.cloud.google.com/)之中的`LINE串接頁面面板`進行串接: 78 | * `Channel ID` : 位於 Basic settings > Basic information > Channel ID 79 | * `Channel Secret` : 位於 Basic settings > Basic information > Channel secret 80 | * `Channel Access Token` : 位於 Messaging API > Channel access token > Channel access token (long-lived) (需點擊右方黑色按鈕『Issue』生成一組) 81 | 5. 點擊面板下方的「START」使 DialogFlow 開始接收來自 LINE 的訊息並給予回應 82 | 6. 複製`LINE串接頁面面板`之中提供的 `Webhook URL`,接著切換頁面到[LINE開發者網頁](https://developers.line.biz/en/)方才建立的官方帳號頁面中的指定位置。 83 | * Messaging API > Webhook settings > Webhook URL,黏貼剛才得到的`Webhook URL` 84 | * Messaging API > Webhook settings > Webhook URL,開啟按鈕使它呈現綠色 85 | 6. 前往LINE官方帳號設定,調整官方帳號的回應方式 86 | * 傳送門位置:Basic settings > Basic information > 下方的 `You can change your app name and icon in LINE Official Account Manager.` 的 [LINE Official Account Manager]()按鈕 87 | * 進入頁面後,點擊左方面板的「回應設定」 88 | * Webhook:使按鈕開啟(呈現綠色) 89 | * 自動回應訊息:使按鈕關閉(呈現灰色) 90 | ## STEP 7: 開始對話! 91 | 使用手機掃描 Messaging API > QR code 的QR圖碼,現在即可進行對話啦! 92 | ![LINE-OA-demo.png](https://github.com/hank199599/openGPT-with-Dialogflow/raw/main/img/LINE-OA-demo.png) 93 | 94 | # Q&A 95 | Q: 為何GPT CHAT給我的回應都很短? 96 | A: 由於 [Dialogflow ES 在Fulfillment回應的時間有5秒的上限](https://cloud.google.com/dialogflow/es/docs/fulfillment-webhook#webhook_response),導致我們必須針對此情形作妥協。讓OPENAI GPT在遇到句號「`。`」時就會強制中斷生成回應並直接回傳。避免回應等待時間超過5秒而掛掉的問題。 97 | 98 | Q: 有辦法改善這個問題嗎? 99 | A: 可以用 [DialogFlow CX (須額外付費)](https://dialogflow.cloud.google.com/cx/projects) 客製化 Fulfillment的回應時間上限到30秒,來解決這個問題。 100 | 101 | Q: 說好的圖文並茂版在哪裡? 102 | A: 這部分正在製作中,還請見諒 >< 103 | 104 | # Reference 105 | * [beta.openai.com](https://beta.openai.com/docs/api-reference/completions/create?lang=node.js) 106 | -------------------------------------------------------------------------------- /dialogflow-ES-agent-OpenAi-GPT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hank199599/openGPT-with-Dialogflow/1dcfe63273cf2c7dea8a7afd04e86cb0b983390f/dialogflow-ES-agent-OpenAi-GPT.zip -------------------------------------------------------------------------------- /function-OpenAI-GPT.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hank199599/openGPT-with-Dialogflow/1dcfe63273cf2c7dea8a7afd04e86cb0b983390f/function-OpenAI-GPT.zip -------------------------------------------------------------------------------- /img/LINE-OA-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hank199599/openGPT-with-Dialogflow/1dcfe63273cf2c7dea8a7afd04e86cb0b983390f/img/LINE-OA-demo.png -------------------------------------------------------------------------------- /img/System architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hank199599/openGPT-with-Dialogflow/1dcfe63273cf2c7dea8a7afd04e86cb0b983390f/img/System architecture.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functions = require("firebase-functions"); 4 | const {WebhookClient} = require('dialogflow-fulfillment'); 5 | require('dotenv').config() 6 | const {Card, Suggestion,Payload} = require('dialogflow-fulfillment'); 7 | const { Configuration, OpenAIApi } = require("openai"); 8 | 9 | require('dotenv').config() 10 | 11 | //console.log(process.env.OPENAI_API_KEY); //chris 12 | 13 | const configuration = new Configuration({ 14 | apiKey: process.env.OPENAI_API_KEY, 15 | }); 16 | 17 | const openai = new OpenAIApi(configuration); 18 | const suggestionList= ["你是誰創造的?","你會反抗人類嗎?","介紹GDG","介紹LINE這家公司","用Python倒數10秒鐘","你聽過賈伯斯嗎?","介紹台北的特色","簡單介紹你是誰","你認識馬斯克嗎?","你會取代人類嗎?","一句話介紹台灣","介紹Google"] 19 | 20 | 21 | exports.chatGPT = functions.https.onRequest((request, response) => { 22 | 23 | const agent = new WebhookClient({ request, response }); 24 | 25 | console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers)); 26 | console.log('Dialogflow Request body: ' + JSON.stringify(request.body)); 27 | /** 28 | * 29 | * @param {*} agent 30 | * @see "https://beta.openai.com/docs/api-reference/completions/create?lang=node.js" 31 | * @returns string 32 | */ 33 | function welcome(agent) { 34 | return new Promise( 35 | 36 | function(resolve, reject) { 37 | openai.createCompletion({ 38 | model: "text-davinci-003", 39 | // model:'text-curie-001', 40 | // APP_DEBUG:true, 41 | prompt: request.body.queryResult.queryText, 42 | max_tokens: 176, 43 | temperature: 0, // Higher values means the model will take more risks. 44 | top_p: 1, 45 | frequency_penalty: 0, 46 | presence_penalty: 0.6, 47 | stop: ["4.","。"], 48 | }, 49 | { 50 | timeout: 4999, // dialogflow required the limit is in 5ns 51 | //maxContentLength: 200, 52 | }).then((response)=>{ 53 | let returnedMessage = response.data.choices[0].text.trim() 54 | 55 | console.log(returnedMessage) 56 | 57 | if (returnedMessage.indexOf('\n\n')!==-1){ 58 | returnedMessage = returnedMessage.split('\n\n')[1] 59 | } 60 | 61 | resolve(returnedMessage) 62 | }).catch((error)=>{ 63 | reject(error) 64 | }); 65 | }).then( (returnedMessage )=>{ 66 | console.log("input message:"+ request.body.queryResult.queryText) 67 | console.log("return message:"+returnedMessage) 68 | 69 | const shuffled = suggestionList.sort(() => 0.5 - Math.random()).slice(0, 3); 70 | 71 | agent.add(returnedMessage); 72 | 73 | for(let i=0;i{ 79 | console.log(err) 80 | agent.add("發生未預期的錯誤"); 81 | agent.add(`${err}`); 82 | 83 | // return Promise.resolve(); // Don't reject again, or it might not send the reply 84 | }) 85 | } 86 | // Run the proper function handler based on the matched Dialogflow intent name 87 | let intentMap = new Map(); 88 | intentMap.set('Input Intent', welcome); 89 | intentMap.set('Input Intent-1', welcome); 90 | intentMap.set('Input Intent-2', welcome); 91 | intentMap.set('Input Intent-3', welcome); 92 | 93 | agent.handleRequest(intentMap); 94 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "16" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^10.0.2", 17 | "firebase-functions": "^3.18.0", 18 | "openai":"^3.1.0", 19 | "dialogflow-fulfillment":"^0.6.1", 20 | "dotenv":"^16.0.3" 21 | }, 22 | "devDependencies": { 23 | "firebase-functions-test": "^0.2.0" 24 | }, 25 | "private": true 26 | } 27 | --------------------------------------------------------------------------------