コンテンツにスキップ

Control IR

Control IR は LLM が artifact と並行して出力できる副作用 op のリストです。OS は各 op をディスパッチし、LLM(または次の Phase)が消費するために結果を返します。

Op の種類

種類 目的 必要な Permission
read_file ファイルを読み取る(行範囲指定も可) file.read
write_file ファイルを書き込む(作成 / 上書き) file.write
edit_file ファイル内の文字列を置換する file.write
delete_file ファイルを削除する file.write
glob_files glob パターンに一致するファイルを列挙する file.read
grep_files 正規表現でファイル内容を検索する file.read
ask_user Phase を一時停止してユーザーに質問する なし(常に許可)
sandboxed_exec SandboxPolicySandboxBackend を介して argv を実行する(削除済みの shell op を置き換え) バックエンドが強制(SandboxPolicy
web_search DuckDuckGo で公開ウェブを検索する Tier 1 — デフォルト許可;reyn.yamlweb.search: deny でブロック
web_fetch 単一 URL を取得してテキストを抽出する Tier 1 — デフォルト許可;reyn.yamlweb.fetch: deny でブロック
mcp 設定済み MCP server のツールを呼び出す Skill frontmatter の permissions.mcp: [server_name]
mcp_install レジストリから MCP server をプロジェクト設定にインストールする Skill frontmatter の permissions.mcp_install: true
index_query インデックス済みソース 1 件に対してセマンティック検索を行う なし
recall マクロ: embed → 各ソースに index_query → トップ K をマージ なし
index_drop インデックス済みソースを完全削除する(破壊的) Skill frontmatter の permissions.index_drop: ask
judge_output rubric + threshold + on_fail ポリシーを持つ LLM スコアラー なし(LLM コスト)

共通エンベロープ

すべての op は kind ディスクリミネーターを持つ JSON オブジェクトです:

{
  "kind": "read_file",
  "path": "src/foo.py"
}

OS は op をその kind のスキーマに対して検証し、実行し、呼び出し元 Phase に結果を返します。

ファイル op(細粒度)

LLM が発行できるファイル操作は 6 つの細粒度 kind です — chat router がツールとして公開しているのと同じサブセットです(concepts/architecture/llm-invocation-surfaces.md を参照)。それぞれ独自のスキーマを持つ独立した op kind であり、op サブフィールドはありません。

{"kind": "read_file", "path": "src/foo.py"}
{"kind": "read_file", "path": "src/foo.py", "offset": 100, "limit": 40}

{"kind": "write_file", "path": "out.txt", "content": "..."}

{"kind": "edit_file", "path": "src/foo.py",
 "old_string": "...", "new_string": "...", "replace_all": false}

{"kind": "delete_file", "path": "tmp.txt"}

{"kind": "glob_files", "path": ".", "pattern": "**/*.py", "max_results": 50}

{"kind": "grep_files", "path": "src", "pattern": "def \\w+",
 "glob": "**/*.py", "case_sensitive": false, "max_results": 50}
種類 Permission 備考
read_file file.read offset / limit(行範囲)は省略可。
write_file file.write 作成または上書き;親ディレクトリは必要に応じて作成。
edit_file file.write replace_all: true でない限り old_string は一意でなければならない。
delete_file file.write
glob_files file.read path のデフォルトは .
grep_files file.read glob で検索対象ファイルを絞り込む。

Permission スコープは op の種類ごとに設定されます。reference/config/permissions.md を参照してください。

粗粒度 file 実行バックエンド(Phase からは発行不可)

上記の細粒度 kind が、Phase が LLM に提示し(また LLM から受け付ける)唯一のファイル op です。これらは統一 ToolRegistry を通じてディスパッチされ、内部で粗粒度の FileIROp{kind: "file", op: ...})を構築して共有バックエンド op_runtime/file.py にルーティングします。その粗粒度 file kind は — OP_KIND_MODEL_MAP から削除済み — LLM が発行できる Control IR kind ではありません。次の用途でのみ存続します:

  • 細粒度ハンドラが委譲する共有実行バックエンド、および
  • OS 決定論的な preprocessor run_op ステップ({kind: file, op: ...})、chat ホストのファイルメソッド、reyn memory CLI のディスパッチ先。

