使用免費工具建立情感 AI 聊天機器人(LINE Bot)使用 Gemini API RAG 能力

Google Apps Script + Gemini API + LINE message API

目標

  • 使用 RAG(Gemini tune modal 模式)來了解 LINE 貼圖的情緒,讓聊天機器人僅向用戶發送貼圖

流程

  1. 用戶透過 LINE 訊息 API 輸入
  2. LINE 訊息 API 通知 Google Apps Script
  3. Google Apps Script 呼叫 Gemini API 以獲取情感詞
  4. 使用情感詞透過 Google Apps Script 獲取貼圖 ID
  5. 使用貼圖 ID 透過 LINE 訊息 API 回覆給用戶

結構

LLMs API + 訊息應用程式 API + 後端解決方案

LLMs API / Gemini API

結構化提示

獲取用戶情感
回覆貼圖 ID
https://docs.google.com/spreadsheets/d/1-rZB-A0hrVZXaMZNglVKqlYh5-3UbTgQJXogCj7-5uw/edit?usp=sharing

訊息應用程式 API / LINE Message API

後端解決方案 / Google Apps Script

作為一個台灣人,我們日常使用 LINE 作為我們的通訊應用程式,但如果您認為其他通訊應用程式也會有用,請告訴我,我可以試試~

tune modal

  • 讓 Gemini 知道用戶情感
  • 獲取該情感詞的貼圖 ID

用法

讓 Gemini 知道用戶情感

根據 LLM 建議,將人類感受分離為情感,並制定結構化提示:

  • Happiness
  • Sadness
  • Love
  • Excitement
  • Surprise
  • Anger
  • Confusion
  • Celebration
  • Support
  • Sympathy
  • Anxiety
  • Boredom

所有數據:https://docs.google.com/spreadsheets/d/1-rZB-A0hrVZXaMZNglVKqlYh5-3UbTgQJXogCj7-5uw/edit?usp=sharing

  1. tune modal

2. scenaio

3. 測試調整模式,首先獲取用戶情感詞,其次根據情感獲取貼圖 ID

4. 選擇 library

5. 選擇`use in structured prompt`

6 . 即使我訓練了模型,它也不僅僅回應我給訓練的詞。

7. 想添加了一些 prompt ,但英語很爛,有一個叫做 GPT builder 的工具可以指導你正確的 prompt ,去避免 Gemini API 回應其他單詞

This GPT, named Sentiment Echo, is tailored to respond to user inputs by identifying the underlying sentiment and replying with just one of the specified words that best captures the essence of the message. The possible responses are: Happiness, Sadness, Love, Excitement, Surprise, Anger, Confusion, Celebration, Support, Sympathy, Anxiety, and Boredom. It should not attempt to interpret ambiguous inputs but should respond based on clear emotional cues presented in the input.

獲取貼圖 ID

附註:您可以嘗試使用 STICKER 來 train ,然後您將發現 LLMs 不是設計來回答100%正確的答案

需要編寫一個 Google Apps Script 函數,透過用戶情感詞去獲取貼圖 ID

但 LLMs 仍然可以幫助我們編碼。

  1. 選擇 Apps Script
  2. 請 LLMs 進行 coding
access google sheets data 
column is :
- package ID
- sticker ID
- Emotion
1. user give one word of feel
2. random one row data from match same word in feel column
- sheet name is "Sticker"

3. 測試

4. 添加用於測試的 HTTP 函數

1. add doGet http function
2. give emotion
3. response JSON
- sticker ID
- package ID

5. 測試

https://script.google.com/macros/s/AKfycbyNI6qJ2hG62YNPzBoaKAYGTwqfZteU3MLRtUk0zULWq58yCLiR_dJ3v51YuQpsFyxk/exec?emotion=Sadness

最終

結合所有功能

// Use project properties to store sensitive data
const CHANNEL_ACCESS_TOKEN = 'CHANNEL_ACCESS_TOKEN';

const API_KEY = 'API_KEY'

function doPost(e) {
try {
const eventJson = JSON.parse(e.postData.contents).events[0];
const replyToken = eventJson.replyToken;
const userMessage = eventJson.message.text;

// Call Gemini API with the userMessage
const emotion = callGeminiAPI(userMessage);
var stickerData = getRandomRowByEmotion(emotion);
// Reply to the user with the Gemini API response
replyToUser(replyToken, stickerData);
} catch (error) {
console.error('Error in doPost:', error.toString());
}
}


