yoi/tickets/model-reasoning-control.md

5.3 KiB
Raw Blame History

モデル reasoning/thinking 制御の内部抽象整理

背景

llm-worker には RequestConfig::reasoningReasoningControl { effort, budget_tokens } があり、各 scheme も OpenAI 系の reasoning_effort / reasoning.effort、Anthropic の thinking.budget_tokens、Gemini の thinking_config.thinking_budget へ投影する実装を持っている。

一方で、上位層から通常のリクエストへ reasoning を設定する経路はまだ整っておらず、内部型も effort: Option<_>budget_tokens: Option<_> を同時に持てるため、「今回の指定がラベルなのか数値 budget なのか」が型で一意に表現されていない。

Provider ごとの正当値は変化しやすく、OpenAI 系は low / medium / high などの文字列 effort、Anthropic / Gemini 系は token budget 数値、Gemini には -1 dynamic budget のような値がある。insomnia はこれらを過度に正規化せず、上位層・manifest では String または i32 として受け、内部では「文字列 effort か数値 budget か」を enum で保持し、scheme が自身の wire 形式へ投影する。

要件

manifest / 上位設定での表現

reasoning/thinking 制御は、上位層および manifest で String または i32 として指定できるようにする。

例:

reasoning = "medium"
reasoning = 4096

文字列は provider-native な effort label として扱い、数値は provider-native な thinking budget token 数として扱う。間違った値や provider が受け付けない組み合わせは insomnia 側で過度に検証せず、原則として provider API のエラーに任せる。

現状は WorkerManifestConfig / WorkerManifestreasoning フィールドが無く、pod::apply_worker_manifest()RequestConfig::new()max_tokenstemperature だけを移しているため、通常の Pod/manifest 起動経路では RequestConfig::reasoning が常に None のまま scheme へ届く。このチケットでは [worker] 由来の reasoning 指定を RequestConfig へ渡す経路まで含める。

内部型の enum 化

ReasoningControleffortbudget_tokens を同時に持つ struct ではなく、指定種別が一意な enum に整理する。

想定形:

pub enum ReasoningControl {
    Effort(ReasoningEffort),
    BudgetTokens(i32),
}

ReasoningEffort は既知値を variant として持ちつつ、未知の provider-native label を素通しできるようにする。

想定形:

pub enum ReasoningEffort {
    Minimal,
    Low,
    Medium,
    High,
    XHigh,
    Other(String),
}

数値 budget は Gemini の -1 dynamic budget 等を表現できるよう、u32 ではなく signed integer とする。

scheme 投影

各 scheme は ReasoningControl の variant と ModelCapability::reasoning を見て、自身が wire に載せられる形式のみ投影する。

  • OpenAI Chat Completions: Effort(_)reasoning_effort に投影する
  • OpenAI Responses: Effort(_)reasoning: { effort, summary: "auto" } に投影する
  • Anthropic: BudgetTokens(_)thinking: { type: "enabled", budget_tokens } に投影する
  • Gemini: BudgetTokens(_)generation_config.thinking_config.thinking_budget に投影する

provider-native な値そのものの妥当性検証は最小限に留める。例えば OpenAI に未知 effort label を送る、Anthropic に provider が許容しない budget を送る、といったケースは provider API の応答で検出されればよい。

Capability との関係

ModelCapability::reasoning は、その model/provider が受けられる reasoning 指定の大まかな形式を表す既存の ReasoningSupport::{Effort, BudgetTokens, Both} を維持してよい。

ただし capability は正当値リストではなく、scheme 投影の可否判定に留める。モデル別の厳密な effort label や budget range を insomnia 側で網羅しない。

範囲外

  • UI 上のプリセットLow / Medium / High 等)をどの値へ変換するかの設計
  • provider ごとの budget 推奨値テーブル
  • OpenRouter / Groq / DeepSeek 等、現行 scheme で未実装の reasoning 統一 API への追加対応
  • reasoning / thinking 出力 block のログ保存・再送・表示ポリシーの変更

完了条件

  • manifest / 上位設定で reasoning を String または i32 として表現できる
  • WorkerManifestConfig / WorkerManifest が reasoning 指定を保持し、cascade merge 後に pod::apply_worker_manifest() から RequestConfig::reasoning へ渡される
  • ReasoningControl が enum 化され、effort と budget の同時指定が型上できない
  • ReasoningEffortminimal / low / medium / high / xhigh と未知文字列の素通しを扱える
  • budget token 指定が signed integer として扱われ、Gemini の -1 のような値を表現できる
  • OpenAI Chat / OpenAI Responses / Anthropic / Gemini の既存 reasoning 投影が enum 型に追従している
  • 既存の reasoning 無指定時は、従来通り wire request に reasoning/thinking パラメータを出さない

Review