これらの非 Phase 呼び出し元は、細粒度 kind が公開しない拡張サブ操作 — mkdirmovestatregenerate_indexreyn memory やインデックスを管理するスキルが preprocessor / CLI 経由で使用、Phase Control IR としては決して使われない)— にも到達します。

ask_user

Phase を一時停止してユーザーに質問します。OS は質問を表示し、stdin を読み取り、回答を user_message artifact として入力にマージした上で同じ Phase を再実行します。訪問カウントは増加しません。

{
  "kind": "ask_user",
  "question": "どのモデルをターゲットにしますか?",
  "suggestions": ["light", "standard", "strong"]
}

sandboxed_exec

宣言された SandboxPolicy と OS が選択した SandboxBackend を介して argv を実行します。分離強制が必要な(または将来必要になる)ケースで shell を置き換えます。

{
  "kind": "sandboxed_exec",
  "argv": ["echo", "hello"],
  "network": false,
  "read_paths": ["{{workspace}}"],
  "write_paths": ["{{workspace}}/output"],
  "allow_subprocess": false,
  "env_passthrough": ["PATH"],
  "timeout_seconds": 60
}

フィールド: - argv(必須)— コマンドと引数。argv[0] が実行可能ファイル。 - network(省略可、デフォルト false)— アウトバウンドネットワークを許可。 - read_paths(省略可)— プロセスが読み取り可能なファイルシステムパス(glob パターン可)。 - write_paths(省略可)— プロセスが書き込み可能なファイルシステムパス。 - allow_subprocess(省略可、デフォルト false)— 子プロセス生成の許可。 - env_passthrough(省略可)— 引き渡す環境変数名(それ以外は除去)。 - timeout_seconds(省略可、デフォルト 60)— ウォールクロック上限。

バックエンド選択: get_default_backend() がプラットフォームに応じて選択します。macOS < 26 では SeatbeltBackend(sandbox-exec SBPL)。Linux ≥ 5.13 かつ sandbox-linux extra インストール済みの場合は LandlockBackend(+ オプションの seccomp-BPF スタック)。その他のプラットフォームまたは選択バックエンドが利用不可の場合は NoopBackend(監査のみ、強制なし)にフォールバック — 初回使用時に一行 WARN を出力。reyn.yamlsandbox.backendauto | seatbelt | landlock | noop)および sandbox.on_unsupportedwarn | error | ignore)で上書き可能。

結果フィールド: returncodestdoutstderrtruncatedbackend

発行イベント: sandboxed_exec_startedsandboxed_exec_completed(P6 監査証跡)。

DuckDuckGo を使って公開ウェブを検索し、構造化された結果を返します。Tier 1 — デフォルト許可;Permission 宣言不要。reyn.yamlweb.search: deny でプロジェクト全体をブロックできます。

{
  "kind": "web_search",
  "query": "reyn agent OS site:github.com",
  "max_results": 10,
  "backend": "duckduckgo"
}

フィールド: query(必須)、max_results(省略可、デフォルト 10)、backend(省略可、デフォルト "duckduckgo";現在唯一サポートされる値)。

