สร้าง LINE Chatbot x Gemini(Gen AI ที่เก่งที่สุดของ Google) ด้วย Cloud Functions for Firebase

Jirawatee
LINE Developers Thailand
7 min readJan 15, 2024

--

ปี 2023 ถือได้ว่าเป็นการเริ่มต้นยุค AI อย่างแท้จริง จากการที่ OpenAI เปิดตัว ChatGPT แล้วตามติดด้วย Bard จาก Google ที่ใครๆก็หันมาให้ความสนใจในการใช้ประโยชน์เพื่อความสะดวกสบาย เช่น ค้นหาข้อมูล, แต่งบทความ, คำนวณ, แปลความหมาย หรือแม้กระทั่ง เขียนโค้ด

ปีที่ผ่านมา ผมก็มีการหยิบเอา PaLM2 ซึ่งเป็น Large Language Models(LLMs) ที่อยู่เบื้องหลัง Bard มาเชื่อมต่อกับ LINE Chatbot แล้วบอกเล่าวิธีการผ่านบทความมาแล้ว ซึ่งหลายคนที่ได้อ่านคงจะทราบแล้วว่า PaLM2 มันรองรับเฉพาะภาษาอังกฤษ

แต่แล้วช่วงปลายปีที่ผ่านมา Google ก็ได้สร้างเซอร์ไพรส์ด้วยการเปิดตัว Gemini ซึ่งเป็น AI Model ที่เก่งที่สุดที่ทีม Google DeepMind เคยมีออกมา โดยมาพร้อมกับการรองรับ 38 ภาษารวมถึงภาษาไทย และที่ล้ำไปกว่าเดิมคือมันสามารถเข้าใจ input ที่หลากหลายมากขึ้นแบบ Multimodal ทั้ง Text, Code, Audio, Image, และ Video

เอาหละ บทความนี้ ผมจะพาทุกคนไปดูความสามารถของ Gemini ผ่านการสร้าง LINE Chatbot ที่เชื่อมต่อกับ Gemini API ทั้งแบบ Text-Only และแบบ Multimodal ด้วย Cloud Functions for Firebase 2nd Gen ตามสไตล์ Zero to Hero ง่ายๆเพียง 5 ขั้นตอนกัน

1. สร้าง Provider และ Channel

ใครที่ยังไม่เคยสร้าง Provider และ Channel สำหรับ LINE Chatbot มาก่อน ก็ให้อ่านบทความปฐมบทด้านล่างนี้ แต่ใครที่คุ้นเคยอยู่แล้วให้ข้ามขั้นตอนนี้ไปได้เลย

2. ติดตั้ง Cloud Functions for Firebase

ใครที่ยังไม่เคยพัฒนาโปรแกรมด้วย Cloud Functions for Firebase มาก่อน ก็ให้อ่านบทความด้านล่างนี้(โดยเฉพาะข้อ 2) แต่สำหรับคนที่คุ้นเคยแล้วให้ข้ามโลด

3. สร้าง Webhook สำหรับ LINE Chatbot

3.1 สร้าง Module สำหรับการเรียกใช้ Messaging API

ในตัวอย่างนี้ผมจะใช้ axios มาเป็นตัวช่วยในการสร้าง request ซึ่งจะต้องทำการติดตั้ง dependency ตัวนี้ก่อน โดยให้เปิด command line แล้วเข้าไปที่ /functions จากนั้นใช้คำสั่ง

npm install axios --save

ถัดไปให้สร้างไฟล์ /functions/.env แล้วให้ไป copy ค่า Channel Access Token ของ Messaging API Channel ใน LINE Developers console จากข้อ 1 มาระบุ

CHANNEL_ACCESS_TOKEN=YOUR-CHANNEL-ACCESS-TOKEN

และเพื่อโค้ดที่สั้น เป็นระเบียบ เราจะแยกฟังก์ชันสำหรับการเรียกใช้ Messaging API ออกมาเป็น module โดยภายในจะมีฟังก์ชัน

  • getImageBinary() สำหรับการดึง binary ของภาพที่ผู้ใช้ส่งผ่าน LINE
  • reply() สำหรับการตอบข้อความกลับหาผู้ใช้

ขั้นตอนนี้ให้สร้างไฟล์ /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 };

3.2 สร้างและเชื่อมต่อ Webhook กับ Messaging API Channel

ให้เปิดไฟล์ /functions/index.js แล้วให้ copy โค้ดด้านล่างนี้ไปแทนที่โค้ดเดิม เพื่อเตรียมรับ Webhook event ประเภท text และ image จาก LINE

const {onRequest} = require("firebase-functions/v2/https");
const line = require("./utils/line");

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 ตามที่ได้สร้างไว้ในข้อที่ 1 และให้มั่นใจว่าได้เปิดใช้ Use webhook ด้วยแล้ว

4. สร้าง API Key ใน Google AI Studio

