Side Project · AI Product Builder 作品展示

WiPPY

用自然語言規劃活動訊息,AI 幫你寫好、排好、推出去。

AI 驅動的活動管理與訊息推播平台。透過 7 階段 Harness Pipeline, 讓每一次 LLM 呼叫都可觀測、可恢復,並透過 LINE Bot 自動推播給對的群組。

知識庫
Agent 對話
推播企畫
發送紀錄
知識庫
知識庫 · load_context
活動知識庫管理
儲存活動背景資料、選書哲學、氛圍調性。每次對話都會自動載入作為 LLM 的 system prompt context。
  • 手動或 AI 萃取新增知識條目
  • 知識內容注入 Stage 1 load_context
  • 目標群組同頁管理,已選 2 個

給活動規劃者

不需要技術背景,只要會打字,就能讓 AI 幫你規劃好所有推播時間與內容。

適合對象:活動規劃者、社群管理者,無需程式知識
前置條件
✓
已登入 WiPPY 平台帳號
✓
WiPPY LINE Bot 已被邀請進入目標群組
✓
「活動目標群組」頁面可看到你的 LINE 群組
操作流程
01
建立活動
填入名稱、日期、目標群組
02
設定知識庫
活動背景、語氣設定、注意事項
03
Agent 對話
用自然語言描述需求,AI 生成草稿
04
審核批准
到「推播企畫」確認並批准
05
自動發送
時間到,系統自動推播到 LINE 群組
對話示範
Agent 對話 · 五月露營活動
你
我有一個漂書活動,五個朋友,五月一號要在匹客球場換書。我想在換書前這幾天(大概從今天到五一當天)在 LINE 群組裡推一些東西,讓大家進入狀態,因為這個活動的氣氛很重要——不是那種很熱鬧的活動,是比較安靜、有點文學感的東西。

我大概有幾個想法:
想提醒大家帶書、帶小卡,但不要太直白,感覺太像通知
想推一些有氣氛的東西,比如書摘或句子,讓大家慢慢有感覺
AI Agent

要創造這種「安靜且有文學感」的氛圍,關鍵在於「留白」與「隱喻」。不要把這當成一個「活動」,而是一場「默契的約定」。以下我為你設計了從 4/27 到 4/30 (活動前一天) 的引導邏輯與文案範例。這些內容請用一種「隨手分享」的口吻發在群組裡。


預熱時間表

4/27(一):關於「書架上的安靜」
  • 目的:讓大家開始意識到要帶書,但不說「請帶書」。
  • 文案:(發一張你書架的照片,或是網路上很有意境的閱讀角落圖)
    「這幾天整理書櫃,發現有些書讀完後,在架子上站了很久,安靜得像是在等什麼。突然想到,也許它們也在期待一趟新的旅行,去看看誰會是下一個翻開它們的人。」
4/28(二):關於「對話的痕跡」
  • 目的:隱晦帶出「小卡」的意義。
  • 文案:(分享一段書摘)
    「以前看書,總喜歡在空白處寫下當時的註解。後來覺得,如果能把這些心情留給下一個讀者,就像是和未來的某個朋友跨越時空對話。如果這兩天有人挑選書的話,不妨也留下一張小卡吧,算是給這本書的一點告別,或是給下一站的祝福。」
4/29(三):關於「流動的意義」
  • 目的:連結匹客球場與環境,強調靜謐感。
  • 文案:「這幾天一直在想,我們選在球場旁見面,在那種甚至還聽得見遠處揮拍聲的環境裡,讀著這些交換來的文字,應該會有一種很特別的沈澱感。書本像水一樣,就是要流動,才會有生命。」
4/30(四):關於「不期而遇的懸念」
  • 目的:為明天的「規則」埋下伏筆。
  • 文案:「明天要見面了。關於這些書怎麼到下一個人手中,我心裡有個小想法,想試著讓過程多一點點不確定性,讓一切順著當時的直覺發生。明天見面時再和你們說。」

你覺得這個節奏符合你們的朋友圈嗎?
確認沒問題的話,請讓我知道。

