ใน Codelab นี้คุณจะได้เรียนรู้การสร้าง LINE Chatbot ให้สามารถโต้ตอบได้แบบ Generative AI ผ่านการเชื่อมต่อ Gemini โดยใช้ Cloud Functions for Firebase 2nd Gen กัน
จุดเริ่มต้นของการพัฒนา LINE Chatbot คือคุณจะต้องสร้าง LINE OA(LINE Official Account) และเปิดใช้งาน Messaging API
หลังจากที่เรามี LINE OA เรียบร้อยแล้ว ขั้นตอนนี้จะพาทุกคนไปเพิ่มความสามารถให้ LINE OA ของเรากลายเป็น LINE Chatbot ได้
ขั้นตอนนี้เราจะเข้าไปใช้งาน LINE Developers Console ซึ่งเป็นเว็บไซต์สำหรับการบริหารจัดการ LINE Chatbot(LINE OA ที่เปิดใช้งาน Messaging API แล้ว) ในส่วนของนักพัฒนา
เบื้องหลังของ Chatbot ตัวนี้ เราจะใช้บริการใน Firebase อย่าง Cloud Functions for Firebase ดังนั้นขั้นตอนนี้เราจะมาสร้างโปรเจค Firebase เพื่อใช้งานกัน
เนื่องจาก Cloud Functions for Firebase มีเงื่อนไขว่า หากต้องการไป request ตัว APIs ที่อยู่ภายนอก Google คุณจำเป็นจะต้องใช้ Blaze plan(เราจะต้องไปเรียก Messaging API ของ LINE)
Firebase CLI เป็นเครื่องมือที่จำเป็นสำหรับการ deploy ตัวฟังก์ชันที่เราพัฒนาขึ้น อีกทั้งยังสามารถจำลองการทำงานฟังก์ชัน(Emulate) ภายในเครื่องที่เราพัฒนาอยู่(Locally) ได้
npm install -g firebase-tools
firebase --version
firebase login
mkdir bot
cd bot
firebase init functions
ขั้นตอนนี้เราจะสร้าง Webhook ขึ้นมาเพื่อให้ LINE Chatbot สามารถรับข้อมูลและโต้ตอบกับผู้ใช้งานได้ผ่าน Messaging API
ใน Codelab นี้เราจะใช้ axios มาเป็นตัวช่วยในการสร้าง request ซึ่งเราจะต้องทำการติดตั้ง dependency ตัวนี้ก่อน โดยให้เปิด command line แล้วเข้าไปที่ /functions จากนั้นใช้คำสั่ง
npm install axios --save
ถัดไปเราจะสร้าง Environment Variable โดยให้ไปสร้างไฟล์ .env ใน /functions แล้วให้ไป copy ค่า Channel Access Token ของ Messaging API Channel ที่ issue ไว้ก่อนหน้านี้มาระบุลงไป
CHANNEL_ACCESS_TOKEN=YOUR-CHANNEL-ACCESS-TOKEN
เพื่อโค้ดที่สั้น เป็นระเบียบ เราจะแยกฟังก์ชันสำหรับการเรียกใช้ Messaging API ออกมาเป็น module โดยภายในจะมีฟังก์ชัน
ขั้นตอนนี้ให้สร้างไฟล์ /functions/utils/line.js แล้วให้ copy โค้ดด้านล่างนี้ไปวาง
const axios = require("axios");
const LINE_HEADER = {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.CHANNEL_ACCESS_TOKEN}`
};
const getImageBinary = async (messageId) => {
const originalImage = await axios({
method: "get",
headers: LINE_HEADER,
url: `https://api-data.line.me/v2/bot/message/${messageId}/content`,
responseType: "arraybuffer"
})
return originalImage.data;
}
const reply = (token, payload) => {
return axios({
method: "post",
url: "https://api.line.me/v2/bot/message/reply",
headers: LINE_HEADER,
data: { replyToken: token, messages: payload }
});
};
module.exports = { getImageBinary, reply };
จากนั้นให้เปิดไฟล์ /functions/index.js แล้วให้ copy โค้ดด้านล่างนี้ไปแทนที่โค้ดเดิม เพื่อเตรียมรับ Webhook event ประเภท text และ image จาก LINE และตอบกลับผู้ใช้งาน
// Import dependencies
const {onRequest} = require("firebase-functions/v2/https");
const line = require("./utils/line");
// Create a webhook via HTTP requests
exports.webhook = onRequest(async (req, res) => {
if (req.method === "POST") {
const events = req.body.events;
for (const event of events) {
switch (event.type) {
case "message":
if (event.message.type === "text") {
}
if (event.message.type === "image") {
}
break;
}
}
}
res.send(req.method);
});
แล้วก็ให้เปิด command line ขึ้นมาอีกครั้ง และให้แน่ใจว่าเราอยู่ที่ /functions จากนั้นใช้คำสั่งด้านล่างนี้เพื่อ deploy ตัว webhook ของเราขึ้น production
firebase deploy --only functions
เมื่อ deploy เสร็จเรียบร้อยเราจะเห็น Webhook URL โผล่อยู่ใน command line เลย ก็ให้เรา copy มา หรือกรณีที่ไม่พบก็ให้เข้าไปที่ Firebase console เลือกโปรเจคที่เราสร้างไว้ แล้วเลือกเมนู Build > Functions จะเจอชื่อฟังก์ชันและ Webhook URL ที่นั่น
เมื่อได้ Webhook URL มาแล้ว ก็ให้เอา URL นี้ไปอัพเดทที่ Messaging API Channel ใน LINE Developers console ตามที่ได้สร้างไว้ และให้มั่นใจว่าได้เปิดใช้ Use webhook แล้ว
ขั้นตอนนี้เราจะไปเปิดใช้งาน Gemini API พร้อมสร้าง API Key เพื่อนำไปเชื่อมต่อกับ LINE Chatbot โดยให้เข้าสู่เว็บไซต์ Google AI Studio แล้วจะเจอหน้า consent ก็ให้กดยอมรับให้เรียบร้อย
จากนั้นให้กดปุ่ม Create API key จะพบทางเลือกที่ให้เรากดสร้าง API key ด้วยการสร้างโปรเจคใหม่ หรือ จะเลือกโปรเจคที่มีอยู่แล้วใน Google Cloud ก็ได้(ถ้ามี) ซึ่งเมื่อสร้างเสร็จ เราก็จะได้ API key มา
ให้เราเพิ่มตัวแปร API_KEY ใน Environment Variable โดยเปิดไฟล์ .env ใน /functions แล้วให้เอา API key ระบุลงไป
CHANNEL_ACCESS_TOKEN=YOUR-CHANNEL-ACCESS-TOKEN
API_KEY=YOUR-GEMINI-API-KEY
สำหรับ module ที่เราจะสร้างจะแบ่งการทำงานออกเป็น 3 ฟังก์ชันดังนี้
history: [
{
role: "user",
parts: "สวัสดีค่ะ",
},
{
role: "model",
parts: "สวัสดีครับ ผมชื่อตี๋, ผู้เชี่ยวชาญเกี่ยวกับ LINE API ที่ช่วยตอบคำถามให้กับคุณครับ",
}
]
ถัดไปเราจะมาลงมือกัน เริ่มจากติดตั้ง GoogleGenerativeAI ซึ่งเป็น dependency สำหรับใช้งาน Gemini API โดยให้เปิด command line แล้วเข้าไปที่ /functions จากนั้นใช้คำสั่ง
npm install @google/generative-ai --save
ให้สร้างไฟล์ /functions/utils/gemini.js แล้วให้ copy โค้ดด้านล่างนี้ไปวาง
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.API_KEY);
const textOnly = async (prompt) => {
// For text-only input, use the gemini-1.5-flash-8b model
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-8b" });
const result = await model.generateContent(prompt);
return result.response.text();
};
const multimodal = async (imageBinary) => {
// For text-and-image input (multimodal), use the gemini-1.5-flash-8b model
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-8b" });
const prompt = "ช่วยบรรยายภาพนี้ให้หน่อย";
const mimeType = "image/png";
// Convert image binary to a GoogleGenerativeAI.Part object.
const imageParts = [
{
inlineData: {
data: Buffer.from(imageBinary, "binary").toString("base64"),
mimeType
}
}
];
const result = await model.generateContent([prompt, ...imageParts]);
const text = result.response.text();
return text;
};
const chat = async (prompt) => {
// For text-only input, use the gemini-1.5-flash-8b model
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-8b" });
const chat = model.startChat({
history: [
{
role: "user",
parts: [{ text: "สวัสดีจ้า" }],
},
{
role: "model",
parts: [{ text: "สวัสดีครับ ผมชื่อตี๋ ผมเป็นผู้เชี่ยวชาญเกี่ยวกับ LINE API ที่ช่วยตอบคำถามและแบ่งปันความรู้ให้กับชุมขนนักพัฒนา" }],
},
{
role: "user",
parts: [{ text: "ปัจจุบันมี LINE API อะไรบ้างที่ใช้งานได้ในประเทศไทย" }],
},
{
role: "model",
parts: [{ text: "ปัจจุบันมีทั้ง Messaging API, LIFF, LINE Login, LINE Beacon, LINE Notify, LINE Pay, และ LINE MINI App ที่สามารถใช้งานในไทยได้ครับ" }],
},
]
});
const result = await chat.sendMessage(prompt);
return result.response.text();
};
module.exports = { textOnly, multimodal, chat };
เมื่อเราเตรียม module ของ Gemini เรียบร้อยแล้ว ก็ให้เราไปอัพเดทโค้ดในไฟล์ /functions/index.js เพื่อดึงเอา module ของ Gemini มาใช้งาน
const {onRequest} = require("firebase-functions/v2/https");
const line = require("./utils/line");
const gemini = require("./utils/gemini");
จากนั้นเราจะไปลองทดลองใช้งานแต่ละฟังก์ชันกัน
ในไฟล์ /functions/index.js ให้เพิ่มโค้ดภายในส่วนที่ตรวจสอบเงื่อนไขว่าเป็นข้อความ text ตามนี้
if (event.message.type === "text") {
const msg = await gemini.textOnly(event.message.text);
await line.reply(event.replyToken, [{ type: "text", text: msg }]);
break;
}
จากนั้นก็ deploy ผ่าน command line ด้วยคำสั่ง
firebase deploy --only functions
เสร็จแล้วก็มาทดสอบกันดูหน่อย...
ในไฟล์ /functions/index.js ให้เพิ่มโค้ดภายในส่วนที่ตรวจสอบเงื่อนไขว่าเป็นข้อความ image ตามนี้
if (event.message.type === "image") {
const imageBinary = await line.getImageBinary(event.message.id);
const msg = await gemini.multimodal(imageBinary);
await line.reply(event.replyToken, [{ type: "text", text: msg }]);
break;
}
จากนั้นก็ deploy ผ่าน command line ด้วยคำสั่ง
firebase deploy --only functions
การทดสอบนี้ ผมจะแบ่ง prompt ออกเป็น 3 แบบ โดยเรียงลำดับตามผลลัพธ์จากซ้ายไปขวา ซึ่งเนื้อหาที่ Gemini ส่งออกมามันน่าทึ่งมากๆ (AI มันเห็นสิ่งเดียวกับที่เราเห็นเลย)
ในไฟล์ /functions/index.js ให้เพิ่มโค้ดภายในส่วนที่ตรวจสอบเงื่อนไขว่าเป็นข้อความ text ตามนี้
if (event.message.type === "text") {
const msg = await gemini.chat(event.message.text);
await line.reply(event.replyToken, [{ type: "text", text: msg }]);
break;
}
จากนั้นก็ deploy ผ่าน command line ด้วยคำสั่ง
firebase deploy --only functions
ผลทดสอบคือมันสามารถสวมบทเป็นเรา ตาม context ที่เราเตรียมไว้ และตอบคำถามเกี่ยวกับ LINE API ได้ดีเลย ซึ่งในอนาคต ถ้าใครจะต่อยอดก็เก็บบทสนทนาและเพิ่มเข้าไปใน history: [] ก่อนจะส่งให้มันสร้างเนื้อหาถัดๆไป ตัว AI มันก็จะเข้าใจบริบททั้งหมดที่คุยกันมาก่อนหน้าได้ แล้วตอบเนื้อหาที่แม่นยำมากยิ่งขึ้น
ยินดีด้วยครับ ถึงตรงนี้คุณก็มี LINE Chatbot ที่เชื่อมต่อกับ Gemini เป็นของคุณเองแล้ว!!!