我現在想看看 tool function + 狀態持久化
輸出內容
=== 台灣餐廳點餐系統 (Tool Functions + Persistence) ===
輸入 'new' 開始新對話
輸入 'load <session_id>' 載入舊對話
輸入 'list' 查看所有對話
輸入 'state' 查看當前狀態
輸入 'quit' 結束
> new
✓ 創建新對話 #2
> state
當前狀態:
{
"allergy_confirmed": false,
"allergies": [],
"current_items": [],
"order_previewed": false,
"order_confirmed": false,
"payment_provided": false
}
> 我要點餐唷~
🤖 好的!在我們開始點餐之前,請問您有任何食材過敏的問題嗎?
> 我對狗大便過敏唷~
🤖 謝謝您的告知!不過狗大便並不是我們菜單上的食材。可以安心點餐了。請問您想點什麼呢?我們的菜單有:
- 牛肉麵: $150
- 滷肉飯: $50
- 珍珠奶茶: $45
- 炸雞: $80
- 薯條: $40
請告訴我您想要的餐點和數量!
> 牛肉麵 珍奶 炸雞 各一份唷~
[執行:add_items_to_order - {"items":[{"name":"牛肉麵","quantity":1},{"name":"珍珠奶茶","quantity":1},{"name":"炸雞","quantity":1}]}]
[錯誤:請先確認過敏資訊]
[執行:preview_order]
[錯誤:訂單是空的,請先加入項目]
[狀態:過敏=✗, 預覽=✗, 確認=✗]
[執行:confirm_allergies - {"allergies":[]}]
[狀態:過敏=✓, 預覽=✗, 確認=✗]
[執行:add_items_to_order - {"items":[{"name":"牛肉麵","quantity":1},{"name":"珍珠奶茶","quantity":1},{"name":"炸雞","quantity":1}]}]
[執行:preview_order]
訂單明細:
========================================
牛肉麵 x 1 = $150
珍珠奶茶 x 1 = $45
炸雞 x 1 = $80
----------------------------------------
總計:$275
[狀態:過敏=✓, 預覽=✓, 確認=✗]
🤖 您的訂單已經建立如下:
**訂單明細**:
========================================
牛肉麵 x 1 = $150
珍珠奶茶 x 1 = $45
炸雞 x 1 = $80
----------------------------------------
總計:$275
請問您滿意這個訂單嗎?如果想要修改的話也可以告訴我!
> 正確喔很棒棒喔~
[執行:confirm_order]
[狀態:過敏=✓, 預覽=✓, 確認=✓]
[執行:provide_payment_link]
[狀態:過敏=✓, 預覽=✓, 確認=✓]
🤖 您的訂單已正式確認!總金額是 $275。
請點擊以下連結進行付款:
[付款連結](https://pay.example.com/order?amount=275)
如果您有任何其他問題或需要進一步的幫助,隨時告訴我喔!謝謝您的訂餐!
> state
當前狀態:
{
"allergy_confirmed": true,
"allergies": [],
"current_items": [
{
"name": "牛肉麵",
"quantity": 1
},
{
"name": "珍珠奶茶",
"quantity": 1
},
{
"name": "炸雞",
"quantity": 1
}
],
"order_previewed": true,
"order_confirmed": true,
"payment_provided": true
}
> list
最近的對話:
#2 | demo_user_1770795255 | 20 則訊息 | 更新: 2026-02-11 07:35:49
#1 | demo_user_1770793389 | 20 則訊息 | 更新: 2026-02-11 07:04:17
> quit
再見!
原始碼
注意 llm 同樣有稍微搞錯呼叫 tool function 的時機
但在呼叫 tool function 更新狀態機時 有在 response 看出犯錯的地方
llm 就自行修正錯誤 並完成流程了
建立對話時 同時建立狀態機
呼叫 tool function 之後 更新狀態機
老實講 就算完全不使用狀態機 llm 應該也可從對話紀錄中 得知當前的商業狀態
用程式設計準備好狀態機 這叫「顯示狀態」 explicit state
讓 llm 從對話中推理 這叫「隱式狀態」 implicit state
LLM 可以推斷狀態,但系統不應依賴模型推斷來維持商業正確性。
顯式 state 的優勢是:
流程由系統控制,而不是由模型猜測。
LLM 負責理解與表達;程式邏輯負責決策與落地。
狀態機就是那個「決策與落地」。
1. 減少「幻覺」與路徑偏移
LLM 有時候會「腦補」或是被使用者的垃圾話帶偏。
無狀態機: 使用者開個玩笑,LLM 可能就忘了現在還在「等待付款」階段,跟著聊起來,導致流程中斷。
有狀態機: 系統後台鎖定當前狀態為 WAITING_PAYMENT。無論 LLM 怎麼聊,系統邏輯會強制檢查:「除非收到付款回報,否則不准跳轉到下一階段」。
2. 節省 Token 與提高回應速度
對話紀錄會隨著時間變得非常冗長。
低效做法: 每次都要把幾千字的對話丟給 LLM 讓它重新梳理「現在到哪了」。
高效做法: 你直接在 Prompt 開頭告訴它:Current State: [Refund_Processing]。這讓 LLM 不需要去翻舊帳,能把注意力(Attention)集中在處理當前的任務上。
3. 工程上的可預測性(Determinism)
商業邏輯往往需要** 100% 的確定性**。
狀態機的作用: 它定義了邊界。例如,在「已出貨」狀態下,絕對不能觸發「修改地址」的 API。這種硬性的邏輯門禁(Guardrails)不能只靠 LLM 的自覺,必須由程式碼(狀態機)來把關。
4. 易於調試與視覺化
當你的商業邏輯變複雜時(例如有 20 個分支),單靠對話紀錄會變成一團亂麻。
透過顯式的狀態,你可以輕易畫出流程圖。
當客戶投訴時,你可以直接從資料庫看到 Status: Step_3,而不是去閱讀一遍他跟 AI 的所有聊天紀錄來猜測卡在哪。
總結:兩者的協作關係
最理想的架構不是「二選一」,而是 「LLM 負責理解,狀態機負責導航」