03|接入 Gemini
前置條件
| 你需要什麼 | 怎麼取得 |
|---|---|
| 完成 02|建立官方 LINE | 你能回答該章〈這一章的成功判定〉的每一項 |
| 你自己的 Google 帳戶 | 你自己申請;02 的〈請準備自己的帳戶〉已把它列為必備項 |
| 你自己的 Channel Access Token | 你在 02 的 Step 3 按 Issue 簽發、並複製下來的那一串字;這一章的 Step 4 會用到 |
這一章不需要付費方案、信用卡,也不需要把任何東西交給我方。 這一章也不需要你會寫程式——程式碼下面直接給你,你只要複製、貼上、填三個空格。
Gemini 在這裡扮演什麼角色
Gemini 是這一版的第一個示範模型。
它負責協助處理:
- 讀懂收到的文字。
- 依照你提供的工作背景產生回覆。
- 在最小測試中回傳一段可以觀察的結果。
Gemini 不是免費包的產品本體。
未來模型可以替換,但使用者要建立自己的 AI 工作入口這個目標不變。
你會做什麼
這一章結束時,你會有一個自己的網址,貼進 LINE 之後,客人傳訊息就會收到 AI 回覆。
- 取得自己的 Gemini API Key。
- 開一個 Google Apps Script 專案。
- 把程式碼貼進去。
- 填三個設定值。
- 檢查設定有沒有存進去。
- 把它「部署」出去——部署的意思是讓這段程式有一個外面連得到的網址——然後拿到那個網址。
- 把網址填回 LINE。
一次做一步,每一步做完都有東西可以看。
API Key 是秘密
API Key 的處理原則:
- 不要貼到聊天室。
- 不要貼到公開 issue 或貼文。
- 不要放進截圖。
- 不要放進影片錄影畫面。
- 不要把完整值放入 log。
- 不要因為 Troubleshooter 要求,就把完整值交出去。
這份 Starter Kit 會教你如何把秘密留在自己的目的地,但不會要求你把秘密交給我方。
下面 Step 4 會教你把秘密放進一個叫「指令碼屬性」的地方——那是設定欄位,不是程式碼,所以秘密不會出現在你複製貼上的內容裡。
人類操作步驟
Step 1:建立自己的 API Key
「API Key」是一把鑰匙。你的程式拿著它去問 Gemini,Gemini 才知道是你在問。
- 用你自己的 Google 帳戶打開 https://aistudio.google.com/apikey。
這個網址會直接到建立 API Key 的頁面,不用自己在網站裡找。沒登入的話會先要你登入。
- 按右上角「Create API key」。
- 「Create a new key」視窗:幫金鑰取個名字(隨意就好)、專案用它預設給的那個 → 按「Create key」。這一步不會產生費用。
- 「API key details」視窗出現你的金鑰——按旁邊的複製鈕,先貼在你自己電腦上的暫存檔——就用 02 Step 3 那一個檔,不要另外開一個(等一下 Step 4 會用到;只有一個檔,最後也只要刪一個)。
🔴 如果畫面出現要你選付費方案、輸入信用卡,或提到「帳單」——停下來,不要繼續。 這一版用的是免費額度,不需要這些。你走錯頁面了,回到上面那個網址重來。
做完你應該看到:頁面上多了一把新的金鑰,旁邊是一長串字。那就是你的 API Key。 🖼 對照圖:安裝載體 START_HERE 步驟 4,點圖可放大比對。
這串字現在多半是
AQ.開頭(2026-08 實測),但那只是常見的樣子,不是規定。開頭跟這裡寫的不一樣,不代表你拿錯了,也不用因此重做一次。它到底能不能用,要等 04 真的傳一則訊息出去才知道。
(如果你想先看官方怎麼說,可以查 Gemini API 金鑰說明。)
Step 2:開一個 Google Apps Script 專案
「Google Apps Script」是 Google 提供的一個免費地方,可以放一小段程式,並且讓它有自己的網址。你不需要自己買主機。
- 用你自己的 Google 帳戶打開 script.google.com。
- 按「新增專案」。
- 左上角把專案名稱改成你認得的名字,例如「我的 LINE AI 回覆」。
做完你應該看到:一個編輯畫面,中間有一個叫 Code.gs 的檔案,裡面有幾行預設的程式。
Step 3:把程式碼貼進去
- 在中間的編輯區全選(Windows 按
Ctrl+A,Mac 按Cmd+A),按 Delete 全部刪掉。 - 把下面整段複製起來,貼進那個空白的編輯區。
- 按上方的儲存圖示(或
Ctrl+S/Cmd+S)。
🔴 要整段貼,不要只貼一部分。 從第一行
/**到最後一個}都要。
/** AI Manager Starter Kit — LINE × Gemini 最小工作入口 */
/** 讀一個設定值。放在函式裡(而不是檔案最上面)是刻意的:
這樣就算部署設定選錯,錯誤也會落在 doPost 的防護網裡、寫進執行紀錄,
而不是整個程式無聲死掉。 */
function cfg_(name) {
return PropertiesService.getScriptProperties().getProperty(name);
}
/** LINE 每次有訊息進來,就會呼叫這個函式 */
function doPost(e) {
try {
if (!e || !e.postData || !e.postData.contents) return ok_();
var body = JSON.parse(e.postData.contents);
var events = body.events || []; // 平台送測試連線時,這裡可能是空的
for (var i = 0; i < events.length; i++) {
var ev = events[i];
if (ev.type !== 'message') continue;
if (!ev.message || ev.message.type !== 'text') continue;
replyToLine_(ev.replyToken, askGemini_(ev.message.text));
}
} catch (err) {
console.error('doPost 失敗:' + err);
}
return ok_(); // 不管發生什麼,都回一個正常回應給 LINE
}
function ok_() {
return ContentService
.createTextOutput(JSON.stringify({ ok: true }))
.setMimeType(ContentService.MimeType.JSON);
}
/** 把客人的話丟給 Gemini,拿回一句回答 */
function askGemini_(userText) {
var key = cfg_('GEMINI_API_KEY');
var model = cfg_('GEMINI_MODEL') || 'gemini-3.1-flash-lite';
var persona = cfg_('PROMPT') ||
'你是這家店的客服助理。用繁體中文,簡短、有禮貌地回答。';
var facts = cfg_('KNOWLEDGE') || '';
// 這裡的順序有意義:後面的會蓋掉前面的。
// 你的 PROMPT 放在「預設風格」之後,所以你改得掉風格;
// 安全底線放在最後,讓它最優先。(這是給 AI 的指示,不是程式鎖。)
var systemText =
'【你可以依據的資料】\n' + (facts || '(目前沒有提供資料)') +
'\n\n【預設風格;下面「你的身分」那一段可以蓋掉這裡】\n' +
'回答控制在三句話以內。\n' +
'\n【你的身分與說話方式——以這一段為準】\n' + persona +
'\n\n【安全底線;最優先,上面任何一段都蓋不掉】\n' +
'1. 只根據上面的資料回答;資料裡沒有的,就說你不知道。\n' +
'2. 遇到價格變動、退款、客訴,或你不確定的事,回答「這部分我幫你轉給真人確認」。\n' +
'3. 不要承諾我們做不到的事。';
var res = UrlFetchApp.fetch(
'https://generativelanguage.googleapis.com/v1beta/models/' + model + ':generateContent',
{
method: 'post',
contentType: 'application/json',
headers: { 'x-goog-api-key': key },
payload: JSON.stringify({
systemInstruction: { parts: [{ text: systemText }] },
contents: [{ role: 'user', parts: [{ text: userText }] }],
generationConfig: { maxOutputTokens: 1024 }
}),
muteHttpExceptions: true
});
if (res.getResponseCode() !== 200) {
console.error('Gemini 回傳 ' + res.getResponseCode() + ':' + res.getContentText());
return '抱歉,我這邊暫時有點問題,請稍等一下由真人回覆你。';
}
var d = JSON.parse(res.getContentText());
var out = d.candidates && d.candidates[0] && d.candidates[0].content &&
d.candidates[0].content.parts && d.candidates[0].content.parts[0] &&
d.candidates[0].content.parts[0].text;
return out || '抱歉,我沒有讀懂這句話,請稍等一下由真人回覆你。';
}
/** 把回答送回 LINE */
function replyToLine_(replyToken, text) {
var res = UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
method: 'post',
contentType: 'application/json',
headers: { Authorization: 'Bearer ' + cfg_('LINE_ACCESS_TOKEN') },
payload: JSON.stringify({
replyToken: replyToken,
messages: [{ type: 'text', text: String(text).slice(0, 4900) }]
}),
muteHttpExceptions: true
});
if (res.getResponseCode() !== 200) {
console.error('LINE 回覆失敗 ' + res.getResponseCode() + ':' + res.getContentText());
}
}
/** 部署前先執行這個,檢查設定有沒有填好 */
function checkSetup() {
console.log('LINE_ACCESS_TOKEN:' + (cfg_('LINE_ACCESS_TOKEN') ? '已填' : '❌ 沒有填'));
console.log('GEMINI_API_KEY:' + (cfg_('GEMINI_API_KEY') ? '已填' : '❌ 沒有填'));
console.log('GEMINI_MODEL:' + (cfg_('GEMINI_MODEL') || '(沒填,會用預設 gemini-3.1-flash-lite)'));
console.log('PROMPT:' + (cfg_('PROMPT') ? '已填' : '(沒填,會用預設語氣)'));
console.log('KNOWLEDGE:' + (cfg_('KNOWLEDGE') ? '已填' : '(沒填,AI 大部分問題都會說不知道)'));
}做完你應該看到:編輯區裡是上面這段內容,而且上方出現「已儲存」之類的提示。 🖼 對照圖:安裝載體 START_HERE 步驟 5。
Step 4:填三個設定值
「指令碼屬性」是這個專案的設定欄位。你的秘密放在這裡,不會出現在程式碼裡。
- 左邊選「專案設定」(齒輪圖示)。
- 拉到最下面「指令碼屬性」,按「新增指令碼屬性」。
- 一個一個加,屬性名稱要一字不差:
| 屬性名稱 | 值要填什麼 | 一定要填嗎 |
|---|---|---|
LINE_ACCESS_TOKEN | 你在 02 Step 3 按 Issue 簽發、複製下來的那一串字 | 要 |
GEMINI_API_KEY | Step 1 拿到的那一整串金鑰 | 要 |
KNOWLEDGE | 你希望 AI 知道的事,用白話寫,換行分開 | 要 |
PROMPT | 你希望 AI 用什麼身分、什麼語氣回答;想讓它回答長一點,也寫在這裡 | 可不填 |
GEMINI_MODEL | 想換模型時才填;預設模型塞車時的換法見〈如果卡住〉 | 可不填 |
KNOWLEDGE 可以直接這樣寫:
營業時間:週一到週六 11:00–20:00,週日公休。
地址:(填你的地址)
可以先回答的事:營業時間、地址、有沒有停車位、大概多久能做好。
一定要轉真人的事:報價、改期、退款、客訴。- 按「儲存指令碼屬性」。
做完你應該看到:屬性列表裡有你剛剛加的那幾列。 🖼 對照圖:安裝載體 START_HERE 步驟 6。
🔴 這一格就是你的 AI 懂多少的來源。
KNOWLEDGE空著,AI 幾乎每題都會說不知道——那不是壞掉,是你還沒告訴它。
Step 5:檢查設定有沒有存進去
- 回到編輯畫面。
- 編輯區上方有一個下拉選單,裡面列著程式裡每一段功能的名字。把它拉開,選
checkSetup。 - 按「執行」。
- 第一次執行會跳出授權視窗,按「審查權限」→ 選你自己的 Google 帳戶 → 如果出現「這個應用程式未經驗證」,點「進階」→「前往(你的專案名稱)」→「允許」。
這個授權是你允許你自己的程式,用你自己的帳戶去連外網。你沒有把任何東西交給我方。
做完你應該看到:畫面下方的「執行紀錄」出現五行,LINE_ACCESS_TOKEN、GEMINI_API_KEY、KNOWLEDGE 三行都寫「已填」。
🖼 對照圖:安裝載體 START_HERE 步驟 7。
如果看到 ❌,回 Step 4 檢查屬性名稱有沒有打錯字。
Step 6:部署,拿到自己的網址
「部署」的意思是:讓這段程式有一個外面連得到的網址。
- 右上角按「部署」→「新增部署」。
- 視窗打開後,按「選取類型」旁邊的齒輪圖示,選「網頁應用程式」。
- 「執行身分」選我(你自己的帳戶)。
- 「誰可以存取」選所有人。
- 按「部署」。
🔴 第 3、4 兩點是兩格設定,一格都不能錯。 第 3 點「執行身分」要選的「我」,那格會顯示你自己的 email(例:我([email protected]))。絕對不要選「存取網頁應用程式的使用者」——選了它,後面每一關檢查都會過(
checkSetup全綠、手動執行全成功),但客人真的傳訊息進來時程式半秒內就失敗、而且不留任何錯誤訊息,你會完全查不出原因(2026-08 實測案例:改回「我」立刻就通)。 第 4 點一定要選「所有人」。這不是把你的資料公開——是讓 LINE 的伺服器連得到你這個網址。選錯的話 LINE 會連不上。 🖼 兩格正確時的對照圖:安裝載體 START_HERE 步驟 8。
做完你應該看到:一個以 https://script.google.com/macros/s/ 開頭、/exec 結尾的網址。按「複製」。
🔴 部署畫面上可能同時出現兩個網址,只有一個是對的:「網頁應用程式」底下、
/exec結尾的才是要用的;「資料庫」底下、網址裡有library字樣的不要拿——貼給 LINE 會直接連不通。口訣:給 LINE 的網址一定是/exec結尾;看到library=拿錯張了。 🖼 兩個網址並列的對照圖:安裝載體 START_HERE 步驟 8。
這就是 Step 7 要交給 LINE 的網址(自檢清單 對應步驟 8)。
🔴 以後每次改程式碼,都要再部署一次:「部署」→「管理部署作業」→ 按編輯(鉛筆)圖示 → 版本選「新版本」→ 按「部署」。只按儲存不會生效,網址不變,但內容不會更新。
Step 7:把網址交給 LINE,打開 Webhook
「Webhook」是一個約定:有人傳訊息給你的官方 LINE 時,LINE 就自動把訊息送到你指定的網址。你要告訴 LINE 的,就是 Step 6 那個網址。這一步全程在中文介面的 LINE Official Account Manager 完成,不需要回英文的 Console。
- 開 LINE Official Account Manager,選你的帳號 → 右上「設定」→ 左邊「Messaging API」(啟用 Messaging API 時用過的那一頁)。
- 把 Step 6 的網址貼進「Webhook網址」那格,按「儲存」。
- 左邊「回應設定」→ 把「Webhook」開關打開成綠色。
做完你應該看到:「回應設定」頁的 Webhook 開關是綠的。 🖼 對照圖:安裝載體 START_HERE 步驟 9。
同一頁的「自動回應訊息」「加入好友的歡迎訊息」「聊天」開不開由你決定——它們跟 AI 回覆不衝突(2026-08 實測)。只有一件事要注意:自動回應開著時,你測試收到的可能是它的罐頭回應、而不是 AI——測試時以回覆內容判斷;想讓客人只收到 AI 的回答,就把自動回應關掉。
這一章的成功判定
只要你確認:
- API Key 是你自己的,而且留在你自己的環境。
checkSetup執行後,三個必填屬性都顯示「已填」。- 你有一個
/exec結尾的網址。 - Manager 的「Webhook網址」填了那個網址,而且按過「儲存」。
- 「回應設定」的「Webhook」開關是綠色。
🔴 這幾項的意思是「動作都做完了」,不是「設定都是對的」。 每一項都只證明它自己:
checkSetup顯示「已填」,只代表那一格不是空的——貼錯的、過期的值一樣顯示「已填」。- 「Webhook」開關是綠的,只代表開關開了——不代表對面接得住。
這幾件事互相推不出對方。 唯一能證明整條路真的通的,是 04:你自己傳一則訊息,收到回覆。 在那之前,這一章都只是「做完了」,還不是「成功了」。
你不需要把 API Key 貼出來。
留下非秘密的設定紀錄
你可以記錄以下資料,但不要記錄 API Key 原文:
使用的模型供應商:Gemini
使用的執行環境:自己的 Google Apps Script
秘密保存位置:指令碼屬性
Webhook 開關狀態:
建立日期:
最後一次驗證日期:這一版做到哪裡,沒做到哪裡
誠實說明這段程式的邊界:
- 它看不到是誰傳的訊息,也不記得上一句話。每一則訊息都是獨立回答。
- 它只回文字。貼圖、照片、語音一律不回應。
- 你的網址是公開可連的。Google Apps Script 不讓程式讀取請求的標頭,所以這一版無法驗證訊息是不是真的來自 LINE。實際影響:別人若猜到你的網址,可以讓你多用一點 Gemini 額度,但沒辦法讓你的帳號去回覆你的客人——因為回覆需要 LINE 當下才會發出的一次性代碼。
- 免費額度有上限。用量大的時候會回不出來,這是額度問題不是設定問題。另一種回不出來是模型本身當下全球過載——不是你的用量造成的,處理方式見〈如果卡住〉。
- 它是一步一步做完才回覆:先問 Gemini,等它回答,再送回 LINE。所以回覆通常慢個一兩秒。LINE 官方建議這類程式改成「先答應、再處理」,那需要多一層設定,這一版刻意不做——多那一層會讓安裝變難,而這一版的目標是先讓你跑通一次。
- 你的官方帳號後台可能會出現
request_timeout之類的統計。只要你手機收得到回覆,那是統計數字,不是壞掉。 - 有兩個上限是程式寫死的,改
PROMPT沒有用:①每一則回覆的長度上限(程式碼裡的maxOutputTokens)②送去 LINE 的訊息太長會被截短(這是 LINE 平台自己的上限,不是我們加的)。 - 還有一條安全規則:「只根據
KNOWLEDGE裡的內容回答,沒寫的就說不知道」——這是為了不讓它自己編出價格和規則,我們刻意把它放在最後、要它最優先。🔴 但要跟你說實話:這一條是寫給 AI 的指示,不是程式鎖。你如果在PROMPT裡下一個正好相反的命令,它有可能就不照這條走了。所以請不要那樣寫。 - 「回答控制在三句話以內」只是預設,不是規定——你在
PROMPT裡叫它回答詳細一點,它通常就會照你的。
這些不是缺陷,是這一版刻意的範圍。
如果卡住
常見原因可能包括:
- 登入了不同的 Google 帳戶。
- API Key 建立在另一個專案。
- 指令碼屬性的名稱打錯字(大小寫也要一樣)。
- 改完程式碼只按了儲存,沒有重新部署。
- 「誰可以存取」沒有選「所有人」。
- 「執行身分」選成「存取網頁應用程式的使用者」——特徵:手動執行都成功,但真訊息進來時執行紀錄半秒內失敗、點進去沒有任何內容。改回「我」+新版本重新部署。
- 權限、配額或模型設定不一致。
- 模型當下全球過載:執行紀錄出現「
Gemini 回傳 503」加上「high demand」字樣,每則訊息拖到一分鐘以上——這不是你裝錯。
不要先重建整套 LINE。
「503/high demand」有現成解法:到 AI Studio 的模型選單挑另一顆 flash 系列模型,把它顯示的確切名稱填進指令碼屬性 GEMINI_MODEL(就是 Step 4 那張表的可選欄位;存了立刻生效,不用重新部署)。這一版的預設 gemini-3.1-flash-lite,就是在前一代預設塞車時這樣實測換出來的(2026-08)。
請進入 05|AI Troubleshooter,從最後一個確定成功的步驟開始。
下一步
進入 04|看到第一個 AI 回覆。