yoi/tickets/pod-factory.md

12 KiB
Raw Blame History

Pod Factory: 設定カスケードとプロンプト資産による Pod 自動生成

背景

現状、Pod を起動するには test_pod.local.toml のような完全な PodManifest TOML を手書きする必要がある。1 人のユーザーが1 つのエージェントを試験運用するには十分だが、Insomnia が狙う「複数のエージェントが独立プロセスとして spawn されて自律的に動く」世界観では、Pod のライフサイクル全体が自動化可能でなければならない。そのためには、Pod の作成自体も自動化可能である必要がある。

手書きマニフェストには以下の問題がある:

  • 1 Pod = 1 ファイルで、Pod を動的に増やす用途にスケールしない
  • 設定項目が多く(worker.* / provider.* / scope.* / compaction.* / tool_output.* 等)、毎回コピペしてわずかな差分だけ書き換える苦行になる
  • system_prompt を TOML 文字列に埋め込む形はプロンプト資産の再利用性が低い
  • Pod の起動条件の共通部分(プロバイダ・モデル・デフォルトツール設定など)は本来一度書けば良いのに、毎回書かされる

ゴール

Pod 作成を「最終的に PodManifest を1 つ構築する問題」として定式化し、その PodManifestカスケード + 差分上書きで組み立てる基盤を提供する。手書きが必要な TOML は「ユーザー / プロジェクト単位のデフォルト上書き」だけに縮退させ、個別の Pod 起動ごとに人間が TOML を触らない状態を目指す。

プロンプトは手書きマニフェストに文字列を埋め込む方式をやめ、テンプレート資産ライブラリとして参照可能にする。

方針

同じ型で、層で上書きする

  • 解決後の型は現行の manifest::PodManifest のまま。Pod 側の契約(Pod::from_manifest)は変更しない。
  • カスケードは PodManifest より上の「部分的な PodManifest を層ごとに保持し、順番にマージして最終形を作る」層として設計する。
  • 各層は部分形を持てる(全フィールドを埋める必要はない)。存在するフィールドだけが下層を上書きする。

カスケードの層

優先順位が低い方から高い方へ:

  1. ビルトインのデフォルト: コードに焼き込んだ基本値(現在 PodManifest 各フィールドの #[serde(default)]Default 実装に散っているものを集約)
  2. ユーザー設定: ~/.config/insomnia/config.toml など。ユーザー個人のプロバイダ指定・デフォルトモデル・常用ツール設定等を書く
  3. プロジェクト設定: プロジェクト直下の .insomnia/config.toml など。プロジェクト固有の scope・compaction 設定・system_prompt のベース等を書く
  4. プログラマティック上書き: Pod 生成を呼ぶコードGUI / CLI / 別 Pod からの spawn 等)が渡す PodManifestOverlay 的な部分形。ここで pod.namepod.pwd のようなその Pod に固有の値を与える

各層とも人間が書くときは PodManifest と同じ TOML スキーマで書く(サブセット可)。

マージのセマンティクス

  • スカラー (String, u32, bool 等): 上層が存在すれば丸ごと置換
  • Option 型: 上層が Some なら置換、None なら据え置き
  • マップ (例: tool_output.per_tool): キー単位でマージ、同一キーは上層優先
  • リスト (例: scope.allow / scope.deny): 原則置換append にすると下層の意図しないルールが漏れる危険)。ただし append したいケースはあるので、設計時に decoration の形式(例: scope.allow_extra など)を別途検討
  • 未知フィールドは manifest エラーにせずログ警告

