iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
自我挑戰組

從零開始的AI學習之路:非本科轉職的30天挑戰記系列 第 27

D27 | 我的Side Project 每拍呷--最後優化+Debug篇

  • 分享至 

  • xImage
  •  

今日進度13/14:

  1. 在生成報告跟語音檔的時候,加入「此次攝取的熱量,以時速4公里散步N分鐘就能消耗囉!」的訊息,參考用戶的體重去計算。
  2. 修改一些視覺的東西,例如字體放大、顏色區隔
  3. 修改狀態機,當用戶輸入非預期的內容時,可以引導到正確的流程

這幾天一直在思考能增加什麼小功能,可是又怕輸出太多太複雜,畢竟我的目標是要讓用戶一目瞭然!
後來就想到,可以增加一行是散步30分鐘(時速4公里)所消耗的熱量~

首先找到資料:
59KG以下:87.5大卡
60~69KG:105大卡
70KG以上:122.5大卡

所以假設用戶60公斤,吃了一包170大卡的餅乾,需要散步49分鐘才能消耗熱量~我是覺得這個蠻實用的!

再來就是優化狀態機流程,狀態機(State Machine)是一種程式設計模式,它用來管理程式的執行流程,特別是在需要根據**「當前狀態」和「接收到的事件」**來決定下一步動作時非常有用。

簡單來說,它就像一個邏輯地圖,告訴你的程式:
現在在哪裡(當前狀態): 例如,state 是 1,代表正在等待用戶輸入「性別」。
下一步要去哪裡(下一個狀態): 當用戶輸入了性別(事件),state 就會變成 2,下一步是等待「身高」。

  • 為什麼要用狀態機?
    不用狀態機的話,程式碼會變得非常複雜且難以維護。想像一下,如果你要用一大堆 if/else 條件來判斷用戶到底想做什麼,你的程式碼會變成一團亂。

  • 使用狀態機的好處是:
    邏輯清晰: 每個狀態都有明確的輸入和輸出,讓程式碼的流程一目了然。
    避免混亂: 它可以防止用戶在不對的時機做不對的事,例如:在要求輸入身高時,輸入份量。
    易於除錯: 當程式出錯時,你可以根據 state 的值,快速定位問題發生在哪個流程步驟。

  • 以我這隻程式為例:
    state = 0 (閒置狀態): 表示用戶已完成所有設定,正在等待他上傳圖片。
    state = 1 (等待性別): 新用戶或點擊「更新資訊」後,state 被設定為 1,此時程式只接受性別的 postback 事件。
    state = 2 (等待身高): 當收到性別 postback 後,state 變成 2,程式只接受數字形式的身高輸入。
    state = 3 (等待體重): 當收到身高輸入後,state 變成 3,程式只接受數字形式的體重輸入。
    state = 4 (等待份量): 當用戶上傳營養標示圖片並成功辨識後,state 變成 4,程式只接受「全部」、「一半」、「1/4」等份量輸入。
    state = 5 (文字輸入營養標示): 當用戶選擇「文字輸入營養標示」後,state 變成 5,程式會依序提示用戶輸入各項營養素的數值。

目前是在用def handle_text_message(event) 這支函式,要來調整函式內部的程式碼順序。
原本的程式碼是從上而下依序檢查,當遇到不符合的狀況時,它會繼續往下執行,造成邏輯漏洞。

  • 調整方案
    讓 handle_text_message 函式遵循更嚴格的「狀態機」邏輯,確保每個輸入都能被正確處理,或是在不符合預期時被明確地「拒絕」。
  1. 優先處理「特殊指令」
    把處理「更新資訊」、「文字輸入營養標示」和「拍照範例」這幾個特殊指令的程式碼,移到了 handle_text_message 函式的最前面。這樣做可以確保,不論用戶當前處於什麼狀態,這些指令都能被優先處理。

  2. 接著處理「對話流程」
    接著特殊指令之後,加入了更明確的狀態機判斷。程式會先讀取用戶的 current_state,然後直接進入對應的 if/elif 區塊。

例如,如果 current_state 是 2 (等待身高),程式碼會只處理身高輸入,並在成功後將狀態更新為 3。如果輸入不正確,會發送錯誤訊息並維持 state 在 2,讓用戶可以重新輸入。這就能避免在等待份量輸入時,用戶又重新輸入身高而導致流程混亂。

  1. 最後處理「新用戶」與「無效輸入」
    只有在用戶沒有任何 state、且資料不完整時,程式碼才會進入新用戶的設定流程。而所有不符合上述任何情況的輸入,都會被視為無效輸入,並發送一個通用提示訊息。