query では標準の DuckDuckGo 検索 operator が使用できます:

  • site:<domain> — 特定ドメインに絞り込む(例: site:news.ycombinator.com
  • "phrase" — phrase 完全一致
  • -termterm を含む結果を除外

ユーザーの意図が特定サイトや phrase に限定される場合に operator を使用し、それ以外は通常のキーワードで問題ありません。結果は results フィールドの {title, url, snippet} オブジェクトのリストとして返されます。

web_fetch

単一 URL を取得し、テキスト抽出したコンテンツを返します。Tier 1 — デフォルト許可;Permission 宣言不要。通常は web_search の後、特定の結果ページを詳しく読むために使用します。reyn.yamlweb.fetch: deny でブロック、web.fetch: allow で明示的に事前承認できます。

{
  "kind": "web_fetch",
  "url": "https://example.com/article",
  "prompt": "主要な知見を抽出する",
  "max_length": 50000
}

フィールド: url(必須)、prompt(省略可 — 何を抽出するかの LLM 向けヒント。OS は実行しない)、timeout(省略可、デフォルト 30 秒)、max_length(省略可、デフォルト 50000 文字)。

HTML レスポンスはテキスト抽出されます(script、style、非コンテンツタグは除去)。コンテンツが max_length を超える場合は切り詰められ、結果に truncated: true が付きます。非 HTML レスポンスはそのまま返されます。

mcp

設定済み MCP server のツールを呼び出します。reyn.yamlmcp.servers: に server が宣言されており、かつ Skill の permissions.mcp frontmatter ブロックに列挙されている必要があります。

{
  "kind": "mcp",
  "server": "filesystem",
  "tool": "read_text_file",
  "args": {"path": "README.md"}
}

フィールド: server(必須 — reyn.yamlmcp.servers: のキーと一致する必要がある)、tool(必須 — server の tools/list レスポンスで公開されているツール名)、args(省略可、デフォルト {})。

提示名。 Phase はこの op を chat-tool 名 call_mcp_tool として LLM に提示し、OS がパース境界で mcp kind にエイリアスし直します。mcpOP_KIND_MODEL_MAP 上およびディスパッチされる op 上の正規 kind のままです。

OS は server のトランスポート(stdiohttpsse)を解決し、MCPClient 経由でディスパッチして、ツール結果を返します。呼び出しごとに mcp_calledmcp_completed、(失敗時)mcp_failed イベントが発行されます。

server の設定、トランスポートの選択、セキュリティモデルについては concepts/tools-integrations/mcp.md を参照してください。

mcp_install

registry.modelcontextprotocol.io から MCP server をプロジェクト設定にインストールします。Phase 専用(ルーターからは使用不可)。Skill frontmatter に permissions.mcp_install: true が必要で、ユーザー承認も必要です。

{
  "kind": "mcp_install",
  "server_id": "io.github.modelcontextprotocol/server-filesystem",
  "scope": "local",
  "env_overrides": {"GITHUB_TOKEN": "ghp_..."}
}

フィールド: - server_id(必須)— レジストリ識別子(例: "io.github.foo/bar-mcp")。 - scope(省略可、デフォルト "local")— 書き込む設定層: - "local"<project>/.reyn/config.yaml - "project"<project>/reyn.yaml - "user"~/.reyn/config.yaml - env_overrides(省略可)— シークレット環境変数の事前提供値。ここに指定したキーは対話型プロンプトをスキップ。

ハンドラーのライフサイクル: 1. RegistryClientserver.json を取得 2. ランタイムコマンドの利用可能性確認(npx / uvx / docker / dnx) 3. PermissionResolver.require_file_write(= .reyn/mcp.yaml)+ require_http_get(= registry host)でゲート。 旧 require_mcp_install bool-axis gate は廃止済み 4. intervention_bus 経由で isSecret=true 環境変数を収集;各 save_secretPermissionResolver.require_secret_write を経由(= Phase 6 で wildcard "*" が runtime-determined key set を許可) 5. 対象スコープの設定ファイルに mcp.servers.<name> を書き込む 6. mcp_server_installed イベントを発行(P6)— キー名のみ。値は含まない

削除された op。 embedindex_write の control-IR op は削除されました。 embedding とインデックス書き込みは現在 provider-directreyn.api.safe.embed_index.embed_and_index() の内部で行われます(safe-mode の python step が自分で chunk を作ってそこに流し込みます — それをラップしていた stdlib skill ごと、バンドルされていた index_docs / index_events の chunker も削除されました)。クエリ側の embedding は recall op の内部で 行われます。EmbeddingProviderSqliteIndexBackend のプリミティブは 変わっていません — 削除されたのは run-op のラッパーとバンドル済み chunker だけです。kind: embed / kind: index_write を発行するものはもうありません。

index_query

インデックス済みソース 1 件に対してセマンティック類似検索を行います。

{
  "kind": "index_query",
  "source": "project_docs",
  "query_vector": [0.1, 0.2, ...],
  "top_k": 5,
  "filters": {"path": "docs/concepts"}
}

フィールド:

  • source(str、必須)— 論理ソース名。
  • query_vector(list[float]、省略可)— 事前計算済み埋め込み。null の場合はカタログ列挙にフォールバック(fallback_size_cap トークン上限)。
  • top_k(int、デフォルト 5)— 返す結果数。
  • filters(dict[str, str]、省略可)— ランキング前に適用するメタデータキー/値フィルター。
  • fallback_size_cap(int、デフォルト 4096)— query_vectornull のときの列挙フォールバックのトークン上限。

戻り値: {"kind": "index_query", "source": str, "results": [{"text": str, "score": float, "metadata": dict}]}.

recall

マクロ op: クエリを embed → 各ソースに index_query → グローバルにトップ K をマージして結果を返します。RAG 取得において推奨される高レベル op です。

{
  "kind": "recall",
  "query": "クラッシュリカバリはどのように動作しますか?",
  "sources": ["project_docs", "api_reference"],
  "top_k": 5,
  "embedding_model": "standard"
}

フィールド:

  • query(str、必須)— embed して検索する自然言語クエリ。
  • sources(list[str]、必須)— 検索する論理ソース名。空にはできません。
  • top_k(int、デフォルト 5)— グローバルマージ後に返す結果数。
  • filters(dict[str, str]、省略可)— 各 index_query サブ op に転送。
  • embedding_model(str、デフォルト "standard")— embed サブ op に転送するモデルクラス。

戻り値: {"kind": "recall", "results": [{"text": str, "score": float, "source": str, "metadata": dict}]}.

イベント: embed サブ op が失敗した場合に recall_embed_failed(query、error)。

index_drop

インデックス済みソースを完全に削除します。SQLite バックエンドとマニフェストエントリを削除します。破壊的かつ不可逆です。 Skill frontmatter に permissions.index_drop: ask(または明示的な allow)が必要で、デフォルトでユーザー承認ゲートが発動します。

{
  "kind": "index_drop",
  "source": "project_docs"
}

フィールド:

  • source(str、必須)— 削除する論理ソース名。

戻り値: {"kind": "index_drop", "source": str, "chunks_dropped": int}.

イベント: index_droppedsourcechunks_dropped)。

