TWICE 機器人 —— 九個有個性的 Discord AI 機器人
把韓國女團 TWICE 的九位成員,各自做成一個獨立的 Discord 機器人。她們會用各自的個性 跟你聊天、彼此之間也會互相搭話,主控機器人還能播音樂和管理伺服器。
緣起
一開始只是想做一隻會回話的 Discord 機器人。但既然要做,就想做得有趣一點 —— 與其做一隻冷冰冰的機器人,不如做「一群有個性的角色」,讓她們不只回應我、 還會彼此互動,把伺服器變得熱鬧。於是目標變成:九個獨立人格 + 會自己聊起來的群體。
實際畫面
九位成員
每位成員都是一個獨立的角色,有自己的暱稱、顏色、emoji 與完整人設:
| 成員 | 個性 | |
|---|---|---|
| 🐰 | 娜璉 Nayeon | 最年長的姐姐,元氣、愛撒嬌、自信,常自誇最可愛 |
| 💚 | 定延 Jeongyeon | 帥氣直率、有點毒舌但很照顧人、有潔癖,跟娜璉是歡喜冤家 |
| 🍑 | Momo 모모 | 舞蹈擔當,呆萌又愛吃,日本三人組之一 |
| 🌸 | Sana 사나 | 開朗愛撒嬌,招牌動作 샤샤샤,熱情愛抱抱 |
| 👑 | 志效 Jihyo(主控) | 隊長,可靠成熟、歌聲超強,大家鬧時負責主持秩序 |
| 🐧 | Mina 미나 | 文靜優雅學過芭蕾,氣質沉穩,隱藏的遊戲宅 |
| 🦅 | 多賢 Dahyun | 隊上的開心果,搞笑反應快,招牌老鷹之舞 |
| 🎨 | 彩瑛 Chaeyoung | 有藝術天分會畫畫寫詞,又酷又有個人風格 |
| 🐶 | 子瑜 Tzuyu | 來自台灣的忙內,外貌擔當,高冷但內心善良貼心 |
人格系統有多「深」
每位成員不是只有一句個性描述,而是一整套設定(全寫在 personalities.json,改它不用動程式):
- 打招呼、口頭禪、關鍵字觸發回應(聽到「可愛」「累了」會講不同的話)
- 萬用回應(沒打到關鍵字時)、主動搭話台詞
- 甚至 針對其他特定成員的專屬吐槽台詞
例如娜璉跟定延是出了名的「歡喜冤家」,設定裡就寫了她們互嗆的專屬台詞:
娜璉 →「定延妳又在擺那張臭臉了齁~😏」 定延 →「娜璉妳安靜零點五秒會怎樣嗎哈哈😏」
正因為「設定」和「程式」徹底分開,要新增一位角色、或調整誰的個性,都只要改 JSON、 完全不用碰程式邏輯。
開發歷程:遇到的問題與怎麼解決
做這個專案最有趣(也最頭痛)的,不是讓機器人講話,而是讓「九隻機器人」乖乖共處。
問題一:機器人會無限互相回話、把頻道洗版
讓成員之間能互相搭話後,馬上踩到雷:A 回 B、B 又回 A,永遠停不下來,瞬間洗爆頻道。
解法:把「被動回應」關掉 —— 機器人收到「另一隻機器人」的訊息時直接忽略, 成員之間的接話改由程式主動觸發。再加上兩道煞車:連環接話深度上限(最多接 2 層) 和同頻道冷卻時間,徹底斷開無限迴圈。
問題二:免費 AI 額度一下就爆
接上 Google Gemini 後,九隻 bot 動不動就各自呼叫 AI,免費額度很快就撞到上限。
解法:想盡辦法省呼叫——限制連環接話深度、被點名時一次只帶一位出場、 用機率控制隨機接話的頻率,而且 AI 呼叫失敗時就「安靜不發言」, 而不是硬補一句台詞。把「聊天的熱鬧感」和「API 成本」之間調出一個平衡點。
問題三:同一句話被記九次
我讓每隻 bot 都記住頻道最近的對話當作 AI 的上下文,結果發現一則人類訊息會被 九隻 bot 各自記一次,記憶瞬間被灌爆。
解法:用訊息 ID 做去重,同一則訊息只記一次。
問題四:AI 有時失敗、有時答非所問
免費 AI 偶爾會逾時或回奇怪的東西,不能讓機器人因此壞掉或亂講話。
解法:設計三層優雅降級——先試 AI;AI 失敗就用關鍵字回覆; 連關鍵字都沒打到,就回一句「欸我剛剛恍神了,再說一次好嗎」的萬用句。 這樣機器人永遠不會崩、也永遠不會答非所問。
# 三層 fallback:AI → 關鍵字 → 「沒聽清楚」萬用句
reply = None
if ai.ai_enabled():
reply = await ai.generate_ai_reply(persona, history)
if not reply:
reply = persona.keyword_reply(clean) or _busy_line(persona)
問題五:斜線指令重複出現兩次
部署後發現每個指令在 Discord 裡都列了兩次,原來是「全域指令」和「每伺服器指令」 同時存在。解法:同步到伺服器後,把舊的全域指令清掉。
問題六:怎麼讓它「像真人」而不是機器人
純粹秒回會很出戲。解法:回話前加上「輸入中…」指標和隨機停頓,模擬打字的節奏; 還會把回覆裡提到的名字自動轉成真正的 @ 提及(並避免自己 tag 到自己)。
一個關鍵的設計決定:從關鍵字進化到 AI
最初版本其實完全不接 AI,個性是用「關鍵字 + 預寫台詞」做的。但我一開始就把 「產生回覆」這件事獨立成一個函式,所以後來要升級成真正的 AI 對話時, 幾乎只改了那一個地方,其他都不用動。這讓我體會到「把會變動的部分隔離出來」 這個設計習慣有多重要。
技術架構
- Python + discord.py,九隻 bot 跑在同一支程式裡(用 asyncio 並行),透過共享註冊表互相搭話
- AI 層:Google Gemini(
gemini-2.5-flash-lite),用 system prompt 鎖定角色扮演 - 人格系統:個性全寫在
data/personalities.json(個性、口頭禪、關鍵字、互動回應),改設定不用動程式 - 音樂:yt-dlp + ffmpeg;yt-dlp 故意不鎖版本,因為 YouTube 常改規格要保持更新
- 部署:Docker 打包,可放到 Railway 等平台 24 小時運行(電腦關機也不斷線)
- 安全:所有 Token / 金鑰放
.env,用.gitignore確保不外洩
新的想法 / 未來計畫
- 長期記憶:現在只記得當下頻道最近幾則,想讓成員記得跟特定使用者的互動
- 語音回覆:接 TTS 讓成員真的「開口說話」,不只打字
- 更省成本:用快取或更小的模型,讓 AI 對話開更大也不怕爆額度
- 這個「優雅降級 + 設定與程式分離」的架構,之後想套用到別的專案上
我學到的東西
- 非同步程式設計(
async / await)與同時管理多個連線 - 串接 LLM API、設計 prompt 做角色扮演,以及失敗時的 fallback 策略
- 在「功能」與「成本 / 穩定性」之間做取捨
- 用 Docker 打包並部署成 24 小時運行的服務