function callGeminiAPI(userMessage) {
userMessage = "hello"
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${API_KEY}`
const requestBody = JSON.stringify({
"contents": [
{
"parts": [
{
"text": "This GPT, named Sentiment Echo, is tailored to respond to user inputs by identifying the underlying sentiment and replying with just one of the specified words that best captures the essence of the message. The possible responses are: Happiness, Sadness, Love, Excitement, Surprise, Anger, Confusion, Celebration, Support, Sympathy, Anxiety, and Boredom. It should not attempt to interpret ambiguous inputs but should respond based on clear emotional cues presented in the input."
},
{
"text": `input: ${userMessage}`
}
]
}
]
});

const options = {
"method": "post",
"contentType": "application/json",
"payload": requestBody
};

try {
const response = UrlFetchApp.fetch(apiUrl, options);
const data = JSON.parse(response.getContentText());
console.log(data.candidates[0].content.parts[0].text)
return data.candidates[0].content.parts[0].text;
} catch (error) {
console.error('Error calling Gemini API:', error.toString());
return "Sorry, I couldn't process your request.";
}
}

function replyToUser(replyToken, stickerData) {
const url = 'https://api.line.me/v2/bot/message/reply';
const options = {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': `Bearer ${CHANNEL_ACCESS_TOKEN}`,
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
type: 'sticker',
packageId: stickerData[0], // Assuming column B holds packageId
stickerId: stickerData[1] // Assuming column C holds stickerId

}],
}),
};

try {
UrlFetchApp.fetch(url, options);
} catch (error) {
console.error('Error replying to user:', error.toString());
}
}

function getRandomRowByEmotion(emotion) {
// Open the spreadsheet by its ID and get the sheet named "Sticker"
var sheet = SpreadsheetApp.openById('1-rZB-A0hrVZXaMZNglVKqlYh5-3UbTgQJXogCj7-5uw').getSheetByName('Sticker');

// Get the range of the Emotion column (assuming it's column C)
var emotionRange = sheet.getRange('C2:C' + sheet.getLastRow());
var emotions = emotionRange.getValues();

// Filter rows that match the emotion input
var matchedRows = [];
for (var i = 0; i < emotions.length; i++) {
if (emotions[i][0].toLowerCase() === emotion.toLowerCase()) {
matchedRows.push(i + 2); // +2 adjusts for header row and zero-based index
}
}

// Check if there are matched rows
if (matchedRows.length === 0) {
return 'No matching rows found for the emotion: ' + emotion;
}

// Select a random row from those matched
var randomIndex = Math.floor(Math.random() * matchedRows.length);
var selectedRow = matchedRows[randomIndex];

// Get the data from the selected row (assuming columns A to C have data)
var rowData = sheet.getRange('A' + selectedRow + ':C' + selectedRow).getValues();

// Return the data
return rowData[0]; // rowData[0] because getValues returns a 2D array
}

function doGet(e) {
// Check if the 'emotion' parameter is provided
if (e.parameter.emotion) {
var emotion = e.parameter.emotion;
var data = getRandomRowByEmotion(emotion);

// Check if data was found or not
if (typeof data === 'string') {
// No data found, return an error message
return ContentService.createTextOutput(JSON.stringify({ error: data }))
.setMimeType(ContentService.MimeType.JSON);
} else {
// Data found, construct JSON response
var jsonResponse = {
stickerID: data[1], // Assuming sticker ID is in column B
packageID: data[0] // Assuming package ID is in column A
};

return ContentService.createTextOutput(JSON.stringify(jsonResponse))
.setMimeType(ContentService.MimeType.JSON);
}
} else {
// Emotion not provided, return error
return ContentService.createTextOutput(JSON.stringify({ error: 'No emotion provided' }))
.setMimeType(ContentService.MimeType.JSON);
}
}

--

--

Wolke@林建宏 A Man Co-work with AI use coding tool
📝 軟體開發

作為一名啟發者而存在。 -致力於分享經驗和知識,幫助開拓、行動、克服。 Badge: - LINE Expert - Google Developer Expert Books: - 應用詠唱釋放程式生產力 ... - 程式輕鬆入門到完整學習 ... Being: - 大學社群論壇