前幾天完成訂閱管理與資料查詢後,SubWise 已經能夠「記住」使用者的消費與訂閱資料。但如果只能做到回答「我這個月花了多少錢?」,其實還不算真正的智慧管理。
比起單純把資料列出來,我更希望 SubWise 可以進一步回答「這個月到底花最多錢在哪裡?」「哪一種消費是目前最大的支出來源?」「平均每天大約花多少錢?」,甚至提醒使用者「你的餐飲支出占了本月消費的 89.2%,是目前最主要的消費來源」。
因此今天的目標,就是讓 SubWise 從「資料查詢工具」開始進化成「消費分析助手」。今天主要完成四件事:建立 analysis_service.py、計算消費總額與分類比例、找出最高支出類別與平均每日支出、將分析結果交給 LINE 顯示並產生第一版智慧消費提醒。
整體架構:
使用者 → LINE Bot → Gemini(判斷使用者意圖:analysis)
→ analysis_service.py → Google Sheets(取得消費資料)
→ Python 分析(總支出/分類統計/分類比例/最高支出類別/平均每日支出)
→ 智慧消費提醒 → LINE 回覆
這裡有一個很重要的設計概念:Gemini 負責「理解使用者想做什麼」,Python 負責「真正計算資料」,避免把所有工作都交給 AI。
新增 analysis_service.py,從 Google Sheets 取得既有消費資料,再用 Python 進行統計,主要建立了四個核心函式:
1. 計算各消費類別的金額 — calculate_category_totals(records),把所有消費按照 Category 分組,例如 Food: 150, Transport: 50, Food: 120, Food: 120, Transport: 50 會彙總成 Food: 390, Transport: 100。
2. 計算各分類占比 — calculate_category_percentages(category_totals, total),例如總支出 930 元中 Food 花了 830 元,計算 830 ÷ 930 × 100 ≈ 89.2%。這個數字比單純告訴使用者「Food 花了 830 元」更有意義,因為使用者可以直接知道自己的 Food 支出已經占了將近九成。
3. 找出最高支出類別 — get_top_category(category_totals),透過 max() 找出金額最高的分類,以今天的測試資料來說就是 Food(NT$830,占比 89.2%),這也成為後面「智慧消費提醒」的重要依據。
4. 計算平均每日支出 — calculate_daily_average(total, period),今天是 8 月 16 日,本月總支出 NT$930 除以已經過的 16 天,得到「平均每日:NT$58」。比起只看到「本月花了 NT$930」,看到「平均每天約 NT$58」更容易建立對消費速度的直觀感受。
建立 get_expense_analysis(period="month"),把前面所有分析串起來:
Google Sheets → get_expenses() → filter_expenses() → calculate_expense_total()
→ calculate_category_totals() → calculate_category_percentages()
→ get_top_category() → calculate_daily_average()
最後整理成一個 Python Dictionary,今天實際測試得到:
{
"period": "month",
"total": 930.0,
"category_totals": {"Food": 830.0, "Transport": 100.0},
"category_percentages": {"Food": 89.2473, "Transport": 10.7527},
"top_category": "Food",
"top_amount": 830.0,
"top_percentage": 89.2473,
"daily_average": 58.125
}
這代表 SubWise 已經不只是「把 Google Sheets 讀出來」,而是開始對資料產生自己的分析結果。
建立 format_analysis_result(result),把 Python Dictionary 整理成適合 LINE 顯示的內容:
📊 SubWise 消費分析
📅 分析期間:month
💵 總支出:NT$930
📊 平均每日:NT$58
🔥 最高支出類別:
Food:NT$830 (89.2%)
📂 分類支出:
Food:NT$830 (89.2%)
Transport:NT$100 (10.8%)
SubWise 第一次算出真正的消費統計。從 Google Sheets 讀出來的原始資料,經過 Python 計算後,變成總支出、每日平均、分類占比這些對使用者有意義的數字,而不只是一筆一筆的流水帳:
今天最有趣的功能是 generate_spending_insight(result),讓 SubWise 開始不只是「報告數字」,而是嘗試對數字做出簡單的解讀,目前設定了三個層級:
雖然還不是複雜的 AI 理財模型,但已經具備一個很重要的概念:從資料 → 分析 → 解讀,這是 SubWise 從「記帳工具」往「智慧助理」發展的第一步。
光有 analysis_service.py 還不夠,因為使用者輸入「幫我分析這個月的消費」時,Gemini 必須判斷這既不是新增消費、也不是查詢明細,而是「進行消費分析」。因此把 SubWise 的資料類型擴充成五種:expense、subscription、query、analysis、chat。
測試「幫我分析這個月的消費」,Gemini 成功回傳:
{
"type": "analysis",
"target": "expense",
"period": "month"
}
Gemini 沒有直接去讀 Google Sheets,只負責回答「使用者想分析什麼?」,真正的資料處理還是交給 Python,讓 AI 與程式邏輯保持清楚的分工。
在 app.py 加入 from analysis_service import analyze_expenses,並在 process_ai_message() 新增 elif data_type == "analysis" 分支,讓 SubWise 收到 Gemini 的 analysis 意圖後,呼叫 analyze_expenses(data),最後將分析結果回傳給 LINE。
一開始測試時遇到 ImportError: cannot import name 'analyze_expenses' from 'analysis_service'。這個錯誤其實很有價值——當時 analysis_service.py 裡已經有 get_expense_analysis() 與 format_analysis_result(),但還沒有真正提供 analyze_expenses() 給 app.py 使用。問題不是 Google Sheets,也不是 Gemini API,而是 Service 層的函式名稱與 app.py 的呼叫名稱沒有對上,補齊函式後重新測試就成功了。
修正後重新測試「幫我分析這個月的消費」,成功看到完整流程:Gemini 判斷為消費分析 → analysis_service 分析 Google Sheets → 取得總支出 930、Food 830、Transport 100 → 產生完整的分析結果與智慧消費提醒,代表整條流程已經成功打通。
analysis_service.py,完成消費總額、分類統計、分類百分比、最高支出類別、平均每日支出五項計算format_analysis_result)generate_spending_insight,三層級判斷)analysis 意圖,資料類型擴充為 expense / subscription / query / analysis / chatapp.py 串接 Analysis Service,完成 AI → Service → Sheets 完整分析流程目前 SubWise 的消費分析只能看到「這個月花了多少」,但沒辦法回答「跟上個月比起來,我是花得比較多還是比較少?」。明天 Day 16,我將在 analysis_service.py 加入月份比較功能,讓 SubWise 能夠橫向比對不同月份的消費資料,找出支出的趨勢變化,讓消費分析從「單月快照」進化成「跨月趨勢」。我們明天見!