judge_output

Phase 内の評価ループで使用する LLM ベースの出力スコアラー。target の dot-path で値を解決し、呼び出し元が供給する rubric と共に LLM を呼び出し、スコア(0.0〜1.0)と合格/不合格フラグを返します。

{
  "kind": "judge_output",
  "target": "artifact.data.summary",
  "rubric": "0.0〜1.0 でスコアリング: サマリーは簡潔で正確かつ完全ですか?",
  "threshold": 0.8,
  "on_fail": "transition"
}

フィールド: - target(str、必須): スコアリング対象の値への dot-path(例: "artifact.data.summary")。現在のワークスペース artifact に対して解決されます。 - rubric(str、必須): LLM prompt 本文。評価基準は Skill author が記述します。OS はこの内容を解釈しません(P7)。 - threshold(float、省略可、デフォルト 0.8): 合格スコア([0.0, 1.0])。 - on_fail"transition" | "abort" | "continue"、省略可、デフォルト "transition"): - "transition": LLM が次の Phase を選択(既存の decision フロー)。 - "abort": Skill 実行を中止。 - "continue": スコアを記録するのみ。フロー変更なし。 - model(str | null、省略可): モデルクラスのオーバーライド(例: "strong")。省略時は Skill の現在のモデルを使用。

戻り値: {"kind": "judge_output", "score": float, "passed": bool, "reason": str, "threshold": float, "on_fail": str}

Audit イベント: tool_executedop=judge_output, target, score, passed, threshold, reason)(P6)。

P7 注記: Reyn は rubric に依存しません。rubric の内容は Skill author の authored prompt の一部であり、OS は解釈せずそのまま LLM に渡すだけです。


コントリビューター向けメモ: src/reyn/schemas/models.py および src/reyn/core/op_runtime/registry.py に新しい Control IR op kind を追加する際は、同じ PR でここにセクションを追加してください。reference と registry は同期を保つ必要があります。ルールの詳細は CLAUDE.md を参照してください。

LLM に op が提示される場所

OS は利用可能な op をすべてのコンテキストフレームに available_control_ops として注入します。各エントリーは kind、一行の説明、動作例を含みます。LLM は意図を説明にマッピングして op を選択します。Phase の Markdown は op の構文を説明してはなりません(P8)。

関連情報

  • events.md — op の種類ごとに発行されるイベント
  • コンセプト: principles P8 (principles doc removed)