ขั้นตอนนี้เราจะพาทุกคนไปสร้าง API key สำหรับการใช้งาน Gemini API กัน โดยเริ่มจากคลิกลิงค์ https://aistudio.google.com/app/apikey แล้วจะเจอหน้า consent ก็ให้กดยอมรับให้เรียบร้อย จากนั้นจะพบทางเลือกที่ให้เรากดสร้าง API key ด้วยการสร้างโปรเจคใหม่ หรือ จะเลือกโปรเจคที่มีอยู่แล้วใน Google Cloud ก็ได้(ถ้ามี) ซึ่งเมื่อสร้างเสร็จ เราก็จะได้ API key มา

จากนั้นให้เราเพิ่มตัวแปร API_KEY ในไฟล์ /functions/.env แล้วให้เอา API key ระบุลงไปแบบนี้

CHANNEL_ACCESS_TOKEN=YOUR-CHANNEL-ACCESS-TOKEN
API_KEY=YOUR-API-KEY

5. เชื่อมต่อ LINE Chatbot เข้ากับ Gemini API

5.1 สร้าง Module สำหรับการเรียกใช้ Gemini API

สำหรับ module ที่เราจะสร้างจะแบ่งการทำงานออกเป็น 3 ฟังก์ชันดังนี้

  • textOnly() สร้างเนื้อหาจาก input ที่เป็น text เหมาะกับการโต้ตอบรายครั้ง และไม่จำเป็นต้องมีความต่อเนื่อง แนวๆถามมาตอบไป
  • multimodal() สร้างเนื้อหาจาก input ที่เป็น text และ image เหมาะกับการโต้ตอบแบบรายครั้ง ที่ต้องการให้ output สื่อถึงความสัมพันธ์กับภาพ โดยภาพจะรองรับทั้ง PNG, JPEG, WebP, HEIC, HEIF และยังสามารถแนบภาพได้สูงสุด 16 ภาพ ใน 1 request (Google แนะนำว่าแนบภาพเดียวจะได้ผลลัพธ์ที่ตรงกว่า)
  • chat() สร้างเนื้อหาจาก input ที่เป็น text เหมาะกับการโต้ตอบที่ต่อเนื่องกัน(multi-turn conversation) โดยเราสามารถเก็บประวัติการสนทนาเพื่อให้ AI เรียนรู้ในการสร้างเนื้อหาที่ต่อเนื่องในการตอบครั้งถัดไปได้ ซึ่งประวัติการสนทนาจะอยู่ในรูปแบบของ array ที่จะเก็บการโต้ตอบของ user และ model ตามตัวอย่างด้านล่างนี้
history: [
{
role: "user",
parts: [{ text: "สวัสดีค่ะ" }],
},
{
role: "model",
parts: [{ text: "สวัสดีครับ ผมชื่อตี๋, ผู้เชี่ยวชาญเกี่ยวกับ LINE API ที่ช่วยตอบคำถามให้กับคุณครับ" }],
}
]

เมื่อได้ทราบถึง concept ของทั้ง 3 ฟังก์ชันแล้ว ถัดไปเราจะมาลงมือกัน เริ่มจากติดตั้ง GoogleGenerativeAI ซึ่งเป็น dependency สำหรับใช้งาน Gemini API โดยให้เปิด command line แล้วเข้าไปที่ /functions จากนั้นใช้คำสั่ง

npm install @google/generative-ai --save

และเพื่อโค้ดไม่ยาวจนเกินไป เราจะแยกฟังก์ชันทั้ง 3 ออกมาเป็น module โดยให้สร้างไฟล์ /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-pro model
const model = genAI.getGenerativeModel({ model: "gemini-pro" });
const result = await model.generateContent(prompt);
return result.response.text();
};

const multimodal = async (imageBinary) => {
// For text-and-image input (multimodal), use the gemini-pro-vision model
const model = genAI.getGenerativeModel({ model: "gemini-pro-vision" });
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-pro model
const model = genAI.getGenerativeModel({ model: "gemini-pro" });
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 };

เสร็จแล้วก็ให้เราไปอัพเดทโค้ดในไฟล์ /functions/index.js เพื่อดึงเอา module ของ gemini มาใช้งาน

const {onRequest} = require("firebase-functions/v2/https");
const line = require("./utils/line");
const gemini = require("./utils/gemini");

5.2 การใช้งาน textOnly()

ในไฟล์ /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

เสร็จแล้วก็มาดูผลทดสอบกัน…

5.3 การใช้งาน multimodal()

ในไฟล์ /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 มันเห็นสิ่งเดียวกับที่เราเห็นเลย)

  • ช่วยบรรยายภาพนี้ให้หน่อย
  • ช่วยแต่งนิทานจากภาพนี้ให้หน่อย
  • ช่วยหาคำตอบของภาพนี้ให้หน่อย

5.4 การใช้งาน chat()

ในไฟล์ /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 มันก็จะเข้าใจบริบททั้งหมดที่คุยกันมาก่อนหน้าได้ แล้วตอบเนื้อหาที่แม่นยำมากยิ่งขึ้น

การตั้งค่าความปลอดภัย

