iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
ChatGPT & Codex

AI 時代的輕量化開發:ChatGPT 打造 LINE 多模態記帳與續訂預警 Agent系列 第 27

[Day 27] 從「聽懂一句話」到「理解一段對話」:SubWise Context Engine

  • 分享至 

  • xImage
  •  

前言

前幾天的 SubWise 已經逐漸具備完整的功能入口——使用者可以透過 LINE 進行記帳、查詢、消費分析、訂閱管理,以及查看近期扣款提醒。但仔細想想,這樣的 Bot 還是存在一個問題:它真的「理解」使用者在說什麼嗎?

例如使用者先輸入「分析」,SubWise 顯示分析期間的選項;接著使用者只回「本月」。人類很自然會知道「本月」是在回答上一個問題,但如果 Bot 每次都只看「目前這一句」,它其實不知道「本月」到底代表什麼。因此 Day 27,決定讓 SubWise 開始具備最基本的對話上下文(Context)能力,目標是:記住使用者上一句話與上一個意圖,根據上一個意圖理解下一句話,並且在不需要 Context 的情況下,不要誤判使用者訊息。


今日實作實錄

一、建立 context_service.py

新增獨立的服務模組專門管理使用者的對話上下文,先採用最輕量的 Python 記憶體方式,而不是立刻導入 Redis 或資料庫等複雜架構。Context 的概念很單純——每個 user_id 對應一份目前的對話狀態,例如 {"last_message": "分析", "last_intent": "analysis"}

建立了四個基本操作:set_context() 儲存使用者的上下文資訊、get_context() 取得指定的 Context、get_all_context() 取得某位使用者目前所有的 Context(方便在 Render Log 中直接觀察除錯)、clear_context() 清除指定使用者的 Context,讓測試時可以重新建立乾淨的對話狀態。

二、加入 Intent 判斷

有了 Context 儲存機制還不夠,還需要知道使用者目前的訊息代表什麼「意圖」,因此加入 detect_intent(),測試結果:「分析」→ analysis、「查詢」→ query、「訂閱」→ subscription、「扣款提醒」→ reminder、「本月」→ None

這裡有一個很重要的設計:「本月」本身不被判定成新的主要功能,因為它比較像是「分析功能底下的條件」,所以「分析」會判斷出 analysis,但「本月」判斷出 None,這時就交給 Context 去理解。

三、讓 Context 真正參與意圖判斷

加入 get_contextual_intent(),邏輯是:如果目前訊息有明確意圖,就直接使用;如果沒有,就查看上一個意圖,判斷能不能合理沿用。例如上一個意圖是 analysis、目前訊息是「本月」或「今天」,都會沿用得到 analysis;但如果上一個意圖是 analysis、目前訊息是「查詢」,因為「查詢」本身已經有明確意圖,會直接判定成 query,而不是繼續沿用 analysis

四、建立 process_contextual_command(),串接既有服務

讓 Context 不只是「記得」,而是真的能執行功能。例如上一個意圖是 analysis、使用者輸入「本月」,Context 會將它理解成 analysis + month,然後實際呼叫既有的消費分析功能。這裡最重要的是:Day 27 並沒有重新製作一套分析系統,而是把 Context 接到 Day 16-26 已經完成的分析功能上,資料流程從「消費資料 → 分析服務 → 格式化結果」,擴展成「使用者對話 → Context → 判斷意圖 → 既有分析服務 → LINE 回覆」,這就是模組化架構開始發揮作用的地方。

五、正式接入 LINE Webhook

handle_message(event) 裡,先取得 LINE 使用者 ID(event.source.user_id),這一步很重要,因為 Context 必須知道「這段記憶到底是誰的」。接著儲存使用者最後一則訊息,再根據目前訊息與上一輪 Context 判斷意圖,如果成功判斷,就更新 last_intent。訊息處理的優先順序也調整成:Context → 固定指令 → Gemini

六、刻意讓 Context 判斷留在 Python,不消耗 Gemini 額度

