90 秒總覽
回合引擎(QueryEngine)負責把「使用者訊息」變成「可控的執行回合」。它不只送 API,還會組裝 system prompt(含條件式 memory prompt)、處理使用者輸入、記錄 permission denials、累積 usage 與持久化 transcript。你可以把它視為單回合的交通總站。
執行路徑(主骨架)
使用者提交 prompt
建立回合 context(模型、工具、權限、system prompt parts)
執行 processUserInput(可能直接結束,不進 query loop)
先寫入使用者 transcript(確保可 resume)
若 shouldQuery=true 才進入 query loop
模型輸出可能觸發 tool_use,回寫 messages 後再迭代
達到完成邊界後輸出 result(usage / permission_denials / transcript)
flowchart TD
A[使用者輸入] --> B[submitMessage]
B --> C[建立回合 context]
C --> D{shouldQuery?}
D -->|否| E[本地結果分支]
D -->|是| F[query loop]
F --> G{是否呼叫工具}
G -->|是| H[tool run + result]
H --> F
G -->|否| I[完成訊息]
E --> J[result]
I --> J
J --> K[usage/持久化/permission_denials]
路徑判讀重點
- 真正的「完成」不是模型輸出一句話,而是收尾步驟完成。
- QueryEngine 會把工具權限拒絕累積成
permission_denials,隨 result 一起回傳。 - 會話持久化不是只在尾端寫入;進 query 前就會先記錄使用者訊息,避免中斷後無法 resume。
- memory prompt 是條件式載入(需 custom system prompt + memory override)。
關鍵決策(為什麼這樣設計)
決策 1:把回合責任集中在引擎層
原因:避免工具層各自管理 usage、持久化與結束條件,導致行為分裂。
代價:引擎會變成高複雜度模組,改動需要更嚴格回歸測試。
決策 2:回合內允許多次 tool/model 循環
原因:真實任務常需要工具結果再推理,不可能單次推論結束。
代價:需要明確 completion boundary,否則容易迴圈失控。
決策 3:尾段整合系統訊息並隔離失敗
原因:QueryEngine 會在尾段整合 query 產生的系統訊息(包含 compact/stop-hooks 相關),並把結果統一輸出,避免中途污染主回合。
代價:尾段邏輯較複雜,需要清楚區分主回覆完成與附加系統訊息處理。
替代方案與取捨
| 方案 | 優點 | 缺點 | 為何未採用/何時可採用 |
|---|---|---|---|
| 把執行拆成多個小協調器 | 單檔更乾淨,責任更局部 | 跨協調器狀態傳遞成本高 | 目前回合資料耦合高,拆分前要先穩定介面 |
| 工具層自行處理錯誤與持久化 | 工具可獨立優化 | 行為不一致,除錯困難 | 除非有嚴格 shared contract,否則風險過高 |
| 每次模型輸出都立即落盤 | 資料最完整 | I/O 負擔高,互動延遲增加 | 除非改成分層緩衝,否則不適合主路徑 |
失敗路徑與防護
Failure 1:prompt 過長造成回合中斷
症狀:模型拒絕處理或輸出錯誤事件。
防護:在回合層做可恢復錯誤分流,避免直接崩潰 UI。
Failure 2:權限拒絕被誤判為系統卡住
症狀:工具被拒絕後沒有正確回顯,使用者誤以為回合無回應。
防護:明確呈現 permission_denials 與拒絕原因,將拒絕視為可觀測結果而非靜默失敗。
Failure 3:回合尾端 hook 失敗污染主流程
症狀:主回覆完成但後續狀態錯亂。
防護:hook 走非阻塞與錯誤隔離,僅回報失敗摘要不破壞主回合。
實作驗證(你改完要怎麼確認)
- 送出一個不需工具的 prompt:確認回合正常完成並持久化。
- 送出會觸發工具的 prompt:確認 loop 可正確往返並收斂。
- 製造可恢復錯誤(例如限制條件):確認引擎能回報而非直接崩潰。
- 檢查 stop hook 副作用:確認主回覆完成後,附加功能才執行。
這四步對應「主路徑、工具路徑、錯誤路徑、尾端路徑」,是最低可接受驗證集。