我如何用 Vibe Coding 設計 TallyTrip:從旅程脈絡到收據 OCR 與多人分帳
A. 不是又一個旅遊 App,而是一個資料脈絡問題
我並不想做一個「旅遊 App」。更精準地說,TallyTrip 的起點是一個資料脈絡問題:一趟旅行明明是同一件事,但它的資料卻很容易在不同工具裡被拆成很多互相不知道彼此存在的片段。
旅行前,景點可能存在 Google Maps,行程草稿可能寫在 Google Docs 或 Notion,旅伴討論散落在 LINE 或 Messenger。旅行中,票券變成截圖,收據留在皮夾或相簿,臨時改行程的理由留在聊天訊息裡。旅行後,真正要算錢時,又打開另一個分帳 App 或試算表,把人、金額、幣別、付款者與分攤對象重新整理一次。
這裡真正麻煩的不是「沒有工具」。相反地,工具其實太多了。問題在於每個工具都只理解旅程的一小段:地圖理解地點,文件理解文字,聊天工具理解對話,相簿理解圖片,分帳工具理解金額;但沒有一個共同結構負責回答:「這些東西其實都屬於同一趟旅行。」
這也是我在設計 TallyTrip 時最先確定的方向:不要先把它想成行程表、相簿、收據工具或分帳工具,而是先把「一趟 Trip」當成資料的核心邊界。實作上,我把 Trip 當成整個產品的主要容器。這裡的容器不是畫面上的分類標籤,而是資料歸屬的起點:一筆行程、一張票券、一張收據、一筆花費,最後都要能回答「它屬於哪一趟旅行」。
所以 TallyTrip 裡的 Trip 不只是旅程名稱和日期。它更像是一個共同資料夾,負責把旅伴、行程內容、旅途中產生的素材,以及後續的花費與結算串在一起。使用者看到的是一趟旅行;系統背後要做的,是讓這些不同型態的資料不要失去彼此的關係。
這也是為什麼我沒有把所有東西都直接塞進 Trip 裡。例如,行程、交通、備註、地點、臨時決策這類內容,會被視為 Trip 底下的旅程節點。它們代表「這趟旅行中發生或被記下的一件事」。花費則是另一種更嚴格的資料,因為它牽涉到金額、幣別、付款者、分攤對象與結算結果;收據和照片也有自己的生命週期,包含上傳狀態、縮圖、原始檔案、OCR 辨識結果,以及是否已經被套用成正式花費。
換成比較白話的說法:Trip 是這趟旅行的主脈絡,但不同資料仍然要由適合的地方負責管理。行程內容歸行程內容,花費歸花費,附件和 OCR 歸附件流程;它們不是混在一起,而是透過 Trip 被串回同一趟旅行。這個設計讓 TallyTrip 不只是「把資料集中顯示在同一頁」。真正重要的是,資料在系統裡本來就知道自己和哪趟旅行有關。前端可以根據這些關係整理成時間線、卡片、花費列表或旅後回看;後端則保存比較穩定、可驗證的資料真相。
我在用 Vibe Coding 推進這個專案時,也很常回到這個問題:AI 可以很快幫我把一個功能長出來,但如果資料邊界沒有想清楚,功能越多,系統反而越容易變成一團「頁面需要什麼就補什麼」的資料堆。
所以 TallyTrip 的第一個產品判斷,其實是技術判斷,也是產品判斷:先把一趟旅行重新視為一個完整的 Trip。當 Trip 成為核心單位後,行程、旅伴、照片、票券、收據、花費與結算才有機會被放在同一條脈絡裡理解,而不是等旅行結束後,再由使用者自己從聊天紀錄、相簿、文件與分帳工具裡重新拼回來。
B. 這個專案也是一次 Vibe Coding 實驗
TallyTrip 同時也是一次 Vibe Coding 實驗。這裡的 Vibe Coding,不是把一句「幫我做一個旅遊分帳網站」丟給 AI,然後期待它自己變成完整產品;比較接近的做法是,我先把問題、限制與產品判斷寫清楚,再讓 AI 協助把它們拆成規格、資料模型、流程、介面狀態與測試邊界。
這種開發方式最大的好處,是可以很快把模糊想法變成可以檢查的東西。原本腦中只有「旅行資料很散」這種感覺,經過一輪輪對話後,會被迫拆成更具體的問題:Trip 應該保存什麼?旅伴權限怎麼判斷?收據 OCR 的結果能不能直接變成正式花費?多幣別分帳要保存哪些金額?哪些只是畫面呈現,哪些才是後端資料真相?
但這也是 Vibe Coding 最容易出問題的地方。AI 很擅長快速長出功能草案,卻不一定會自然維持長期架構邊界。如果沒有一直校正,它可能會把顯示用資料當成正式資料、把某個頁面的需求寫進核心模型,或是把原本應該分層處理的流程塞在同一個地方。短期看起來很快,長期會變成難以維護的資料堆。
所以在 TallyTrip 裡,我一直把自己的角色放在幾個判斷點上:問題是否真的存在、功能是否該做、資料應該由誰負責、權限應該在哪裡判定,以及哪些技術細節不能因為方便就暴露到前端。像 Trip、Expense、Media、OCR 這些東西,表面上都屬於同一趟旅行,但在實作上仍然要分清楚責任。Trip 負責旅程脈絡,Expense 負責正式花費與結算,Media 負責附件與收據流程,OCR 結果則只是花費草稿的來源,不是正式帳目本身。
安全邊界也是同樣的邏輯。AI 可以協助設計 OCR、檔案上傳或第三方服務串接流程,但不能因此把 provider key、OAuth token、上傳憑證或權限判斷放到不該出現的地方。對使用者來說,畫面上只是「上傳收據、自動辨識、確認花費」;對系統來說,背後必須確保前端只拿到它需要的狀態,真正的權限驗證、背景任務派發與資料寫入仍由後端控制。
因此,TallyTrip 的 Vibe Coding 過程比較像是在加速「設計可以被攤開檢查」這件事。AI 幫我更快產生草稿,也更快暴露矛盾;我則負責把這些草稿拉回產品目標、資料邊界與安全限制裡。最後真正有價值的不是程式碼生成得多快,而是每一次生成之後,都能更清楚看見這個系統應該長成什麼樣子。
C. 我不想把 TallyTrip 做成單純的行程表或帳本
如果只從功能表面看,TallyTrip 很容易被理解成「行程表加分帳」。但我在設計時一直避免把它收斂成這兩種既有工具的組合,因為這樣會錯過真正的問題。
行程表通常關心的是「接下來要去哪裡」。它適合整理日期、時間、地點、交通與備註,讓使用者在旅行前規劃,或在旅行中快速查看下一步安排。帳本或分帳工具則關心「這筆錢怎麼算」。它擅長處理付款者、分攤對象、金額、幣別與最後誰欠誰。
這兩種工具都重要,但它們各自只看見旅行的一部分。
旅行實際上不是一份靜態行程,也不是一張支出列表。它比較像是一條連續發生的資料流:出發前有規劃,旅途中有臨時調整、截圖、票券、收據與補記,旅後還要核對花費、回看內容、整理整趟旅行發生過什麼。如果產品只是一份行程表,花費和收據就會變成外部資料;如果產品只是一個帳本,行程、票券和決策脈絡又會被排除在外。
所以我在 TallyTrip 裡把「旅程內容」和「正式花費」分開處理,但讓它們都能回到同一個 Trip。
比較白話地說,旅程內容是「這趟旅行中發生或被記下的事」;正式花費則是「需要被核對、分攤與結算的帳務資料」。一個交通安排可以是旅程內容,一張票券可以是附件,一筆餐費可以是正式花費;它們不需要被硬塞進同一張表,也不應該互相取代,但它們都應該能在同一趟旅行裡被找到。
這個取捨會直接反映到實作上。TallyTrip 裡有用來描述旅程節點的資料,也有獨立承接正式花費與結算的資料。前者比較接近時間線與上下文,後者則需要處理金額、幣別、付款、分攤與匯率。這樣做的目的,是避免把「看起來像同一張卡片」的東西,在資料層也混成同一種東西。
因為對系統來說,行程卡片和花費卡片的責任其實不同。行程卡片重點是讓人理解當時發生了什麼;花費卡片則必須能被驗證、被重算、被納入結算。前端可以把它們放在相近的位置呈現,讓使用者感覺它們都屬於同一趟旅行;但後端不能因為畫面上長得像,就把資料真相混在一起。
這也是我不想把 TallyTrip 做成單純行程表或帳本的原因。
我想處理的是兩者之間的斷裂:行程和花費本來就發生在同一趟旅行裡,收據和票券也不是孤立檔案。它們應該能各自保有正確的資料結構,同時又被 Trip 這個共同脈絡串起來。這樣使用者在旅途中補記資料、旅後核對花費,或之後回看整趟旅行時,才不需要重新在不同工具之間拼接上下文。
D. Trip 為什麼是核心資料單位
我最後選擇用 Trip 當核心資料單位,是因為旅行中的多數資料,其實都不是只屬於某一個人,也不是只屬於某一個功能頁。它們更自然的歸屬是「某一趟旅行」。
如果用使用者當核心,資料會很快變得破碎。因為多人旅行裡,同一筆行程、同一張票券、同一張收據或同一筆花費,通常都和多個旅伴有關。它可能是 A 建立的、B 付款的、C 也要分攤的,最後大家都需要在同一趟旅程裡看得到。這種資料如果只掛在個人底下,系統就要不斷補共享、同步與權限判斷,最後反而更複雜。
如果用行程表當核心,也會遇到另一個問題:不是所有旅程資料都適合被理解成行程。收據、照片、票券、臨時決策、分帳結果,很多都不是「某個時間要去哪裡」,但它們仍然是這趟旅行的一部分。反過來,如果用帳本當核心,行程、素材與決策脈絡又會被壓縮成花費的附屬資料,旅行就只剩下金額。
Trip 則比較接近真實使用情境。使用者心裡想的不是「我要管理一份表」,而是「我要整理這趟旅行」。所以在 TallyTrip 裡,Trip 是共同容器,也是權限、內容、附件、花費與結算可以互相對齊的基準。
實作上,這個判斷會反映在資料關聯裡。旅程內容會回到 Trip,旅伴會回到 Trip,正式花費會回到 Trip,附件與 OCR 結果也會回到 Trip。它們各自有自己的資料結構和生命週期,但都會保留「我屬於哪一趟旅行」這個關係。
這樣做有一個很實際的好處:系統可以清楚分辨「資料由誰負責」和「資料屬於哪個脈絡」。
例如,正式花費由花費模組負責,因為它要處理付款、分攤、幣別與結算;附件與 OCR 由媒體流程負責,因為它要處理上傳狀態、縮圖、辨識結果與套用狀態;旅伴權限則由 Trip 的參與者規則負責,因為誰能看、誰能改、誰能被納入結算,都是一趟旅行內部的協作問題。
但不管是哪一種資料,它們最後都能透過 Trip 回到同一個脈絡裡。
這也是 Trip 作為核心資料單位的價值:它不是把所有資料混成同一種東西,而是讓不同資料在保有各自責任的同時,仍然能被理解成同一趟旅行的一部分。前端可以把它整理成時間線、卡片、花費頁或旅後回看;後端則不需要為了某個畫面,把資料真相寫死在單一呈現方式裡。
對 TallyTrip 來說,Trip 不是一個列表分類,而是整個產品的上下文邊界。只要這個邊界清楚,後面不論是行程協作、收據 OCR、多幣別分帳,或未來的旅後摘要,都可以沿著同一個核心往外長,而不是每新增一個功能,就重新發明一套旅行資料結構。
E. 出發前:先讓旅伴進到同一個上下文
在旅行開始前,最常見的狀態不是「大家已經有一份完整行程表」,而是每個人手上都有一些片段。有人存了景點,有人查了交通,有人截了票券資訊,有人記得某家餐廳,有人只是在聊天群裡丟了一句「這個好像不錯」。
如果這些片段一直留在各自的工具裡,後面就會開始出現同步成本。有人不知道最新版本在哪裡,有人找不到之前討論過的地點,有人以為某個安排已經確定,但其實只是聊天裡的一個提案。旅行還沒開始,脈絡就已經開始分裂。
所以 TallyTrip 在出發前的重點,不是先逼使用者填完一份完整行程,而是先建立一個共享 Trip,讓旅伴進到同一個上下文裡。
這裡的「共享」不是單純把一個連結丟給別人,而是讓系統知道這些人都屬於同一趟旅行。誰是建立者、誰可以管理旅伴、誰能查看內容、誰能編輯、誰會被納入後續結算,這些事情如果一開始沒有邊界,後面行程、附件、花費與分帳就很容易混在一起。
實作上,TallyTrip 會把旅伴關係放在 Trip 底下管理。每個旅伴不只是 email 或暱稱,而是有自己的角色、加入狀態與權限旗標。這樣做的目的,是讓「旅伴」不只是畫面上的成員列表,而是後續所有協作流程的基礎:能不能看這趟旅行、能不能修改內容、能不能被放進分帳計算,都可以回到同一套規則判斷。
這個設計對產品體驗也有影響。出發前的規劃不一定要一次完成,但只要 Trip 已經建立,旅伴也被帶進來,後續資料就有地方可以累積。今天先記一個景點,明天補一段交通,之後再加票券或備註,這些內容都不需要散在不同人的聊天紀錄裡。
對使用者來說,這只是「先開一趟旅行,邀請旅伴一起整理」。對系統來說,這一步其實是在建立後續所有資料的共同邊界。
因為只有先有這個邊界,後面的行程節點、照片附件、收據 OCR、正式花費與結算結果,才知道自己該掛回哪一趟旅行、哪些人能看到、哪些人能操作。旅行前的第一步不是把行程表填滿,而是先讓所有旅伴進到同一個 Trip 裡。
F. 旅途中:資料要能低摩擦地補進來
旅行中最不適合發生的事,就是為了整理資料而打斷旅行本身。
出發前可以慢慢規劃,但旅途中很多資料都是臨時產生的:剛買完票的截圖、店家給的收據、路上臨時換的交通方式、朋友先代墊的一餐、突然決定加入的景點、或是某個「等一下再記」但很容易被忘掉的小細節。這些資料通常不會在最理想的時間點出現,也不會以乾淨的格式出現。
所以旅途中真正重要的不是把每個欄位都填得很漂亮,而是讓資料能先被低摩擦地補進 Trip。
這也是 TallyTrip 在旅途中設計上的重點:使用者可以先把內容放進來,再讓系統逐步把它整理成可用的資料。照片、票券、截圖和收據可以先作為附件保存;一段臨時備註可以先成為旅程節點;一筆支出可以先記下基本資訊,之後再補齊付款者、分攤對象、幣別或收據。
這裡有一個很重要的取捨:前端可以用時間線、卡片、列表或 Modal 讓使用者快速操作,但後端不能只保存「畫面看起來需要的樣子」。因為畫面會變,資料真相不能跟著漂。
例如,同一張收據在畫面上可能出現在某個行程卡片下,也可能出現在花費詳情裡,旅後回看時又被整理進另一種檢視方式。如果後端只保存「它現在顯示在哪一區」,未來頁面一改,資料關係就會變得很脆弱。比較穩定的做法是保存它實際屬於哪個 Trip、是否關聯到某個旅程節點、是否已經被用來建立正式花費,以及它目前的上傳或辨識狀態。
這也是為什麼 TallyTrip 會把旅途中產生的資料拆成不同責任來處理。旅程節點負責描述「這趟旅行中發生或被記下的事」;附件流程負責處理照片、票券和收據的上傳狀態、縮圖與檔案關聯;花費流程負責處理金額、付款、分攤與結算。它們在畫面上可以被整合呈現,但在資料層不能混成同一種東西。
從使用者角度看,這件事應該很簡單:我在旅途中想到什麼、拍到什麼、買了什麼,就能先放進這趟 Trip。從系統角度看,背後要做的是保留足夠完整的關係,讓這些資料未來可以被重新整理、核對和回看。
低摩擦不代表資料可以隨便存。真正好的低摩擦,是讓使用者當下不用承擔太多整理成本,但系統仍然保存足夠清楚的資料邊界。這樣旅途中不會因為記錄而被打斷,旅後也不會因為當時記得太少而無法還原。
G. 為什麼收據 OCR 是流程的一部分
我把收據 OCR 放進 TallyTrip,不是因為 OCR 本身很新,也不是想把它做成「自動記帳」的噱頭。真正的原因比較務實:旅行記帳最大的阻力,常常不是計算,而是輸入。
人在旅行中不太可能每次吃完飯、買完票、搭完車,就坐下來完整填一筆花費。金額、幣別、付款者、分攤對象、日期、備註,這些欄位在系統裡都很重要,但在旅行現場,它們都是額外負擔。尤其是多人旅行時,一筆支出可能還要考慮誰先付、誰要分、是否用當地貨幣、最後用哪個幣別結算。流程一重,使用者就會想「晚點再記」,然後晚點通常就忘了。
收據 OCR 在這裡的價值,是先降低第一步的輸入成本。
對使用者來說,最自然的動作可能只是拍一張收據,或從相簿選一張票券截圖。TallyTrip 可以先把這張圖片放進 Trip,再透過 OCR 嘗試讀出商家、日期、金額、幣別或品項等資訊。這些資訊不會直接變成正式帳目,而是先成為一份可編輯的花費草稿。
這個設計很重要。OCR 結果一定可能出錯,尤其旅行中的收據格式很多,語言、幣別、稅額、折扣和手寫資訊都可能影響辨識。如果系統把 OCR 結果直接寫成正式花費,看起來很自動,實際上卻會污染帳務資料。TallyTrip 的做法是讓 OCR 只負責「預填」,正式花費仍然要由使用者確認、修正後才建立。
實作上,我把這段流程拆成幾個階段,而不是讓前端直接拿圖片去呼叫 OCR 服務。前端只負責送出 OCR 請求和顯示狀態;後端會先驗證使用者是否有權限操作這個 Trip 或附件,再建立 OCR job,交給背景任務處理。辨識完成後,結果會被保存成 OCR result,前端再把它帶回花費表單裡。
換成比較白話的說法:使用者看到的是「上傳收據,等待辨識,確認花費」;系統背後其實分成「建立任務、背景辨識、保存結果、等待 review、套用到正式花費」幾個步驟。
這樣拆的好處是,OCR 不會卡住主要操作流程。辨識需要時間,可能成功,也可能失敗,甚至可能判斷這張圖片根本不是可辨識的收據。這些狀態都應該被明確保存,而不是只在畫面上顯示一個 loading。當 OCR 失敗或不可辨識時,使用者仍然可以手動建立花費;當 OCR 成功時,它也只是幫使用者少填一些欄位。
這裡也有安全上的考量。OCR provider 的金鑰、背景任務派發、附件讀取權限,都不應該暴露到前端。前端不應該直接持有外部服務憑證,也不應該直接寫入 OCR 結果。這些流程必須由後端控制,因為 OCR 處理的不只是圖片,還可能包含收據上的消費資訊。
所以在 TallyTrip 裡,收據 OCR 不是一個獨立功能,而是花費流程的一部分。它的定位不是取代使用者判斷,而是把「從收據到花費草稿」這段最容易讓人放棄的輸入成本降下來。
旅行中能先拍下來,旅後能快速核對,最後再由使用者確認成正式花費。這才是 OCR 在 TallyTrip 裡真正要解的問題。
H. 多人分帳真正難的地方
多人分帳表面上看起來只是「誰欠誰多少錢」,但實作起來最麻煩的地方,通常不是最後那個數字,而是前面每一筆資料到底代表什麼。
一筆旅行花費至少會牽涉幾個問題:這筆錢原本是多少?用什麼幣別消費?誰先付款?付款的人實際付了多少?哪些旅伴要分攤?每個人分多少?最後要用哪個幣別結算?如果是多人旅行,還會再多一層問題:某個人只是一起看行程,還是真的要被納入分帳?
這些問題不能混在一起。
例如,「付款者」和「分攤者」不是同一件事。A 可以先刷卡付整桌晚餐,但最後由 A、B、C 三個人平均分攤;也可能 A 和 B 各付一部分,但只有其中幾位旅伴需要分這筆錢。如果系統只保存一個簡單的 payer 欄位,再加上一個 amount,就很快會遇到無法表達的情境。
多幣別會讓這件事更複雜。旅行中常見的情況是:原始交易用日圓、有人用台幣信用卡付款、最後旅伴希望用台幣或美元結算。這時候「原始交易金額」、「實際付款金額」和「結算金額」都可能不同。如果系統只保存換算後的結果,旅後回看時就很難知道當初到底發生了什麼;如果匯率日後更新,結果還可能跟著漂移。
所以 TallyTrip 的花費資料不是只存一個總金額。實作上,我會把正式花費、付款列、分攤列、結算資訊和匯率快照分開處理。正式花費保存原始交易脈絡;付款列描述誰實際付了多少;分攤列描述誰應該負擔多少;結算資訊則保存最後用什麼幣別計算。匯率也不是只拿最新值臨時計算,而是要在建立或更新花費時留下當下採用的換算依據。
換成比較白話的說法:TallyTrip 不只想知道「最後誰要付錢」,也要保留「這個結果是怎麼算出來的」。
這樣做的好處,是旅後核對時比較有跡可循。當某個旅伴覺得金額不對,系統不能只回他一個總數,而要能回到相關花費,看見原始幣別、付款方式、分攤方式與換算結果。分帳不是只要結論,還需要能解釋結論。
權限也是多人分帳裡容易被低估的部分。
在 TallyTrip 裡,旅伴不只是「在不在這趟旅行裡」而已,還會有查看、編輯、是否納入結算等差異。有人可能還沒正式加入,但已經會被納入預計分帳;有人可能曾經參與旅行,後來被停用,但歷史花費仍然需要保留。這些規則如果只靠前端判斷,很容易出現畫面上看不到、但 API 還能寫入的問題。
因此,分帳相關的邊界必須由後端控制。前端可以根據 API 回傳結果決定按鈕是否可用、欄位是否唯讀,但真正能不能新增付款列、能不能把某個旅伴加入分攤、能不能更新 archived 狀態下的花費,都應該在後端被驗證。
多人分帳真正難的地方,不是寫一個公式把金額除以人數,而是把付款、分攤、幣別、匯率、權限和歷史資料都保存成可驗證的結構。
只有這樣,分帳結果才不會只是一個看起來正確的數字,而是可以被回看、被核對,也能在旅程脈絡中被理解的帳務資料。
I. Vibe Coding 讓我更快看見架構問題
Vibe Coding 最直接的效果,是讓功能長得很快。但在 TallyTrip 這種狀態多、資料關係多、又牽涉權限與金額的產品裡,寫得快不是最重要的事。真正有價值的地方,是它能很快把架構問題暴露出來。
當我要求 AI 依照某個使用流程產生功能草案時,它通常能很快把頁面、API、資料欄位和基本邏輯拼出來。但也正因為生成速度很快,一些原本可能晚一點才會遇到的問題會提早浮上來:這筆資料到底是正式資料,還是畫面上的暫存狀態?這個欄位應該存在資料庫,還是前端根據既有資料推算?這個權限應該在前端關按鈕,還是後端直接擋掉?這個流程失敗時,資料會不會停在一個很難修復的狀態?
這些問題如果在傳統開發流程裡,可能會等到功能慢慢堆起來才發現。但在 Vibe Coding 裡,因為草稿來得很快,矛盾也來得很快。對我來說,這反而是好事,因為它逼我更早面對架構邊界。
TallyTrip 的開發過程中,我最常反覆校正的是「資料真相」這件事。
例如,前端可以把 Trip 內容整理成時間線,也可以把花費放進卡片、列表或旅後回看頁。但這些都是呈現方式,不應該反過來決定後端怎麼保存資料。後端要保存的是比較穩定的事實:這筆資料屬於哪個 Trip、是否關聯到某個旅程節點、誰建立了它、它目前是否可編輯、是否已經進入正式結算。畫面可以重組,但資料真相要能支撐重組。
這也是為什麼我在實作上把不同責任拆開。trips 負責 Trip、旅伴、旅程節點和權限脈絡;expenses 負責正式花費、付款、分攤、結算和匯率快照;media 負責附件、上傳狀態、收據 OCR 與 OCR 結果;console 則只負責頁面入口、API route 和 delivery layer。白話來說,就是不要讓「使用者在哪個頁面操作」決定「資料應該被誰擁有」。
Vibe Coding 在這裡很有幫助,因為它可以快速產出一種可能的切法,再讓我檢查這個切法是否合理。當 AI 把某段邏輯塞到頁面層,我就要問:這只是顯示需要,還是正式業務規則?當 AI 建議新增某個欄位,我就要問:這是資料真相,還是可以從既有資料推導出來?當 AI 快速補上一段 API,我也要檢查:權限驗證在哪裡?失敗狀態怎麼保存?測試有沒有覆蓋到跨模組邊界?
安全問題也是這個過程中特別需要守住的部分。像 OCR、檔案上傳、Google Drive 整合、背景任務這些流程,很容易在快速開發時為了方便而把細節往前端推。但實際上,前端不應該持有外部服務金鑰,也不應該直接決定某個使用者是否能讀寫 Trip 資料。前端可以發起請求和呈現狀態,真正的授權、任務派發、資料寫入和錯誤記錄,仍然要由後端控制。
所以我對 Vibe Coding 的看法不是「AI 讓我少想很多」。剛好相反,它讓我更快看到自己必須想清楚的地方。
當功能生成速度變快,架構判斷的重要性會變得更高。因為你可以更快得到一版能跑的東西,也會更快得到一版長期不能維護的東西。TallyTrip 的開發過程讓我很明顯感覺到:Vibe Coding 最有價值的地方,不是替我省掉設計,而是把設計問題提早攤在桌上,讓我能更快重構、收斂和驗證。
J. 如果旅行資料本來就發生在聊天裡
寫到這裡,其實會遇到下一個問題:如果 TallyTrip 的核心是把旅行資料收回同一個 Trip,那資料一定要從網站表單進來嗎?
很多旅行資料並不是在使用者打開某個工具、準備好填表時才產生的。更常見的情況是,它們直接發生在聊天裡。旅伴會在 LINE 群組裡傳景點、丟地圖連結、貼票券截圖、傳收據照片,也會用一句話完成很多決策,例如「這餐我先付」、「明天早上改搭計程車」、「這張票先放這裡」。這些訊息其實都帶有旅程脈絡,只是它們通常會被留在聊天紀錄裡,最後還是需要人工重新整理。
所以我有想過,TallyTrip 未來可以結合 LINE,讓 LINE 成為旅途中最自然的資料輸入載體。
比較理想的體驗不是每次都要求使用者打開網站填表,而是讓他可以在 LINE 裡用原本就習慣的方式留下資料。傳一張收據,TallyTrip 可以先把它視為待辨識的花費素材;傳一句「午餐 1200 日圓,我先付,三個人平分」,系統可以先建立花費草稿;傳一張票券截圖或地點連結,就先掛回目前 Trip 的素材或旅程節點。使用者之後仍然可以回到網站確認、修正、核對和結算,但最初的紀錄不必離開聊天場景。
這裡真正有趣的地方,不只是做一個 LINE Bot。如果只是把 LINE 當成另一個表單入口,價值其實有限。更重要的是,TallyTrip 需要能讓 AI 理解目前 Trip 的上下文,並且用受控方式操作後端能力。
這也是為什麼我會想到讓 TallyTrip 支援 MCP。MCP,也就是 Model Context Protocol,可以把系統裡的資料和操作能力,用比較標準化的方式提供給 AI agent。白話來說,它可以讓 AI 不只是回答文字,而是在有權限的前提下查詢 Trip、讀取待確認收據、建立花費草稿、列出缺漏資料,或協助整理旅後摘要。
如果把 LINE 和 MCP 結合起來,TallyTrip 的形態就會更接近「對話式的旅行資料中心」。LINE 負責承接使用者最自然的輸入,TallyTrip 負責保存正式資料真相,MCP 則提供一層受控的工具介面,讓 AI 可以在 Trip 脈絡中協助整理資料。
這也能延伸到原本我對 TallyTrip 的其他想像。旅後回顧不一定只是靜態頁面,而可以變成「幫我整理這趟旅行的每日重點」;分帳檢查不一定只是列表,而可以變成「還有哪些收據沒有確認」;素材整理不一定只是上傳附件,而可以變成「這幾張票券可能屬於第二天的交通」。使用者用對話提出意圖,AI 透過受控工具查詢和整理 Trip,最後仍由 TallyTrip 保存正式結果。
但這裡的邊界必須很清楚。AI 不應該直接把聊天內容變成正式帳目,也不應該繞過權限去讀取或修改 Trip。比較合理的流程是:AI 可以建立草稿、標記待確認事項、提醒缺漏資料、協助整理摘要;但正式花費、分帳結果、權限變更和敏感操作,仍然需要使用者確認,並由 TallyTrip 後端依照既有規則驗證。
這也會反過來要求 TallyTrip 的內部能力要更乾淨。後端不能只提供給網頁使用的零散 API,而是要把能力整理成清楚的工具邊界,例如建立旅程節點、建立收據 OCR 任務、建立花費草稿、查詢結算狀態、列出待處理項目。每一個工具都要有明確的輸入、輸出、權限檢查和錯誤狀態。
如果這件事做得好,TallyTrip 就不只是旅行後拿來整理的網站,而是一個能在旅行過程中持續接住資料的系統。使用者在 LINE 裡留下片段,AI 協助判斷這些片段可能是行程、素材、收據、花費還是待確認事項,TallyTrip 則把它們整理回同一個 Trip。
這個方向其實仍然延續 TallyTrip 一開始的核心判斷:重點不是再多做一個入口,而是讓一趟旅行的資料不要再被切碎。LINE 可以成為輸入場景,MCP 可以成為 AI 操作橋樑,但 Trip 仍然是最後的資料脈絡邊界。
K. 結語:旅行不只是行程表,也不只是帳本
回頭看 TallyTrip,我覺得它真正想處理的問題,一直都不是「旅遊 App 還缺什麼功能」。
旅行不只是行程表。行程表可以告訴你接下來要去哪裡、幾點出發、搭什麼交通工具,但它很難完整承接旅途中產生的票券、照片、收據、臨時決策和花費脈絡。旅行也不只是帳本。帳本可以算清楚誰付了多少、誰還要補多少,但它通常不在乎這筆花費發生在哪一天、對應哪段行程、背後有什麼旅程脈絡。
真正有價值的,是把這些資料放回同一趟旅行裡理解。
這也是 TallyTrip 一開始選擇以 Trip 為核心的原因。Trip 不是一個畫面分類,也不是單純的資料夾名稱,而是一趟旅行的上下文邊界。行程、旅伴、照片、票券、收據、花費、OCR 草稿、結算結果,都可以各自保有正確的資料結構,但最後仍然回到同一個 Trip。
這樣設計的好處,是使用者不需要在旅行結束後重新拼圖。資料在產生時就已經知道自己屬於哪趟旅行,之後不論要整理成時間線、花費列表、旅後回顧,或未來透過 LINE 和 AI 對話來查詢、補記、核對,都有同一個脈絡可以依附。
Vibe Coding 在這個過程中扮演的角色,是加速想法被拆開和驗證。它讓我更快產出規格草案、資料模型、介面流程和測試方向,也讓一些架構問題更早浮出來。但它沒有取代產品判斷。哪些資料是正式真相、哪些只是畫面呈現、哪些操作需要使用者確認、哪些能力不能暴露到前端,這些仍然需要人自己決定。
所以對我來說,TallyTrip 不是一次「用 AI 做出旅遊網站」的嘗試,而是一次重新整理旅行資料結構的實驗。
如果一趟旅行最後留下的,不只是散落在聊天紀錄、相簿、文件和分帳 App 裡的碎片,而是一個能被回看、核對、整理,也能被繼續延伸的 Trip,那 TallyTrip 想解的問題就成立了。