課程介紹
LLM 與 OpenAI API
自然語言理解(NLU)與JSON處理
AI Agent 整合與開發
AI工具與開發框架
建立AI Agent
Lab 4(A): create a ChatGPT bot (LINE)
- create a line bot (messaging api) https://developers.line.biz/en/
- get Channel access token and Your UserID
- create a new webapi project
md testbot
cd testbot
dotnet new webapi -controllers
dotnet new --install isRock.Template.LineWebHook
# https://arock.blob.core.windows.net/blogdata202310/LineBotChatGPTWebHookController.cs
dotnet add package linebotsdk
dotnet new linewebhook
code .
#編輯程式碼
dotnet run
final version: git clone https://github.com/isdaviddong/Sample_Net8_LINE_Bot_with_OpenAI.git
run ngrok command(不推薦):
ngrok authtoken 2eeb482nM____your____token____V3Ar4
ngrok http 5000 --host-header="localhost:5000"
or DevTunnel (推薦)
參考說明
https://studyhost.blogspot.com/2023/10/devtunnel-webapi.html
安裝方式:
winget install Microsoft.devtunnel
或
Invoke-WebRequest -Uri https://aka.ms/TunnelsCliDownload/win-x64 -OutFile devtunnel.exe
.\devtunnel.exe -h
安裝完成之後,請先關掉terminal再開,接著透過底下網址登入:
devtunnel user login
在命令列執行上面這個指令會開啟瀏覽器,請用你的 MS Account 或 GitHub 帳號登入。登入完成後,你就可以透過底下這樣的指令,把 localhost 特定的 port 公開到網際網路上(例如底下的port是5000):
devtunnel host -p 5000 --allow-anonymous
程式碼修改:
- AdminUserId

- ChannelAccessToken
- Programs.cs ==> //app.UseHttpsRedirection();

- ChatGPT.CallAzureOpenAIChatAPI() 改為 ChatGPT.CallOpenAIChatAPI()

sample code: https://gist.github.com/isdaviddong/5c630c9a97720962fc52ef766dbb7530#file-openailinebot-cs
//基本的 OpenAI ChatGPT WebHook
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
namespace isRock.Template
{
public class LineBotChatGPTWebHookController : isRock.LineBot.LineWebHookControllerBase
{
[Route("api/LineBotChatGPTWebHook")]
[HttpPost]
public IActionResult POST()
{
const string AdminUserId = "_____Admin_User_ID_________"; //👉repleace it with your Admin User Id
try
{
//設定ChannelAccessToken
this.ChannelAccessToken = "____LINE_Channel_Access_Token____"; //👉repleace it with your Channel Access Token
//配合Line Verify
if (ReceivedMessage.events == null || ReceivedMessage.events.Count() <= 0 ||
ReceivedMessage.events.FirstOrDefault().replyToken == "00000000000000000000000000000000") return Ok();
//取得Line Event
var LineEvent = this.ReceivedMessage.events.FirstOrDefault();
var responseMsg = "";
//準備回覆訊息
if (LineEvent.type.ToLower() == "message" && LineEvent.message.type == "text")
{
responseMsg = ChatGPT.getResponseFromGPT(LineEvent.message.text);
}
else if (LineEvent.type.ToLower() == "message")
responseMsg = $"收到 event : {LineEvent.type} type: {LineEvent.message.type} ";
else
responseMsg = $"收到 event : {LineEvent.type} ";
//回覆訊息
this.ReplyMessage(LineEvent.replyToken, responseMsg);
//response OK
return Ok();
}
catch (Exception ex)
{
//回覆訊息
this.PushMessage(AdminUserId, "發生錯誤:\n" + ex.Message);
//response OK
return Ok();
}
}
}
public class ChatGPT
{
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum role
{
assistant, user, system
}
public static string CallOpenAIChatAPI(object requestData)
{
var client = new HttpClient();
// 設定 API 網址
var apiUrl = $"https://api.openai.com/v1/chat/completions";
// 設定 HTTP request headers
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk-sEoH______open_ai_api_key_____qQTZU");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT heade
// 將 requestData 物件序列化成 JSON 字串
string jsonRequestData = Newtonsoft.Json.JsonConvert.SerializeObject(requestData);
// 建立 HTTP request 內容
var content = new StringContent(jsonRequestData, Encoding.UTF8, "application/json");
// 傳送 HTTP POST request
var response = client.PostAsync(apiUrl, content).Result;
// 取得 HTTP response 內容
var responseContent = response.Content.ReadAsStringAsync().Result;
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responseContent);
return obj.choices[0].message.content.Value;
}
public static string getResponseFromGPT(string Message)
{
return ChatGPT.CallOpenAIChatAPI(
new
{
model = "gpt-3.5-turbo",
messages = new[]
{
new {
role = ChatGPT.role.system ,
content = @"
假設你是一個專業客戶服務人員,對於客戶非常有禮貌、也能夠安撫客戶的抱怨情緒。
請檢視底下的客戶訊息,以最親切有禮的方式回應。
但回應時,請注意以下幾點:
* 不要說 '感謝你的來信' 之類的話,因為客戶是從對談視窗輸入訊息的,不是寫信來的
* 不能過度承諾
* 要同理客戶的情緒
* 要能夠盡量解決客戶的問題
* 不要以回覆信件的格式書寫,請直接提供對談機器人可以直接給客戶的回覆
----------------------
"
},
new {
role = ChatGPT.role.user,
content = Message
},
}
});
}
}
}
如果出現 307 error, 請在程式碼中槓掉 //app.UseHttpsRedirection();
如果 build error , 請在程式碼中補上 using System.Net.Http;
final version (.net 8): git clone https://github.com/isdaviddong/Sample_Net8_LINE_Bot_with_OpenAI.git
Lab 4(B): create a ChatGPT bot (MS Bot)
download VS2022
https://visualstudio.microsoft.com/zh-hant/vs/older-downloads/
更新 VS 2022:
更新工具