這是今天設計上的一個重要考量:「分析」交給 Python 判斷、「本月」「今天」交給 Context 判斷,只有像「今天天氣真好」這種真正需要理解自然語言的訊息,才會交給 Gemini。這樣做的好處是:簡單、明確的多輪對話可以由 Python 本地邏輯處理,不需要消耗 Gemini API 額度——考量到 Day 26 才剛實測遇到過 Gemini 429 RESOURCE_EXHAUSTED 的額度限制,這次的設計也讓 SubWise 的架構更加節省 AI 呼叫。


測試流程

先透過 Python REPL 模擬多輪對話:手動建立 {"last_message": "分析", "last_intent": "analysis"} 的狀態,接著輸入「本月」,成功判斷出 analysis 並正常產生本月消費分析;測「今天」也成功進入 today 分析流程。最重要的一組測試是輸入「今天天氣很好」,結果正確得到 None,代表 Context 不會因為上一個意圖是 analysis,就把所有後續訊息都當成分析指令——這是一個很關鍵的防呆。

確認本地邏輯沒問題後,正式部署並在 LINE 上測試:

  • 分析 → 本月:輸入「分析」後顯示期間選項,接著直接輸入「本月」(不需要重新輸入「分析」),SubWise 成功理解上下文,回傳本月總支出 NT$4209、21 筆消費、平均每日 NT$150,並附上完整的最高支出類別、最高單筆消費、月度比較、消費洞察等資訊。
    https://ithelp.ithome.com.tw/upload/images/20260828/20178527MrpB7vj8zW.png
    https://ithelp.ithome.com.tw/upload/images/20260828/20178527Dk9aMVSbus.png

  • 分析 → 今天:同樣流程,輸入「今天」後成功回傳今日消費分析(總支出 NT$140,1 筆消費)。
    https://ithelp.ithome.com.tw/upload/images/20260828/20178527Vdp6m5a1TZ.png

  • 一般聊天防呆:在已經進行過分析操作之後,輸入「今天天氣真好」,SubWise 沒有誤判成消費分析,而是正常以聊天語氣回覆「是的,今天天氣真的很好呢!」代表 Context 只在合理範圍內生效,不會過度延伸。
    https://ithelp.ithome.com.tw/upload/images/20260828/20178527vyaLZVGzdn.png


今日完成

  • 建立 context_service.py,實作 set_context() / get_context() / get_all_context() / clear_context()
  • 建立 detect_intent(),判斷訊息的明確意圖
  • 建立 get_contextual_intent(),結合上一輪 Context 理解模糊訊息
  • 建立 process_contextual_command(),串接既有的分析/查詢/訂閱/扣款提醒服務
  • 取得 LINE User ID,將 Context 接入 handle_message()
  • 完成 Python 本地測試(分析→本月、分析→今天、一般聊天防呆)
  • 完成 LINE 實際測試,三組情境全部通過

目前 SubWise 的訊息處理架構:

使用者 → LINE → User ID → Context → Intent 判斷
→ 上下文指令(分析/查詢/訂閱/扣款提醒)→ 既有服務 → LINE 回


明日預告

Day 27 讓 SubWise 開始具備基本的對話上下文。明天 Day 28 將繼續往「自然語言理解」前進,讓使用者不一定要使用完全固定的指令,也能用比較自然的方式操作功能。

例如:

  • 「幫我看看這個月花多少」
  • 「我最近花錢多嗎?」
  • 「下個要扣款的是什麼?」

SubWise 將嘗試把這些自然語句轉換成對應的功能與參數。讓 Bot 從「你必須說對指令」,逐漸變成「你用自然的方式說,我來理解你的需求。」我們明天見!


上一篇
[Day 26] 讓 SubWise 不只會聊天:打造互動式首頁與固定功能入口
系列文
AI 時代的輕量化開發:ChatGPT 打造 LINE 多模態記帳與續訂預警 Agent27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言