プロンプト資産ライブラリ

  • プロンプトは TOML 文字列ではなくファイルとして管理する。
  • 検索パスはカスケードと対応した3層:
    1. ビルトイン: バイナリに同梱されたデフォルトプロンプト(coder / reviewer / planner 等、設計時に選定)
    2. ユーザー: ~/.config/insomnia/prompts/*.md
    3. プロジェクト: .insomnia/prompts/*.md
  • 同名があれば上層が優先
  • 既存の SystemPromptTemplateminijinja ベース)のローダを拡張し、テンプレート内から他のプロンプトを {% include "coder" %} / {% import "planner" as p %} のように参照できるようにする。
  • 層の異なる同名プロンプトを合成するための include 先解決は上記優先順位に従う。

設定値のテンプレート参照は扱わない

worker.max_tokens = "{{ env.INSOMNIA_MAX_TOKENS }}" のような設定値の中でテンプレートを展開する機能は本チケットの範囲外とする。テンプレートエンジンはプロンプト本文の組み立てだけに限定する。設定値の動的化が必要になった時点で別チケットで検討する。

プログラマティック Pod 作成 API

  • Pod::from_manifest(path) の隣に、カスケード解決を経由する生成経路を追加する。イメージ:

    // 層を明示的に指定して最終形を得る
    let manifest = PodFactory::new()
        .with_user_config(user_config_path)?     // absent OK
        .with_project_config(project_root)?      // absent OK
        .with_overlay(overlay_toml_or_struct)    // programmatic
        .resolve()?;                             // -> PodManifest
    Pod::from_manifest(manifest, store).await?;
    
  • 細かい形状は設計時に詰めるbuilder 型 vs 関数型、overlay を TOML 文字列か型付きか等)。

  • CLI からは insomnia spawn --overlay '...' 相当で同じ経路を叩く想定。

要件

カスケード基盤

  • ユーザー設定・プロジェクト設定・プログラマティック overlay を順に重ねた結果が PodManifest として取れる。
  • 各層が部分形を許容する(pod.pwd だけ書いてあっても良い等)。
  • マージセマンティクスがフィールドごとに定義され、テストされる(スカラー / Option / マップ / リスト)。
  • 層が全て空でもビルトインデフォルト単体で有効な manifest にならない(少なくとも pod.pwdproviderscope.allow は上位層で与える必要がある)。その旨を resolve 時のエラーで明示する。

プロンプト資産ライブラリ

  • 3層の検索パスでプロンプトファイルを解決できる。
  • 同名プロンプトは上層優先で解決される。
  • SystemPromptTemplate の minijinja Environment にカスタムローダを仕込み、{% include "name" %} / {% import "name" as x %} で資産を参照できる。
  • プロンプト資産自体もテンプレートとして評価され、現行の SystemPromptContextnow / cwd / scope / tools / files 等)と同じ変数が見える。

プログラマティック Pod 作成

  • 既存の Pod::from_manifest を壊さず、追加経路として PodFactory 系の API を提供する。
  • TUI / GUI / daemon 等の上位クライアントが、TOML ファイルパスではなくオーバーレイ + 設定ディレクトリパスを渡すだけで Pod を起動できる。

ドキュメント

  • カスケード層の優先順位・マージ規則を docs/ にまとめる。
  • ユーザー設定 / プロジェクト設定ファイルの最小例全オプション例を残す。

設計で決めること

  • ユーザー設定のパス: ~/.config/insomnia/config.toml か、XDG 非準拠のパスも許容するか。環境変数で上書きできるか。
  • プロジェクト設定の場所: プロジェクトルートの .insomnia/config.toml か、別の命名か。サブディレクトリから起動したときの discovery上位ディレクトリ探索の挙動。
  • プロジェクトルートの判定: 明示指定 vs .git.insomnia/ で自動検出
  • preset の概念を入れるか: 名前付きの overlay セット(例: insomnia spawn coderを導入するか。導入する場合、preset はユーザー設定内に [preset.coder] として持つか、個別ファイル ~/.config/insomnia/presets/coder.toml として持つか
  • リストフィールドのマージ方針: 置換 only にするか、append 用の別フィールド (scope.allow_extra 等) を用意するか
  • ビルトインプロンプトの初期ラインナップ: どの役割をデフォルトで同梱するか、どこに置くか(crates/pod/assets/prompts/*.mdinclude_str! で埋め込む等)
  • プロンプト資産のファイル形式: .md.txt か、拡張子省略可能にするか、フロントマターYAML/TOMLで引数デフォルトを持たせるか
  • プロンプト include 時の context 伝搬: 親テンプレートの変数を include 先でも見えるようにするか、明示的に with で渡させるか
  • エラー戦略: 上層で書かれた未知フィールドや型ミスマッチをどこまで寛容に扱うか
  • 既存の Pod::from_manifest(path) とのインターフェース整理: 廃止するか、内部的に PodFactory に委譲するか
  • CLI コマンド名: insomnia spawn / insomnia pod new / その他

完了条件

  • PodManifest の最終形を層のマージで構築する PodFactory(または同等の仕組み)が実装され、マージセマンティクスの単体テストが通る。
  • ユーザー設定・プロジェクト設定・プログラマティック overlay のすべての層を使う end-to-end テストで、Pod が TOML ファイルパスを一切渡さずに起動できる。
  • プロンプト資産ライブラリを経由して system_prompt が組み立てられ、{% include "ビルトイン名" %} で同梱プロンプトを参照できることをテストで確認できる。
  • ユーザー設定ファイル / プロジェクト設定ファイルのドキュメントが docs/ に存在する。
  • 既存の Pod::from_manifest(path) 経路が動き続ける(回帰させない)。

他チケットとの関係

  • tickets/native-gui-mvp.md: 現状「manifest ファイルを選ぶ UI」を含むが、本チケット完了後はその UI が「preset 選択 + overlay 入力」に置き換わる想定。native-gui-mvp 実装時に本チケットの API を使うか、先に文字列パス渡しで済ませて後から差し替えるかは別途判断
  • tickets/tui-pod-spawn-ui.md: 同上。Pod spawn UI は本チケットが提供する API の上に構築される
  • tickets/protocol-design.md: Pod ↔ Client protocol 自体は変わらない。spawn 要求を protocol に載せるかどうかは protocol-design 側で検討
  • docs/system-prompt-template.md / crates/pod/src/system_prompt.rs: プロンプト資産ライブラリはこの minijinja 基盤の拡張として実装される

範囲外

  • 設定値の中のテンプレート展開max_tokens = "{{ env.X }}" のような動的値)。プロンプト本文のテンプレート展開のみを扱う
  • GUI 内での設定ファイル編集 UI。編集は人間がエディタで TOML を書くだけあくまで「Pod 生成時に手書きしない」ことを目指す)
  • チーム共有・同期。ユーザー設定とプロジェクト設定は各自・各リポジトリ単位で管理される
  • 秘密情報管理API キー等)。既存の api_key_file 方式を維持する
  • 設定値の型バリデーション強化JSON Schema など)。現行の serde ベースで十分な範囲に留める