更新VS2022:

修改安裝

增加套件

增加SDK

Create a Echo-Bot and Show Attachment
- 更新VS install
- 更新VS 2022
- 🎁安裝 emulator https://github.com/microsoft/BotFramework-Emulator/releases/download/v4.11.0/BotFramework-Emulator-4.11.0-windows-setup.exe
- 🎁安裝 bot Templates https://marketplace.visualstudio.com/items?itemName=BotBuilder.botbuilderv4
- 👉建立echo bot

- ~~修改project file 從 6.0 改為 5.0 ~~(如果環境於vs2019)

- 執行
- 開啟 emulator
- 與bot對話
- 在VS2022中,設定中斷點

- 觀察中斷點中,用戶傳來訊息
- 修改程式碼,當用戶上傳檔案時,bot顯示檔案名稱…
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var replyText = $"Echo: {turnContext.Activity.Text}";
if (turnContext.Activity.Attachments!=null && turnContext.Activity.Attachments.Count > 0)
{
replyText = $"you've upload a file : {turnContext.Activity.Attachments[0].Name}";
}
await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
}
- 使用emulator測試 <=== 截圖

整合 OpenAI
- 加入 ChatGPT.cs 類別
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
namespace EchoBot1.Bots
{
public class ChatGPT
{
const string AzureOpenAIEndpoint = "https://____________.openai.azure.com"; //👉replace it with your Azure OpenAI Endpoint
const string AzureOpenAIModelName = "________"; //👉repleace it with your Azure OpenAI Model Name
const string AzureOpenAIToken = "_________________"; //👉repleace it with your Azure OpenAI Token
const string AzureOpenAIVersion = "2023-03-15-preview"; //👉replace it with your Azure OpenAI Model Version
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum role
{
assistant, user, system
}
public static string CallAzureOpenAIChatAPI(
string endpoint, string modelName, string apiKey, string apiVersion, object requestData)
{
var client = new HttpClient();
// 設定 API 網址
var apiUrl = $"{endpoint}/openai/deployments/{modelName}/chat/completions?api-version={apiVersion}";
// 設定 HTTP request headers
client.DefaultRequestHeaders.Add("api-key", apiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT heade
// 將 requestData 物件序列化成 JSON 字串
string jsonRequestData = Newtonsoft.Json.JsonConvert.SerializeObject(requestData);
// 建立 HTTP request 內容
var content = new StringContent(jsonRequestData, Encoding.UTF8, "application/json");
// 傳送 HTTP POST request
var response = client.PostAsync(apiUrl, content).Result;
// 取得 HTTP response 內容
var responseContent = response.Content.ReadAsStringAsync().Result;
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responseContent);
return obj.choices[0].message.content.Value;
}
public static string CallOpenAIChatAPI(string apiKey, object requestData)
{
var client = new HttpClient();
// 設定 API 網址
var apiUrl = $"https://api.openai.com/v1/chat/completions";
// 設定 HTTP request headers
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); //👉OpenAI key
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
// 將 requestData 物件序列化成 JSON 字串
string jsonRequestData = Newtonsoft.Json.JsonConvert.SerializeObject(requestData);
// 建立 HTTP request 內容
var content = new StringContent(jsonRequestData, Encoding.UTF8, "application/json");
// 傳送 HTTP POST request
var response = client.PostAsync(apiUrl, content).Result;
// 取得 HTTP response 內容
var responseContent = response.Content.ReadAsStringAsync().Result;
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responseContent);
return obj.choices[0].message.content.Value;
}
public static string getResponseFromGPT(string Message)
{
return ChatGPT.CallOpenAIChatAPI(
"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",
//ref: https://learn.microsoft.com/en-us/azure/cognitive-services/openai/reference#chat-completions
new
{
model = "gpt-3.5-turbo",
messages = new[]
{
new {
role = ChatGPT.role.system ,
content = @"
假設你是一個專業的客戶服務人員,對於客戶非常有禮貌、也能夠安撫客戶的抱怨情緒、
盡量讓客戶感到被尊重、節盡可能的回覆客戶的疑問。
請檢視底下的客戶訊息,以最親切有禮的方式回應。
但回應時,請注意以下幾點:
* 不要說 '感謝你的來信' 之類的話,因為客戶是從對談視窗輸入訊息的,不是寫信來的
* 不能過度承諾
* 要同理客戶的情緒
* 要能夠盡量解決客戶的問題
* 不要以回覆信件的格式書寫,請直接提供對談機器人可以直接給客戶的回覆
----------------------
"
},
new {
role = ChatGPT.role.user,
content = Message
},
}
});
}
}
}
- 修改主程式:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.18.1
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace EchoBot1.Bots
{
public class EchoBot : ActivityHandler
{
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var ret = ChatGPT.getResponseFromGPT(turnContext.Activity.Text);
//var replyText = $"Echo: {turnContext.Activity.Text}";
await turnContext.SendActivityAsync(MessageFactory.Text(ret, ret), cancellationToken);
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var welcomeText = "Hello and welcome!";
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
}
使用 Bot Framework 與 Azure Bot 建立 Teams Bot https://studyhost.blogspot.com/2024/12/bot-framework-azure-bot-teams-bot-2024.html
Lab 5 : 加入企業資料(包含菜單的點餐機器人)
- 使用上一個Lab做出的 Chat bot
- 修改prompt
你是餐飲店的客服機器人,會回答客戶詢問的餐點問題,並且推薦讓店裡獲利最高的產品。你也能處裡客戶的抱怨和申訴,安撫客戶情緒。
目前店裡面所販賣的產品有底下這些:
大亨堡,45元
麥香雞,36元
蛋餅,27元
可麗餅,50元
飯糰,45元
而飲料有底下這些:
可樂,55元
紅茶,35元
奶茶,45元
店裡沒有套餐,只能把餐點和飲料搭配銷售,這樣搭配沒有折扣。此外,跟客人推薦餐點時,必須是上述的清單中的餐點。
目前唯一的優惠是周日早上買一送一,只限單點大亨堡才有這個優惠。
----------------------
- 加入history
user: 請告訴我最便宜的餐點價格。
assistant: 你可以點選蛋餅,只需要27元
user: 有什麼好吃的餐點嗎?
assistant: 這個問題很主觀,不同的人有不同的口味喜好,以下是我個人的推薦:
1. 大亨堡:這是我們餐廳的招牌餐點,肉質鮮美,搭配特製的醬料非常美味
2. 麥香雞:這是一個很受歡迎的餐點,雞肉鮮嫩,外皮酥脆,非常適合當作午餐或晚餐
3. 可麗餅:這是一個很受歡迎的早餐餐點,口感軟嫩,搭配楓糖漿和奶油非常美味
user: 我想要吃飽一點,你可以推薦我豐盛的套餐嗎?
assistant: 很抱歉,我們店裡沒有套餐,只能把餐點和飲料搭配銷售。
但是,如果你想吃得飽一點,我建議你可以考慮以下搭配:
1. 大亨堡 + 可樂:肉質鮮美,搭配可樂口感更佳。
2. 麥香雞 + 紅茶:雞肉鮮嫩,搭配紅茶口感更清爽。
3. 飯糰 + 奶茶:飯糰營養豐富,搭配奶茶口感更香濃。
希望這些搭配可以滿足你的食慾需求!
user: 這些搭配套餐分別是多少錢?
assistant: 很抱歉,我剛才的回答有點不清楚,我重新提供給你更詳細的價格資訊:
1. 大亨堡 + 可樂 = 100元 (大亨堡 45元 + 可樂 55元)
2. 麥香雞 + 紅茶 = 71元 (麥香雞 36元 + 紅茶 35元)
3. 飯糰 + 奶茶 = 90元 (飯糰 45元 + 奶茶 45元)
希望這些資訊可以幫助到你!
reference code: https://gist.github.com/isdaviddong/511d74e5d8620b990460a2af003442f0
- 如果今天想吃素? 該怎麼辦?
- 如果換成 GPT-4 表現會更好嗎? https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4
- 如果採用 gpt-3.5-turbo 模型,如何調整 system prompt (or hostory) 讓用戶花100元買大亨堡時候,永遠得到4個...