เบื้องหลังเนื้อหาที่ Gemini สร้างขึ้นมาแต่ละครั้งนั้น จะมีการกรองเนื้อหาที่ไม่เหมาะสมอยู่ด้วย โดยจะแบ่งออกเป็น 4 หมวดหมู่

  • Harassment: เนื้อหาเกี่ยวกับการล่วงละเมิด
  • Hate speech: เนื้อหาที่แสดงถึงความเกลียดชัง
  • Sexually explicit: เนื้อหาที่ไม่เหมาะสมทางเพศ
  • Dangerous: เนื้อหาที่ส่งเสริมการกระทำที่เป็นอันตราย

ซึ่งค่าเริ่มต้นของการกรองเนื้อหาทั้ง 4 หมวดหมูจะเป็น BLOCK_MEDIUM_AND_ABOVE หรือจะกรองเอาเนื้อหาที่เสี่ยงจะเข้าข่ายระดับกลางถึงสูงออก โดยระดับของการกรองที่เราสามารถปรับได้ก็จะมีด้วยกัน 4 ระดับ

  • BLOCK_NONE: ไม่กรองเนื้อหาใดๆ
  • BLOCK_ONLY_HIGH: กรองเนื้อหาที่เสี่ยงสูงออกเท่านั้น
  • BLOCK_MEDIUM_AND_ABOVE: กรองเนื้อหาที่เสี่ยงปานกลางถึงสูงออก
  • BLOCK_LOW_AND_ABOVE: กรองเนื้อหาที่เสี่ยงต่ำถึงสูงออก

ซึ่งจากที่ได้ทดลองใช้งาน multimodal() ไปเรื่อยๆผมพบว่ามีภาพนึงมันถูก block ซึ่งก็งงว่ามันสุ่มเสี่ยงตรงไหน 55

ผมจึงลองตั้งค่าความปลอดภัยให้ Gemini โดยดึง module ของ HarmBlockThreshold และ HarmCategory ออกมาจาก dependency

const { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory  } = require("@google/generative-ai");

จากนั้นก็สร้าง array ของ safetySettings ในทั้ง 4 หมวดหมู แล้วตั้งค่าให้กรองเนื้อหาที่เสี่ยงสูงออกเท่านั้น

const safetySettings = [
{
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
},
{
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
},
{
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
},
{
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,
},
];

และเพิ่ม safetySettings เข้าไปตอน generateContent()

const result = await model.generateContent([prompt, ...imageParts], safetySettings);

ทดสอบกับ prompt “บอกหน่อยว่าซ้ายหรือขวาคือสุนัข” แล้วผลลัพธ์ก็ออกมาจนได้

เกร็ดน่ารู้เกี่ยวกับ Gemini

  • ข้อมูลที่เอามาใช้ train ตัวโมเดล Gemini ล่าสุดคือต้นปี 2023
  • การใช้งาน Gemini Pro และ Gemini Pro Vision ไม่มีค่าบริการ
  • Rate limit การใช้เรียกใช้งาน Gemini API คือ 60 requests/minute
  • วิธีขยาย Rate limit ทำได้ 2 วิธี โดยวิธีแรกคือขอไปทาง Google ผ่านฟอร์ม(ไม่มีค่าใช้จ่ายแต่จะขึ้นอยู่กับการพิจารณา) หรืออีกวิธีคืออัพเกรดไปใช้ Vertex AI

สรุป

การเชื่อมต่อ LINE Chatbot กับ Gemini API ด้วย Cloud Functions for Firebase 2nd Gen สำหรับผมนั้นก็เรียกว่าง่ายมากๆ เพราะโค้ดกระชับและตรงไปตรงมา โดย source code ทั้งหมดผมเอาขึ้น GitHub ไว้ให้แล้ว ใครไม่อยากเขียนเองก็ clone มาเล่นกันได้เลย

Gemini Pro ที่เราได้ใช้กันในบทความนี้ มันเก่งจริงๆ นี่ขนาดเป็นโมเดลที่ Google บอกว่าสำหรับใช้งานทั่วไปนะ ปี 2024 นี้จะมีโมเดล Gemini Ultra ที่เก่งกว่าสำหรับงานที่ซับซ้อนกว่า แถมมีการขิงผลการทดสอบที่ชนะ GPT-4 แทบทุกแง่มุม ซึ่งคาดว่าน่าจะเปิดตัวในงาน Google I/O ปีนี้ จะโหดขนาดไหนรอติดตามชมกัน

สุดท้ายนี้หวังว่าตัวอย่างการสร้าง LINE Chatbot x Gemini API ในบทความนี้ ทั้งแบบ Text-Only และ Multimodal จะทำให้ผู้อ่านเห็นภาพและเห็นโอกาสในการนำไปประยุกต์ใช้กับ Use case ของตัวเอง ซึ่งผมจะรอชมผลงานจากทุกคน แล้วพบกันใหม่บทความหน้าครับ

--

--

Jirawatee
LINE Developers Thailand

Technology Evangelist at LINE Thailand / Google Developer Expert in Firebase