| 你需要 | 說明 |
|---|---|
| 你自己的 LINE 帳號 | 手機上正在用的那個即可 |
| 你自己的 Google 帳號 | 一般 Gmail 帳號即可 |
| 一支裝了 LINE 的手機 | 最後測試要用它收第一個 AI 回覆 |
| 大約 5–20 分鐘 | 照著做一次到底;中斷也沒關係,平台上做完的都會留著 |
勾了會自動更新;進度只存在本頁開著的期間(刻意設計)。
已經有官方帳號的人,直接到步驟 2。
這一步會把「能接程式的通道」打開。全程在中文介面。




Token=你的程式代表這個帳號發訊息的憑證。這是全程唯一要進 LINE Developers Console(英文介面)的一步,照字面按就好。

AQ. 開頭;開頭不同不代表錯)。全程免費,不需要信用卡——若畫面要你選付費方案,你走錯頁了,回第 1 點的網址重來。


Google Apps Script(GAS)=Google 免費提供、能讓一小段程式有自己網址的地方。不用會寫程式,整段複製貼上。
Ctrl+A)→ Delete 全部刪掉。Ctrl+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 大部分問題都會說不知道)'));
}

「指令碼屬性」=這個專案的設定欄位;秘密放這裡,不會出現在程式碼裡。先在下面備齊值(Token/Key 已從步驟 3、4 帶過來),再照表逐格貼。
| 屬性名稱 | 值 | 複製值 |
|---|---|---|
LINE_ACCESS_TOKEN 必填 | (回步驟 3 貼上 Token) | |
GEMINI_API_KEY 必填 | (回步驟 4 貼上 API Key) | |
KNOWLEDGE 必填 | (在上面填 KNOWLEDGE) | |
PROMPT 可不填 | (沒填,會用預設語氣) | |
GEMINI_MODEL 可不填 | (沒填,會用預設 gemini-3.1-flash-lite) |
貼完 Token/Key 後,隨手再複製一段別的文字把剪貼簿蓋掉;Windows 有開剪貼簿記錄(Win+V)的,順手刪掉裡面的秘密。

LINE_ACCESS_TOKEN、GEMINI_API_KEY、KNOWLEDGE 都寫「已填」。有 ❌ 就回步驟 6 檢查屬性名稱拼字。

https://script.google.com/macros/s/… 網址已複製。可以順手貼進下面這格備用。/exec 結尾——結尾不是 /exec(例如網址裡有 library)=複製到別格了,回完成視窗拿「網頁應用程式」那串。


同頁的「自動回應訊息」「加入好友的歡迎訊息」「聊天」開不開由你決定——它們跟機器人回覆不衝突(2026-08 實測)。想讓客人只收到 AI 的回答,就把自動回應關掉。注意:自動回應開著時,你測試收到的可能是罐頭回應而非 AI——測試時以回覆內容判斷。


用手機把自己的官方帳號加好友(Console 的 Messaging API 分頁有 QR code),照順序傳兩句。第一次測試先用步驟 6 的範例四行,通了再換自己的內容。
已複製 ✓
v2 候選|動線正典=2026-08-26 從零實走全程(環境:Windows+Chrome、繁中介面、個人 Gmail/LINE 帳號;未涵蓋:企業帳號、行動裝置瀏覽器安裝)。基線計數見交付紀錄。
MVP 專用官方 LINE;裝之前/裝之後都能點