這個調整讓程式碼的邏輯變得更像是「優先執行 → 狀態機 → 預設處理」,這會讓整個 LINE Bot 的對話流程更穩定,也更容易除錯。


接下來我就做很多例外測試,像是故意不照著BOT提示輸入、輸入錯誤值、故意跟它聊天...等等。

結果,我抓到一個很好笑的Bug:

https://ithelp.ithome.com.tw/upload/images/20250901/20177974AbOgSmxGRX.jpg

上傳風景照居然能生成報告跟音檔!天哪~
這是因為,它辨識不到營養標示,就擅自把所有營養數據都變成0,(好啦不是擅自,是我程式沒寫好)
都是0的狀況下不會噴錯,依然會生成報告,報告因為不是寫死的、是生成式AI產生的,所以它會說:「你不需要花任何時間來消耗這些熱量」(我笑到併軌)

所以我想了兩個做法來防呆:

** 方案一:判斷 Gemini API 的回覆內容 **

優點: 程式碼最簡單、最快速實現。我們只需要在接收到 Gemini 回覆後,檢查 JSON 裡的關鍵數值(如:總熱量、脂肪等)是否為零或空值。如果是,就判斷為辨識失敗。

缺點: 缺乏精準的「非營養標示」判斷。如果用戶上傳一張完全空白的照片,或是一張數值非常低的圖片,它可能還是會被判斷為「沒有數據」,雖然邏輯正確,但訊息不夠直觀。

** 方案二:讓 Gemini 判斷圖片「是不是」營養標示 **

這個方案更進階,我們在給 Gemini 的指令中加入一個「預判」環節。

優點: 能夠更精準地判斷圖片類型。我們可以要求 Gemini 先判斷圖片是否包含「營養標示」,如果不是,就直接回覆一個特定的標籤或訊息,例如 {"is_nutrition_label": false}。這樣我們就能在程式碼中立刻判斷並給出更精準的提示:「您上傳的似乎不是營養標示」。

缺點: 流程稍微複雜一點,需要調整給 Gemini 的提示詞(Prompt),並要求它回傳兩種不同情境的 JSON 格式。這也可能增加 Gemini 辨識的複雜度,但對於提升用戶體驗來說,是值得的。

Gemini力推第二種方案,所以就來做看看囉!

程式碼主要修改點

  • 修改 handle_image_message 函式:
    在成功辨識出營養標示後,程式會立即檢查熱量、蛋白質、脂肪、碳水化合物、鈉這五個關鍵營養素的數值。
    如果其中任何一個數值為 null 或 0,程式會自動將用戶的狀態切換到「手動輸入」流程。
    這時,Bot 會回覆一個客製化的訊息,明確告知使用者哪一個營養素辨識失敗了,並提示他們輸入正確的數值。
    同時,Bot 會將這次辨識出的部分資料暫存起來,以便後續手動輸入時使用。

  • 修改 handle_text_message 函式:
    原本的 state == 5 狀態機,現在同時處理兩種情況:
    從頭開始的手動輸入: 透過圖文選單點選進入。
    部分辨識失敗的手動輸入: 從照片辨識流程中導引進來。
    這兩種流程會無縫接軌,Bot 會依序引導用戶輸入每一個缺失的欄位,直到所有數據都完整。
    這個改動讓你的 LINE Bot 變得更智慧,能更優雅地處理辨識不完整的狀況,而不是直接丟一個錯誤訊息給使用者。

修改完畢!現在傳風景照就不會出問題啦!

https://ithelp.ithome.com.tw/upload/images/20250901/20177974aS1Rwned3v.jpg

還有請長輩幫忙測試,長輩回饋使用起來順手、內容也好懂,真是太好了。
明天要來研究看看能否上線到GCP,加油!

最後來回顧一下兩周前的第一版到現在Final版:

第一版(Gradio介面):
https://ithelp.ithome.com.tw/upload/images/20250901/20177974ivcHbnpPln.png
https://ithelp.ithome.com.tw/upload/images/20250901/201779749Rs3aQojAM.png

Final版:
https://ithelp.ithome.com.tw/upload/images/20250901/20177974XjVqESUsS1.jpg
https://ithelp.ithome.com.tw/upload/images/20250901/20177974jFdkF5VqNl.jpg


上一篇
D26 | 【學習心得】Azure Speech服務~進階篇
下一篇
D28 | 我的Side Project 每拍呷--部署篇
系列文
從零開始的AI學習之路:非本科轉職的30天挑戰記30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言