# Decision Model

由 TypeSafe 或 OpenRouter 提供的快速分類模型，回答分派、監看檢查與內容過濾所需的型別化問題。

Decision model（`internal/agents/decision`）不是聊天模型：它接收一份 JSON 狀態與一組選擇題，對每題回傳一個選項與信心值。供應商有兩家：`typesafe` 與 `openrouter`。可選模型即時列出：TypeSafe 取自其 models API，OpenRouter 取自輸出模態含 `decisions` 的模型，並排除 5 個輸入格式與其他模型不同的 OpenRouter 模型。各供應商的清單快取 15 分鐘（`TTL_MODELS_CACHE_SEC`），該供應商存入 key、完成 OAuth 或清除登入時即失效。選定值以 `<provider>@<model>` 存於 `config.json` 的 `decision_model`；空值或 `off` 代表關閉。

設定分兩步。先存 key：TypeSafe 位於 `/model add` 的 **API Key** 分頁（按 `o` 開啟 `https://console.typesafe.ai/keys`），存完 key 會寫入 `TYPESAFE_API_KEY` 後直接結束，不進入選模型；OpenRouter 沿用聊天模型的 `OPENROUTER_API_KEY`。HTTP 則以 `POST /v1/provider/typesafe/key` 存 key。接著在 `/model` → `decision` 選模型（第一項 `disable` 為清除）、在 Web 模型設定的 **Decision** 選擇，或以 `POST /v1/model` `{decision}` 設定；不在 `decision_options` 內的值回 400。

每次呼叫時限 3 秒（`TIMEOUT_DECISION_CALL`）。請求文字截至 2,048 個 rune（`MAX_DECISION_RUNES`）；項目多時分批送出，每批最多 120 題（`MAX_DECISION_QUESTIONS`）、狀態約 20,000 個 rune（`MAX_DECISION_STATE_RUNES`）。除了 skill 過濾，判斷結果只在信心值至少 0.7 時才生效（`DECISION_MIN_CONFIDENCE`，監看檢查亦同）；呼叫失敗或逾時則維持原內容。

## 用途

| 用途 | Decision model 判斷的內容 |
|---|---|
| 分派 | 工作類別、請求是否指名模型、主題是否延續（見下文） |
| 監看檢查 | `background_command` 被錯誤關鍵字標記的輸出是否真的是錯誤，以及是否只是重複上次回報；無害或重複的命中不回報，呼叫失敗則附註後未經檢查直接回報。沒有 decision model 時每次命中都回報。`run_command_background` 與 `run_websocket` 的 interval／prompt 監看移除於 v1.3.2 |
| `search_web` | 移除與請求無關的結果；全部無關時回覆說明並要求改寫查詢 |
| `fetch_page` | 抓取的頁面存快取前先移除噪音區塊；與請求無關的頁面不交給模型 |
| `read_files` / `chat_history` | v1.3.11 起，`read_files` 讀取的檔案或 `chat_history` `mode=read` 讀取的 task 與請求無關時不交給模型，改回傳一段說明。只檢查至少 4,096 個 rune 的內容，帶 `around` 的 `read_files` 項目不檢查，同一檔案或 task 在每個 session 最多擋一次，第二次讀取即回傳內容 |
| Turn context | 沒有工具能滿足請求時，加上指向 `reasoning_guide(topics=["tool_generate"])` 的工具缺口提示；並從 skill 清單隱藏判定不相關的 skill |
| Session summary | 合併摘要前，移除噪音對話與過時的 `key_decisions` / `past_discussions` 條目；v1.3.7 起，摘要注入 turn context 時也只保留與請求相關的 `key_decisions` / `past_discussions` / `current_discussion` 條目（語言、格式、工作流程這類通則算相關），判斷失敗時全部保留，全都無關時不放摘要 |
| Context 超限 | 依最舊優先刪減之前，先移除請求已用不到的工具步驟 |

## 分派

只要設定了 decision model，每次需要路由的請求會同時發出 decision 呼叫與 LLM dispatcher，採用先取得的有效回覆；decision 呼叫失敗則採用 LLM dispatcher 的結果。未設定 decision model（`decision_model` 為空或 `off`）時只跑 LLM dispatcher。沒有獨立開關：`dispatcher_beta`（設定欄位 `DispatcherDecision`）、`/model` → `dispatch` 與 Web Dispatcher 清單中的 **Decision Model** 項目，以及 `POST /v1/model` `{dispatcher_beta}` 皆移除於 v1.3.2；改為設定或清除 decision model，`config.json` 殘留的 `dispatcher_beta` 會被忽略。

該呼叫最多問三題：請求屬於哪一類工作、是否明確要求使用某個候選模型，以及 session 已有前文且記錄了上一次的模型時，請求是否延續上一則使用者訊息的主題。工作類別決定 tier 順序與 reasoning 等級：

| 工作類別 | Tier 順序 | Reasoning |
|---|---|---|
| `code` | S > A > B > C | `xhigh` |
| `research` | S > A > B > C | `high` |
| `work`（其餘情形的預設） | A > S > B > C | `medium` |
| `chat` | B > C > A > S | `none` |
| `fetch` | C > B > A > S | `low` |

請求中指名的模型排第一；未指名且主題未變時，由 session 上一次使用的模型佔此位置，以重用其 prompt cache。上一次的模型居首時，只要記錄的是合法等級就沿用上一次的 reasoning，且這些居首的選擇不受供應商優先序重排，只有其後依等級排序的候選會。上一次的模型在每次成功回覆後記錄，`openai` / `codex` 30 分鐘、`gemini` 60 分鐘、其他供應商 5 分鐘後失效。其餘候選依 tier 排序，先讀 `model_tag`，未設定則套用命名規則；同 tier 內 `claude-code`、其次 `codex` 的模型排前，其餘依模型家族決定先後。`research` 工作中，`copilot@` 模型因 Copilot context window 較小而排在同等級其他模型之後。`pass` 模型不參與此排序。送出的 context 為最後 4 則 user 與 assistant 訊息，每則截至 2,048 個 rune。

TypeSafe/Jev beta dispatcher（`dispatcher_beta` 直接呼叫 `jev-latest`，有自己的 `TYPESAFE_API_KEY` 提示與 `missing_key` 400）移除於 v1.3.1，改由上述 decision model 取代，TypeSafe 成為其供應商之一。