你
很好,放進排程
AI Agent
✅ 已將 4 則推播訊息加入排程!請到「推播企畫」分頁確認後點「批准」,訊息就會在指定時間自動發出。
常見問題
訊息到了時間卻沒發出去,怎麼辦?
最常見原因是忘記點「批准」。請到「推播企畫」分頁確認審核狀態是否為「已批准」。若已批准仍未發送,請確認 LINE Bot 是否仍在目標群組中,並到「發送紀錄」查看錯誤訊息。
AI 產出的內容不夠精準,怎麼改善?
在「知識庫」分頁補充更多背景資訊:活動特色、注意事項、目標受眾、語氣要求等。知識庫越詳細,AI 產出的內容就越符合期待。
可以針對不同群組發送不同內容嗎?
可以。在 Agent 對話中直接說明:「A 群組收到○○,B 群組收到△△」,AI 會幫你分開建立兩組排程。

為什麼要做 WiPPY?

讓活動主辦方專注於活動本身,僅需審查 AI 規劃的不同群組的客製推播文案、推播時機

Pain Points

  • 手動撰寫推播文案,耗時且難以維持一致性
  • 排程邏輯散落各處,缺乏可觀測性
  • 知識庫與歷史對話沒有系統性管理

WiPPY 的解法

  • 自然語言輸入,Agent 自動產生訊息計畫
  • 7 階段 Pipeline,每步可觀測、可恢復
  • Firestore 知識庫 + 對話歷史,context 有記憶

(非常簡陋的) Harness Pipeline

7 階段有狀態執行器。每次執行都建立一筆 harnessRun, 記錄當前 stage、LLM 呼叫次數與狀態,讓 debug 不靠猜。

01
load_context
平行查詢 Firestore,撈活動資料、知識庫、現有排程、歷史對話,組成 ContextEnvelope
展開詳情
負責檔案
contextBuilder.ts
待優化
  • 不同群組間調性不同,未來可從對話等紀錄各群組風格
  • 知識庫架構待擴充,以提供 LLM 更多活動相關資訊
  • 知識庫筆數過多,context 超過 token 限制
02
build_prompt
將 ContextEnvelope 注入 system prompt,拼接最近 10 輪對話,組成送給 LLM 的 messages 陣列
展開詳情
負責檔案
harnessOrchestrator.ts
待優化
  • 對話歷史過長,超過模型 context window
  • 待優化 System Prompt 中所有 Constraints 條件,使 llm 回覆合規合理內容
  • 三種 intent 共用同一 prompt,指令干擾
03
run_planner
呼叫 LLM;每次 run 最多 3 次呼叫(含 repair),捕捉 429 轉為結構化 llm_rate_limit_error
展開詳情
負責檔案
llmProvider.ts
待優化
  • 429 rate limit,轉為結構化錯誤後 run 失敗
  • LLM 回傳空字串或格式完全不符預期
  • max_llm_calls_exceeded(repair 耗盡 3 次配額)
04
parse_output
從 raw reply 推斷 intent:能解析 JSON 陣列 → plan_messages;含 EXTRACTED_KNOWLEDGE: 區塊 → extract_knowledge;否則 → general_chat
展開詳情
負責檔案
harnessOrchestrator.ts
待優化
  • LLM 同時輸出 JSON + EXTRACTED_KNOWLEDGE,intent 判斷衝突
  • intent 事後推斷,無法事前分流控制輸出格式
05
validate_output
驗證 plan_messages 欄位合法性(ISO datetime、sequenceOrder 不重複、content 非空);失敗自動送回 LLM repair,repair 後仍失敗整個 run 標記 failed
展開詳情
負責檔案
planValidator.ts
待優化
  • repair 後 LLM 仍回傳相同錯誤格式 → repair_failed
  • extract_knowledge 路徑不經過此 validator
  • validateExtractedKnowledge() 已實作但尚未接入
06
persist_effects
批次寫入訊息排程與知識庫(每批 ≤ 499 ops);所有寫入帶 runId 確保可追溯;session history 保留最近 20 筆
展開詳情
負責檔案
persistenceAdapter.ts
待優化
  • 多批 batch.commit() 無原子性,中途失敗導致部分寫入
  • Firestore 寫入配額超限(499 ops/batch)
  • 目前無補償邏輯,僅靠 runId 事後追溯
