昨天已經讓 SubWise 能夠透過 Gemini 理解使用者的自然語言,例如輸入「我今天買咖啡花了80元」,Gemini 可以判斷這是一筆消費,並整理成結構化資料:
{
"type": "expense",
"category": "Food",
"amount": 80,
"item": "咖啡",
"date": "2026-08-13",
"note": null
}
但這裡有一個很重要的問題:AI 知道我花了 80 元,那然後呢? 如果每次都只能把結果顯示在 LINE 裡,這還稱不上真正的智慧記帳。
今天的目標,就是把這條資料流真正接起來:
LINE → Gemini AI → Expense JSON → Python 資料驗證 → Google Sheets
讓 SubWise 第一次真正完成「自動記帳」。Day 12 的核心任務有三個:建立 Google Sheets 寫入功能、建立 Expense Service 負責驗證 AI 回傳資料、把整個流程部署到 Render 並透過 LINE 實際測試。
為了避免把所有邏輯都塞進 app.py,另外建立 expense_service.py,專門負責「驗證 Gemini 的消費資料 → 呼叫 Google Sheets 寫入」:
def save_expense(data):
if not data:
return False
if data.get("type") != "expense":
return False
amount = data.get("amount")
if amount is None:
print("❌ 缺少消費金額")
return False
if data.get("date") is None:
print("❌ 缺少消費日期")
return False
雖然 Gemini 很聰明,但 AI 回傳的資料還是需要 Python 再驗證一次,例如確認金額能轉成數字、且大於 0:
try:
amount = float(amount)
except (TypeError, ValueError):
print("❌ 消費金額格式錯誤")
return False
if amount <= 0:
print("❌ 消費金額必須大於 0")
return False
這是今天很重要的架構概念:不要讓 AI 直接決定資料庫裡最後會存什麼——Gemini 負責「理解」→ Python 負責「驗證」→ Google Sheets 負責「儲存」。另外也把 item 與 note 整理成單一欄位(例如「咖啡|下午茶」),讓 Google Sheets 的資料更簡潔。
建立 test_save_expense.py,用固定資料測試,執行後成功看到:
🧪 SubWise Expense Service 測試
🚀 開始透過 expense_service 寫入 Google Sheets...
✅ 消費資料已寫入 Google Sheets
📌 測試結果:✅ 測試成功
代表 expense_service → google_sheets → Google Sheets 這條線在本機已經打通。
把程式部署到 Render 後,一連串問題才真正開始:
問題一:ModuleNotFoundError: No module named 'google'
本機虛擬環境已經安裝 Gemini SDK,但 Render 是重新根據 requirements.txt 建立環境——本機有安裝不代表 Render 有安裝,補上 google-genai 到 requirements.txt 後解決。
問題二:ModuleNotFoundError: No module named 'gspread'
同樣的原因,補上 gspread==6.2.1 到 requirements.txt 後,Render 才正常啟動。
問題三:Render 找不到 credentials.json
因為 credentials.json 被正確地放進 .gitignore(Google Service Account 金鑰不應該上傳到 GitHub),Render 自然也拿不到這個檔案。解決方式是改用 Render 的 Environment Variables,新增 GOOGLE_CREDENTIALS_JSON 存放憑證內容,讓本機繼續用 credentials.json、Render 則透過環境變數取得憑證。這是今天很重要的部署觀念:本機檔案可以存在,但部署環境的機密資料應該透過 Environment Variables 提供。
問題四:Extra data: line 1 column 12 (char 11)
一開始以為是程式邏輯錯誤,後來發現是 Render 裡的 GOOGLE_CREDENTIALS_JSON 貼錯了格式,修正 JSON 內容後解決。
問題五:Gemini 一度回傳 503 UNAVAILABLE
錯誤訊息顯示模型當下處於高流量狀態,這不是程式碼寫錯,重新測試一次後 Gemini 就正常回應了。這也提醒了一件事:第三方 API 發生暫時性錯誤,不一定代表程式有問題,Debug 時要先看清楚錯誤類型。
修正所有問題後,再次透過 LINE 傳送「我今天買咖啡花了80元」,Render Logs 依序顯示收到訊息、交給 Gemini 處理、解析成 JSON、判斷為消費資料、寫入 Google Sheets:
📦 Gemini JSON:{'type': 'expense', 'category': 'Food', 'amount': 80, 'item': '咖啡', 'date': '2026-08-13', 'note': None}
💰 Gemini 已辨識為消費資料
📝 開始寫入 Google Sheets...
✅ 消費資料已寫入 Google Sheets
打開 Google Sheets,真的看到 2026-08-13 | Food | 80 | 咖啡 這筆資料。🎉 成功!
| 問題 | 原因 | 解決方式 |
|---|---|---|
No module named 'google' |
Render 缺 Gemini SDK | 加入 google-genai |
No module named 'gspread' |
Render 缺 Google Sheets 套件 | 加入 gspread |
credentials.json 不存在 |
金鑰沒有上傳 Render | 改用 Environment Variable |
Extra data |
JSON 憑證格式貼錯 | 修正 GOOGLE_CREDENTIALS_JSON |
503 UNAVAILABLE |
Gemini 暫時高流量 | 稍後重新測試 |
| 本機成功、Render 失敗 | 本機與部署環境不同 | 分別處理依賴與機密設定 |
這也讓我更深刻體會到:部署不是把程式丟上雲端就結束,真正的部署,是要讓程式、依賴、環境變數、外部服務全部一起正常運作。
expense_service.py,負責驗證 Gemini 回傳資料google-genai、gspread 套件問題今天留下了幾個重要 Commit:19e92ee、45d8e2a、a6c97f5、62e52e1、333cfcb,完整記錄了今天從「程式跑不起來」到「LINE 真的可以把資料寫進 Google Sheets」的過程。
目前完整的資料管線:
LINE 使用者 → Gemini AI(多模態理解)→ expense service(資料驗證)→ Google Sheets(資料儲存)
這代表 SubWise 已經不只是聊天機器人,開始具備「感知 → 理解 → 判斷 → 執行 → 儲存」的 Agent 雛形。
在 LINE 輸入「我今天買咖啡花了80元」,SubWise 自動完成消費辨識,並將資料寫入 Google Sheets:
明天 Day 13,將把 Day 11 完成的查詢功能也一起接上 LINE Bot,讓「我這個月花多少?」「我有哪些訂閱?」不只是本機測試成功,而是真的能在 LINE 對話中直接使用。我們明天見!