前幾天的 SubWise 已經可以做到「新增記帳」「查詢資料」與「消費分析」。但做到這裡,開始遇到一個很自然的問題:如果我剛剛記錯了怎麼辦?
例如原本記了一筆「2026-08-20 / Food / NT$100」,結果後來發現其實應該是交通費。理想中的使用方式應該很簡單:「把 2026-08-20 那筆 100 元分類為 Other 的消費改成 Transport」,然後 SubWise 幫我找到正確紀錄並直接修改 Google Sheets。這就是 Day 21 想完成的事情:讓 SubWise 不只是「會記帳」,而是開始具備修改既有資料的能力。
這其實比想像中困難,因為「今天花 100 元買午餐」跟「把今天那筆 100 元改成午餐」這兩句話裡都有日期、金額、消費項目,如果只看關鍵字很容易被誤判成同一種意圖。因此第一個重要工作,是重新整理 Gemini 的意圖分類,在原本的 expense、subscription、query、analysis、chat 之外,新增 expense_update,代表「使用者正在修改已經存在的消費,而不是建立新的消費」。
在 SYSTEM_PROMPT 裡加入判斷規則:只要句子出現「改成」「修改」「記錯」「分類改成」「剛剛記錯了」這類操作詞,就優先判斷為 expense_update 而不是 expense。這個規則很重要,因為 SubWise 的核心不是單純讓 Gemini「分類文字」,而是讓 Gemini 成為一個真正的意圖解析器。
把修改既有消費的 JSON 格式定義清楚:
{
"type": "expense_update",
"date": "2026-07-31",
"amount": 55,
"field": "category",
"value": "Transport"
}
date 跟 amount 用來定位是哪一筆資料,field 是要修改哪個欄位,value 是要改成什麼。這樣 Python 就不需要理解自然語言,只需要執行 Gemini 已經解析好的結構化指令。
如果使用者說「把 55 元改成 Drink」,SubWise 不能直接把 Drink 寫進 Google Sheets,因為系統只支援 Food、Transport、Entertainment、Shopping、Bills、Health、Education、Subscription、Other 九種分類。因此新增 invalid_category 類型,讓 Gemini 能區分合法分類與不支援分類,由 Python 顯示友善提示:
⚠️「Drink」不是目前支援的消費分類。
目前分類有:
🍜 Food|餐飲 🚇 Transport|交通 🎮 Entertainment|娛樂
🛍️ Shopping|購物 💡 Bills|生活帳單 ❤️ Health|醫療保健
📚 Education|學習 🔔 Subscription|訂閱 📦 Other|其他
請問你想把它改成哪一個分類?
這代表 SubWise 並不是「AI 說什麼,我就寫什麼」,而是AI 負責理解使用者,程式負責守住系統規則。
完成 edit_expense(),先透過日期加金額找到可能符合的消費,再根據 field 決定要修改哪一欄:
column_map = {
"category": 2,
"amount": 3,
"item": 4,
"note": 5
}
這讓自然語言最後真的可以轉成 Google Sheets 的實際修改操作。
坑一:AI 把「修改」當成「新增」
一開始在 LINE 測試「把 2026-08-20 那筆 100 元分類為 Other 的消費改成 Transport」,Gemini 卻回傳 type: expense,SubWise 因此又新增了一筆消費,而不是修改原本的資料。這是一個非常典型的 AI Agent 問題:AI 有理解文字,但沒有理解「操作語意」,因此才有了前面重新設計 Gemini 意圖判斷規則的過程。
坑二:Python 測試成功,LINE 卻失敗
直接測試 edit_expense() 這個函式本身完全正常,能成功修改資料。但實際在 LINE 測試時,SubWise 又新增了一筆消費而不是修改。往上追查後才發現,真正的問題出在 Gemini 的意圖判斷(也就是坑一),而不是 edit_expense() 本身。這次除錯讓整個架構的概念變得更清楚:測試單一函式成功,只能證明那個函式成功,真正的 Agent 功能必須從 LINE → Gemini → Python → Google Sheets 整條鏈路測試。
修正 Gemini 意圖判斷後,測試四種不同的修改情境,全部成功:
修改分類:「把 2026-08-20 那筆 100 元分類為 Other 的消費改成 Transport」→ 分類已修改為 Transport
修改金額:「把 2026-07-31 那筆 55 元改成 120 元」→ 金額已修改為 120
修改成不存在的分類:「把 2026-07-31 那筆 120 元改成 Drink」→ 成功阻止,顯示分類不支援提示
修改消費項目:「把 2026-07-31 那筆 120 元改成午餐」→ 項目已修改為午餐
測試修改功能時,自然聯想到「那如果想直接刪掉呢?」測試輸入「把 2026-07-31 那筆 120 元刪掉」,先單獨測 Gemini,發現它已經能正確理解成:
{'type': 'expense_delete', 'date': '2026-07-31', 'keyword': None, 'old_amount': 120}
也就是說,目前 Gemini 意圖判斷跟 Google Sheets 的刪除邏輯都已經準備好,只差 app.py 還沒有把 expense_delete 這個 type 接起來。
但今天決定先停在這裡,因為如果現在繼續做刪除功能,還需要測試「找不到資料」「多筆相同金額」「防止誤刪」等情境,因此決定把它留到 Day 22,讓今天的成果聚焦在「修改」這一個主題上。
expense_update 意圖,區分「新增消費」與「修改既有消費」expense_update JSON 格式(date / amount / field / value)invalid_category 防呆,阻擋不支援的分類寫入edit_expense(),完成 Google Sheets 實際欄位修改expense_delete 意圖(實作留到 Day 22)目前修改既有消費的完整流程:
使用者「把 2026-07-31 那筆 55 元改成 120 元」
→ Gemini(expense_update)
→ Python(找到指定日期+金額的資料)
→ Google Sheets(修改 Amount)
→ LINE(✏️ 記帳修改成功!)
今天讓 SubWise 學會「改錯」,明天 Day 22 要完成的是「刪掉」——把今天已經驗證過、Gemini 能正確判斷的 expense_delete 意圖,正式接進 app.py,並處理找不到資料、多筆符合、防止誤刪等情境,讓 SubWise 補齊消費資料管理的最後一塊拼圖。