10 KiB
Insomnia アーキテクチャ
プロジェクトの目的
複数の LLM エージェント(Pod)が独立プロセスとして並行動作し、自律的に分業・統括できるエンジン。シングルエージェントの LLM リグとの差別化は、タスクを別 Pod に委譲し結果を集約するオーケストレーションを LLM 自身に行わせる点にある。
設計原則
層の分離と方向性
各クレートは下位層に依存し、上位層を知らない。上位層は下位層の API を丸ごと隠蔽せず、制約が必要な部分だけをラップして提供し、それ以外は下位層を直接使わせる。
宣言した層が解決する
ある層が構成を宣言として受け取ったなら、その解決もその層の責務。マニフェストに [model] ref = "anthropic/claude-sonnet-4-6"(あるいは scheme = "anthropic" + model_id = ... の inline 形式)と書いた以上、ModelManifest → LlmClient の変換は insomnia 側(crates/provider)が行う。逆に llm-worker が LlmClient trait だけを受け取るのは正しい — llm-worker はプロバイダの選択を宣言として受け取っていないから。
概念の追加は不在が問題になってから
概念を先に作らない。「これがないと今の問題が解けない」と言えるまで追加しない。拡張ポイントはドキュメントに記録するが、実装はしない。
最小の構造化で最大の自由度
insomnia は環境再現・コンテナ管理・VCS 統合などを自身の責務としない。Pod が動くホストの fs 上で活動する主体を提供し、それにコンテキストを与えてスポーンさせられる仕組みを付与する。構築する環境はユーザー次第。
Pod
独立したエージェントの実行単位。llm-worker の Worker をラップし、マニフェストによる宣言的構成、ディレクトリスコープ、Pod 名に紐づく永続状態を加える。
- 1 live Pod process は 1 Pod name/current state を所有する
- 会話の永続化は
session-storeのsession_id/segment_idログ、Pod 名の current state はpod-storemetadata が担う - マニフェストまたは profile から完結構築できる
- scope で読み取り・書き込み可能なパスを制限する
- 独立した socket サーバーを持ち、Client (TUI / GUI) が接続して操作する
実行ループ
Client → Method::Run { input }
→ Worker: user message 追加 → LLM リクエスト
→ LLM 応答: text → Client に Event::TextDelta で配信
→ LLM 応答: tool_use → Interceptor (pre_tool_call) → Tool 実行 → Interceptor (post_tool_call) → Hook
→ tool result を履歴に追加 → 次の LLM リクエスト
→ ターン終了: Hook (on_turn_end) → Client に Event::RunEnd
Interceptor と Hook の分離
- Interceptor: Worker の内部制御フロー。context / history の直接操作が可能。Pod の内部機構(compaction トリガー、notification 注入)が使う。外部に公開しない
- Hook: Pod の公開 API。read-only のサマリ情報のみを受け取り、制御フロー判断(continue / skip / abort / pause)を返す。将来スクリプト言語に安全に公開可能
- 実行順序: Interceptor → Hook。Interceptor が context を整えた結果を Hook が観測する
コンテキスト管理
- Prune: 古い tool result の content を除去(summary は残す)。LLM request context だけを加工し、永続 history 本体は変更しない
- Compact: 履歴 prefix を要約し、同じ
session_id配下の新segment_idへ rotate する。SegmentStart.compacted_fromが元 segment を参照し、Pod metadata の active pointer は新 segment を指す - サーキットブレーカー: compact が3回連続失敗したら無効化
Protocol
Pod の制御・監視に使う JSONL ベースのメッセージプロトコル。トランスポートに依存しない。正確な wire enum は crates/protocol/src/lib.rs::{Method, Event} を正とし、この節はカテゴリの目次として扱う。
- Client → Pod (
Method)- turn 制御:
Run,Resume,Cancel,Pause,Shutdown - context/session 制御:
Compact,ListRewindTargets,RewindTo - typed injection / child lifecycle:
Notify,PodEvent - client 補助:
ListCompletions - Pod visibility / restore:
ListPods,RestorePod
- turn 制御:
- Pod → Client (
Event)- accepted input / history seed:
Snapshot,UserMessage,SystemItem,SegmentRotated - generation stream:
TurnStart,TurnEnd,LlmCallStart,LlmCallEnd, retry/continuation events,Text*,Thinking*,ToolCall*,ToolResult,Usage,RunEnd - control replies: completions, rewind, visible Pod list / restore results
- operational status:
Status,Alert,MemoryWorker,Compact*,Error,Shutdown
- accepted input / history seed:
- リクエストとレスポンスの紐付けを一般化した RPC にはしない。多くの状態は broadcast event と Pod status で観測する
- 一部の reply(例: completions)は要求 socket にだけ返る。broadcast event と request-local reply の違いは enum variant のコメントを正とする
- 操作の競合は先勝ち(run 中に別の run →
AlreadyRunningエラー)
マニフェストとファクトリ
PodManifest
Pod の宣言的構成。TOML で記述。
[pod]
name = "agent"
[model]
ref = "anthropic/claude-sonnet-4-6"
[worker]
instruction = "$insomnia/default"
max_tokens = 4096
temperature = 0.3
[[scope.allow]]
target = "/abs/path"
permission = "write"
[model] は ref = "<provider>/<model_id>" でプロバイダ / モデルカタログを引く短縮形と、scheme / model_id / auth を直書きする inline 形式の両方を受ける。カタログは resources/{providers,models}/builtin.toml を builtin、<config_dir>/{providers,models}.toml を user override として解決する(<config_dir> の解決ルールは manifest::paths 参照)。詳細は docs/pod-factory.md と crates/provider/README.md。
Manifest / profile 入力
通常の Pod 起動は Lua profile discovery/default から PodManifest を生成する。bundled builtin:default が fallback default で、user/project profiles.toml は profile registry と default selection だけを担う。user/project manifest.toml の ambient cascade は通常起動では使わない。
insomnia pod --manifest <PATH> は explicit one-file compatibility/debug input で、指定 TOML 1 枚だけに builtin defaults を merge し、PodManifestConfig -> PodManifest の required validation を通す。
PodFactory の user/project/overlay API は低レベル構成部品として残るが、CLI の通常起動 path では generic TOML overlay を公開しない。
Instruction とプロンプト資産
worker.instruction はファイル参照。3 層の prefix addressing でプロンプト資産を解決:
$insomnia/...— バイナリ同梱(resources/prompts/、include_dir!で埋め込み)$user/...—<config_dir>/prompts/(manifest::pathsで解決)$workspace/...—<project>/.insomnia/prompts/
テンプレートは minijinja で評価。{% include "$insomnia/common/tool-usage" %} のようにプロンプト間で参照可能(prefix なしの include は現在のファイルからの相対解決)。
レンダリング結果の末尾に scope summary と AGENTS.md(あれば)がコード側で固定付加される。ユーザーテンプレートからはこれらに触れない。
Scope
Pod が操作できるファイルパスの制御。
allowルールで読み取り・書き込みを許可、denyルールで制限- effective permission = allow - deny
recursive = falseで直下のみに制限可能(summary に[non-recursive]マーカー)- scope 排他: Pod 間の write 衝突は runtime registry / scope lock で検出する。child Pod へ委譲した write scope は親の effective scope から delegated-out deny として差し引かれ、child 停止・prune 時に reclaim される
セッション永続化
session-storeは append-only JSONL segment log。1LogEntry= 1 行SessionIdは論理会話の fork-tree root、SegmentIdはその中の現在の書き込み先 segment- fresh conversation だけが新
SessionIdを作る。compact / fork は同じSessionId配下に新SegmentIdを作り、SegmentStart.{compacted_from,forked_from}: SegmentOriginで出自を持つ - segment log の先頭は
LogEntry::SegmentStart。以降にInvoke,UserInput,AssistantItem,ToolResult,SystemItem,TurnEnd,RunCompleted/RunErrored,ConfigChanged,LlmUsage,Extensionなどを append する - replay は segment log から Worker state を再構成する。lineage は entry hash ではなく
SegmentOrigin.at_turn_indexと segment id で参照する - Pod 名の durable current state(active pointer、resolved manifest snapshot、spawned child delegation/reclaim)は
pod-storemetadata が担う。socket path や runtime mirror は liveness authority ではない
組み込みツール
正確な callable set と description は ToolRegistry / manifest permission / scope / profile に依存する。高レベルには以下のカテゴリを持つ。
| カテゴリ | 例 | 概要 |
|---|---|---|
| File / shell | Read, Write, Edit, Glob, Grep, Bash |
workspace ファイル操作と shell 実行。file tools は ScopedFs と read-before-edit tracker を通る。Bash は permission policy と出力退避で制御する |
| Task | TaskCreate, TaskUpdate, TaskList, TaskGet |
セッション内の短期 task 状態管理 |
| Memory / Knowledge | MemoryQuery, MemoryRead, MemoryWrite, MemoryEdit, MemoryDelete, KnowledgeQuery |
manifest の memory 設定が有効な時に登録される durable memory / knowledge 操作 |
| Pod orchestration | SpawnPod, SendToPod, ReadPodOutput, StopPod, ListPods, RestorePod |
child / visible Pod の起動・通信・停止・一覧・復元 |
| Web | WebSearch, WebFetch |
manifest で明示設定された provider と local secret-store reference 経由の bounded web access |
すべての tool call は manifest tool permission と scope/policy のチェックを通る。ファイル write scope、Pod delegation、memory layout、web provider 設定はそれぞれ別の authority を持ち、UI 表示だけで権限を広げない。