07
finalize_response
截掉回覆中的 EXTRACTED_KNOWLEDGE 區塊;若成功寫入排程則替換為固定確認文案;將 HarnessRun 寫為 completed
展開詳情
負責檔案
harnessOrchestrator.ts
待優化
  • EXTRACTED_KNOWLEDGE 區塊截取 regex 邊界條件
  • confirm 文案寫死,無法反映實際寫入結果差異
HarnessRun · 可觀測性
statusqueued → running → completed | failed
currentStageload_context → build_prompt → … → finalize_response
llmCallCount1 – 3(含 repair)
intentTypeplan_messages | extract_knowledge | general_chat
lastErrorrepair_failed / max_llm_calls_exceeded / …

Output Intent

parse_output 階段事後推斷 intent,決定後續行為。每種 intent 對應不同的副作用。

plan_messages

訊息排程計畫

產生訊息排程,寫入 Firestore 並準備推播至目標 LINE 群組。

副作用 → 寫入排程 + 觸發推播
extract_knowledge

知識萃取

從對話中萃取結構化知識,更新群組知識庫,供下次 context 載入使用。

副作用 → 更新知識庫
general_chat

一般問答

回答使用者的問題,純文字回覆,不觸發任何 Firestore 寫入。

副作用 → 無

v0.5 (preview) Known Issues

以下是程式碼層級已確認的限制,不是警告,是下個版本的優先工單。

v0.5 (preview)
2026-04-28
Known Issues — 程式碼層級已確認,非假設
knowledge validator 未接入
validateExtractedKnowledge() 已實作且有測試,但 orchestrator 只 import validateMessagePlan。knowledge 寫入前僅做 type 白名單過濾,不檢查 title / content 是否為空。
Phase 2
單一 prompt 承擔三種 intent
buildSystemPrompt() 只有一個,三種 intent 共用同一組 system prompt。intent 在 parse_output 事後推斷,而非事前分流,prompt 品質受限。
Phase 3
跨批寫入無原子性
persistEffects() 分批 batch.commit(),若第一批成功、第二批失敗,資料部分寫入,目前無補償邏輯,僅靠 runId 事後追溯。
Phase 2

Roadmap

分為技術強化與功能擴充兩條軸線,持續迭代。

技術強化
Phase 2 Recovery 與 Safety 強化
將 validateExtractedKnowledge() 接進 orchestrator 的 validate_output stage
建立分層錯誤分類(context_load_error / llm_rate_limit_error / persistence_error),每類有明確 retry 策略
persist_effects 多批寫入加補償邏輯,依 runId 重試未完成的批次
Scheduler / send path 套入相同的 retry + structured failure policy
Phase 3 LangGraph-ready
每種 intent 有獨立 prompt mode(plan_messages / extract_knowledge / general_chat 各自的 system prompt)
將每個 stage 顯式化為 tool contract(name、input/output schema、timeout、retry policy),為接 LangGraph 做準備
ContextEnvelope 生成器替換為 retrieval-augmented 版本,召回相關知識而非全量載入
明確標註 pause hook(pause_before_persist、pause_after_validate)對應 LangGraph human-in-the-loop 中斷點
功能優化及擴充
Phase 2 知識庫與 Prompt 優化
優化知識庫可輸入類型及輸入方式,擴增不同格式檔案及串接方式
優化現有 System Prompt 的約束條件、產出格式、錯誤處理
Phase 3 個人化風格與意圖判斷
增加各群組個人化風格蒐集及紀錄機制,以供 LLM 針對不同群組產出不同調性的推播內容
設計意圖判斷機制,判斷後以使用不同 System Prompt

Tech Stack

全端 TypeScript,GCP 原生部署。

Backend

Node.js 18+ TypeScript Express Firebase Admin SDK Firestore Google Genai SDK LINE Bot SDK Cloud Tasks date-fns-tz

Frontend

React 19 TypeScript Vite 8 Tailwind CSS 4 React Router 7 Firebase SDK 12

Infrastructure

Google Cloud Run Firebase Hosting Firestore Cloud Tasks LINE Messaging API