# **【AI 圖片文字記帳 Line Bot: 自動記帳寫入 Google Sheet】ft. n8n 藥單辨識模板** :::info - 參考模板:藥單辨識 - Line Bot 個人記帳助理 1. 設置 Line Bot 2. n8n Webhook 3. Swift message/image 分流處理資料 4. 提取去重使用欄位 5. 比對資料是否已存在 6. Switch 判斷 7. 改自動回應訊息/圖片 8. 正式部署 ::: <br/> 一開始在 threads 看到有人分享 n8n藥單辨識 [Extract Structured Data from Medical Documents with Google Gemini AI](https://n8n.io/workflows/5917-extract-structured-data-from-medical-documents-with-google-gemini-ai/) 的模板 好像可以把裡面的圖片辨識節點拿來用? 自己常常忘記記帳,月底想不起來刷卡外的花費,於是萌生了做一個可以口述文字/圖片辨識記帳機器人的想法 因為是要隨時回傳的,這次部署在 Zeabur 相關文章: [【Zeabur 架設 n8n: 手把手教你做自動化 TSMC 產業分析助理】](https://hackmd.io/@workcata/S1rCw_y8lg) [【LINE Bot + Google Sheets, 部署到 Reader】](https://hackmd.io/@workcata/HyygFyMmel) 另外想感謝 [Darrell TW](https://www.darrelltw.com/),原本寫的比較雜,他幫我 review 後簡化很多。他的網站分享蠻多n8n知識的,想研究可以挖寶一下 <br/> ## 參考模板:藥單辨識 先用 postman 測試 Zeabur 能不能收到圖片 找了張圖片上傳 [Postimages](https://postimages.org/) Webhook 點 listen for event ![截圖 2025-07-18 15.35.41](https://hackmd.io/_uploads/H1kYKdwIxe.png) 到 Postman,選 POST + 貼上 test url Header ![截圖 2025-07-18 15.34.49](https://hackmd.io/_uploads/HkCNtdwUlg.png) Body ![截圖 2025-07-18 15.34.53](https://hackmd.io/_uploads/rJ0XtuP8lx.png) Send -> 回到 Webhook 確定有收到 ![截圖 2025-07-18 15.35.49](https://hackmd.io/_uploads/BJvRF_vUle.png) <br/> ## Line Bot 個人記帳助理 ![截圖 2025-07-21 17.36.10](https://hackmd.io/_uploads/r1eQ5YjLel.png) ### 1. 設置 Line Bot 登入 [Line Developers](https://developers.line.biz/zh-hant/) 創一個 provider -> create new channel 選 Messaging API -> 填寫 Channel 資訊 ![截圖 2025-06-10 00.47.48](https://hackmd.io/_uploads/S1wUe5Vmxl.png) ![1752924144889](https://hackmd.io/_uploads/S18WxZtIxe.jpg) ![截圖 2025-06-10 00.48.45](https://hackmd.io/_uploads/H1zqe5V7lx.png) ![截圖 2025-07-18 15.44.04](https://hackmd.io/_uploads/H1nIidPLgg.png) ![1752924178587](https://hackmd.io/_uploads/Sk87e-YLge.jpg) Line 有更新,現在需要進到 [LINE Official Account Manager](https://manager.line.biz/) 啟用 API 點進設好的 Provider -> 右上角齒輪 設定 -> Messaging API -> 啟用 ![1752924210081](https://hackmd.io/_uploads/HJEwlWFIll.jpg) ![1752924228171](https://hackmd.io/_uploads/SyY8gWKLge.jpg) ![1752937988384](https://hackmd.io/_uploads/H1u7I4FUee.jpg) ![1752924293036](https://hackmd.io/_uploads/ryGjeWFLll.jpg) 創建成功 ![1752825662365](https://hackmd.io/_uploads/rJytxFDLee.jpg) 回到 line,會看到設好的 line bot ![1752938064241](https://hackmd.io/_uploads/HyVDIEFUle.jpg) 現在再回到 [Line Developers](https://developers.line.biz/zh-hant/) 重整 剛才的 Provider 就會有資料了 ![1752924391240](https://hackmd.io/_uploads/SJceb-YIxg.jpg) ### 2. n8n Webhook n9n 新增第一個節點 test 的網址複製下來 -> 按 listen for the test event ![1752938180511](https://hackmd.io/_uploads/Hy1ywEt8ex.jpg) 回到 [Line Developers](https://developers.line.biz/zh-hant/) 點 Messaging API settings 把 n8n Webhook test-url 貼上 -> verify 出現 success 代表有成功收到 PS 下方 use Webhook 要打開 ![1752924424667](https://hackmd.io/_uploads/rJ2zZZKIeg.jpg) 點選 Listen for the test event (接下來都是在測試環境,傳任何訊息都要先點一次) -> line bot 發送訊息 Webhook 就會出現了 line bot 回覆的訊息先不管它,最後再來改 ![截圖 2025-07-18 16.34.33](https://hackmd.io/_uploads/BkyEwKDUxg.png) 回到 n8n Webhook 節點有收到資料就可以繼續 ![1752938153624](https://hackmd.io/_uploads/ByphUNtIel.jpg) ### 3. Swift message/image 分流處理資料 新增分流節點 分出收到的訊息是文字 text / 圖片 image ![1752936195768](https://hackmd.io/_uploads/rysM14tUgg.jpg) #### - text_openai ![截圖 2025-07-21 17.51.10](https://hackmd.io/_uploads/ryGj6KsIel.png) 設一個 set 提取 text ![1752936230799](https://hackmd.io/_uploads/SkqVy4K8xl.jpg) 接 AI Agent prompt ```= 分析出資訊: 先判斷是否為記帳相關明細、發票,若不是則不需要處理,直接停止所有流程。若是記帳相關,分析出六個資訊: 1. 日期(如果只提到'今天',用{{ $now.format('YYYY-MM-DD') }} ) 2. 通路 3. 通路類型(只能為:便利商店、個人用品店、量販超市、傳統市場、網路購物、藥局、五金百貨、餐廳小吃店、醫療院所、3C商場、航空客運、軟體儲值、加油交通儲值、線上課程、電信公司)4. 花費明細 5. 金額 6. 類別(只能為:家用、正餐飲食、飲料甜點、生活用品、美妝、衣服飾品、交通、娛樂、電信、醫療、3C、軟體、學習、旅遊)。若通路類型判斷為航空客運,類別一定為旅遊。若無法判斷或沒資訊,請填入'DN',每格都要有資料。請輸出為 JSON 格式,例如:{\"日期\": \"...\", \"通路\": \"...\", \"通路類型\": \"...\", \"花費明細\": \"...\", \"金額\": \"...\", \"類別\": \"...\"}" ``` 下面 Chat Model 點開,放 api key + 選模型 ![截圖 2025-07-19 15.04.32](https://hackmd.io/_uploads/BJt976OLgg.png) ![截圖 2025-07-19 15.34.33](https://hackmd.io/_uploads/Byfo9T_Ulx.png) 設一個 Structured Output Parser,指定輸出格式 ![截圖 2025-07-21 18.01.32](https://hackmd.io/_uploads/BknWe9sLgx.png) #### - image_openai ![截圖 2025-07-21 17.51.10](https://hackmd.io/_uploads/ryGj6KsIel.png) 設一個 Https 回到 [Line Developers](https://developers.line.biz/zh-hant/) 點 Messaging API settings 滑到下面,產生一個 Channel access token 複製下來 Send Headers 打開 Name: Authorization Value: Bearer Channel access token ![1752828770753](https://hackmd.io/_uploads/S1fFitD8ll.jpg) ![1752936295331](https://hackmd.io/_uploads/Hy0_14F8gx.jpg) 之後一樣接 AI Agent #### - image_gemini PS 這裡只是想試試藥單辨識模板的做法,如果不想用 gemini,可以直接跳過這段,最後我的模板只接 AI Agent ![截圖 2025-07-19 16.08.02](https://hackmd.io/_uploads/SJ9_MA_Uel.png) 設一個 Https 這裡改用 gemini 回到 [Line Developers](https://developers.line.biz/zh-hant/) 點 Messaging API settings 滑到下面,產生一個 Channel access token 複製下來 Send Headers 打開 Name: Authorization Value: Bearer Channel access token ![1752828770753](https://hackmd.io/_uploads/S1fFitD8ll.jpg) ![1752936295331](https://hackmd.io/_uploads/Hy0_14F8gx.jpg) 設一個 Extract from File 從檔案中擷取出特定的資料內 ![截圖 2025-07-19 15.23.08](https://hackmd.io/_uploads/rkGl_auLel.png) 設一個 set (prepare for AI) 只提取裡面的 data ![截圖 2025-07-19 15.23.35](https://hackmd.io/_uploads/rJaf_6dUge.png) 設一個 Https image post 到gemini ```= https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent ``` ![截圖 2025-07-19 16.08.34](https://hackmd.io/_uploads/HJLcfRdIxl.png) 設一個 code 整理 請 gpt 幫我寫的 ```= return items.map(item => { // 1. 提取 JSON 數據 if (item.json.candidates?.[0]?.content?.parts?.[0]?.text) { const textContent = item.json.candidates[0].content.parts[0].text; // 移除 Markdown 的 ```json\n 和 \n``` 標記 const jsonString = textContent.replace(/^```json\n/, '').replace(/\n```$/, ''); try { const parsedData = JSON.parse(jsonString); // 將解析後的數據合併到 item.json 中 item.json = { ...item.json, ...parsedData }; // 可選:刪除原始的 Gemini API 響應結構,如果你不再需要它 delete item.json.candidates; delete item.json.usageMetadata; delete item.json.modelVersion; delete item.json.responseId; } catch (e) { console.error("Error parsing JSON from Gemini text content:", e); // 處理解析錯誤,例如設置一個錯誤標誌或保留原始數據 } } // 2. 提取 replyToken (如果 Gemini API 的輸出中也包含 LINE 的 body/events 結構) // 這通常發生在 Line Trigger 直接連接到 Gemini 節點時 if (item.json.body?.events?.[0]?.replyToken) { item.json.replyToken = item.json.body.events[0].replyToken; delete item.json.body; // 刪除原始的 body 結構 } return item; // 返回處理後的項目 }); ``` ![截圖 2025-07-19 16.08.34](https://hackmd.io/_uploads/SklyQAu8ex.png) <br/> ### 4. 提取去重使用欄位 為了防止自己重複紀錄,這裡提取去重使用欄位,之後用來比對 ![截圖 2025-07-21 18.24.43](https://hackmd.io/_uploads/S1tqScsUle.png) <br/> ### 5. 比對資料是否已存在 ![截圖 2025-07-21 18.26.10](https://hackmd.io/_uploads/S1BCSqiLll.png) 寫入資料前,比對 google 去重使用欄位是不是一樣 一樣 -> 不寫入 不一樣 -> 寫入 設一個 google sheet ![截圖 2025-07-22 09.49.43](https://hackmd.io/_uploads/B1qHAwhLxx.png) 設一個 get rows in sheet 假設有資料,output 會讀到 ![截圖 2025-07-19 21.10.37](https://hackmd.io/_uploads/BJy_tGYLll.png) 接著要判斷,這次新資料的去重使用欄位,是否存在於 get rows in sheet 的去重使用欄位 一開始我直接用 if 判斷,發現不能這樣做,就先提取到 list,再做比對 之後 Darrell 教我使用了 Aggregate 設一個 Aggregate ![截圖 2025-07-22 10.36.34](https://hackmd.io/_uploads/B1FvFunUee.png) 設一個 merge_all 把要比對的兩份資料合起來 選 combine position ![截圖 2025-07-22 10.37.36](https://hackmd.io/_uploads/r1O5Kun8eg.png) <br/> ### 6. Switch 判斷 我希望不管寫入或不寫入,都要回傳訊息,Line Bot 使用當個會話的 replytoken 來 reply 一開始我用 if 判斷,要回傳資料發現 Line Bot 的 replyToken 只能使用一次,但 n8n 會每個分支都跑過一遍,就改成用 userId POST, Darrell review 幫我改成 Switch,方便很多 ![截圖 2025-07-22 10.40.28](https://hackmd.io/_uploads/rk3Q5u2Lgg.png) 邏輯是 ```= 去重使用欄位沒有重複 -> 寫入,回傳"✅ 記帳成功 <明細> " 去重使用欄位 = 'DN-DN-DN-DN' -> 不寫入,回傳"不相關明細或圖片,不會計入" (前面下的prompt,判斷不出來,所有欄位會是DN,去重使用欄會出現'DN-DN-DN-DN') 去重使用欄位 正則表達配對 '^.*-DN-DN-DN$' -> 不寫入,回傳"不相關明細或圖片,不會計入" (我有時候手殘,打到一半就按到送出,去重使用欄會出現'2025-01-01-DN-DN-DN) 去重使用欄位 = '---' ->不寫入,回傳"不相關明細或圖片,不會計入" (傳不相關照片時,有可能會全空值,去重使用欄會出現'---') 去重使用欄位重複 -> 不寫入,回傳"⚠️ 此筆資料已記錄過,不會重複記帳" ``` ![截圖 2025-07-22 10.48.27](https://hackmd.io/_uploads/BJC0R_nIle.png) ![截圖 2025-07-22 10.58.09](https://hackmd.io/_uploads/BkR1JY2Ule.png) #### - ✅ 記帳成功 <明細> 設一個 google sheet ![截圖 2025-07-22 11.27.52](https://hackmd.io/_uploads/SyzUSthIle.png) 設一個 Https url = https://api.line.me/v2/bot/message/reply Send Headers 打開 Name: Authorization Value: Bearer Channel access token Name: Content-Type Value: application/json Body json ```= { "replyToken": "{{ $('Webhook').item.json.body.events[0].replyToken }}", "messages": [ { "type": "text", "text": "✅ 記帳成功: {{ $('Merge_all').item.json['去重使用'] }}" } ] } ``` ![截圖 2025-07-22 11.29.58](https://hackmd.io/_uploads/r1G0rFnLxx.png) ![截圖 2025-07-22 11.33.28](https://hackmd.io/_uploads/HkqcLF3Lge.png) #### - 不相關明細或圖片,不會計入 設一個 Https url = https://api.line.me/v2/bot/message/reply Send Headers 打開 Name: Authorization Value: Bearer Channel access token Name: Content-Type Value: application/json Body json ```= { "replyToken": "{{ $('Webhook').item.json.body.events[0].replyToken }}", "messages": [ { "type": "text", "text": "不相關明細或圖片,不會計入" } ] } ``` ![截圖 2025-07-22 11.24.02](https://hackmd.io/_uploads/rJ8vNF3Uxx.png) ![截圖 2025-07-22 11.33.59](https://hackmd.io/_uploads/Skv2UKnLgx.png) #### - ⚠️ 此筆資料已記錄過,不會重複記帳 設一個 Https url = https://api.line.me/v2/bot/message/reply Send Headers 打開 Name: Authorization Value: Bearer Channel access token Name: Content-Type Value: application/json Body json ```= { "replyToken": "{{ $('Webhook').item.json.body.events[0].replyToken }}", "messages": [ { "type": "text", "text": "⚠️ 此筆資料已記錄過,不會重複記帳" } ] } ``` ![截圖 2025-07-22 11.25.30](https://hackmd.io/_uploads/ByHTVKhUee.png) ![截圖 2025-07-22 11.32.48](https://hackmd.io/_uploads/BkL_IY2Lll.png) ### 7.改自動回應訊息/圖片 進到 [LINE Official Account Manager](https://manager.line.biz/) 點頭像可以直接改 ![截圖 2025-07-19 22.27.57](https://hackmd.io/_uploads/SyCOomYIel.png) ![截圖 2025-07-19 22.27.34](https://hackmd.io/_uploads/BJLwjmt8xe.png) 自動回應訊息 ![截圖 2025-07-19 21.59.44](https://hackmd.io/_uploads/SJRyS7YUgx.png) ![截圖 2025-07-19 22.28.24](https://hackmd.io/_uploads/ByA9j7KLlx.png) 基本檔案 -> 改背景圖 ![截圖 2025-07-19 22.01.16](https://hackmd.io/_uploads/H1QzjXKIge.png) ![截圖 2025-07-19 22.26.32](https://hackmd.io/_uploads/BJOmo7KIee.png) ### 8. 正式部署 回到 n8n 上面的 Active 打開 ![截圖 2025-07-22 11.36.57](https://hackmd.io/_uploads/SJOwwFhIlg.png) Webhook,複製 Production URL ![1752936963480](https://hackmd.io/_uploads/rkOMzEY8lx.jpg) 回到 [Line Developers](https://developers.line.biz/zh-hant/) 點 Messaging API settings 改 n8n Webhook url -> verify 出現 success 代表有成功收到,部署成功 ![1752935431325](https://hackmd.io/_uploads/B1CLpXt8xe.jpg) 月底拉個樞紐分析圖表,就會很清楚花費佔比了 ![截圖 2025-07-19 22.59.05](https://hackmd.io/_uploads/BJYRMEtLxx.png)