UNPKG

agent-contracts-runtime

Version:

Runtime bridge for executing agent-contracts workflows on Agent SDKs

212 lines (163 loc) 16.4 kB
# DSL → Claude Agent SDK Options ブリッジ仕様 > **Status**: Proposal > **Updated**: 2026-06-05 > **対象**: `src/adapters/claude-agent-sdk.ts`(および他 SDK アダプタのブリッジ層) > **基準 SDK**: `@anthropic-ai/claude-agent-sdk` v0.2.141(型定義 `sdk.d.ts` 実測) --- ## 1. 問題意識 現行アダプタは、DSL から組み立てたエージェント定義を **`systemPrompt` 文字列に全部書き込み**、 SDK のネイティブ連携チャネル(`agents` / `skills`)を使っていない。 ```ts // 現状 src/adapters/claude-agent-sdk.ts buildOptions(抜粋) opts.systemPrompt = systemPrompt; // ← agent 定義を文字列で丸投げ if (this.guardrailHooks) opts.hooks = buildClaudeHooks(...); // guardrail は hooks 連携済み opts.tools = readonly ? ["Read","Glob","Grep"] : [..., "Edit","Write","Bash"]; opts.permissionMode = this.permissionMode; // agents / skills / settingSources は未設定 ``` これでは: - **skills(SKILL.md)が SDK に渡らない** → progressive disclosure(必要時ロード)が働かない。 - **`agents` 未登録** → LLM がサブエージェントを Agent/Task tool で呼べない。 **LLM にエージェントのルーティング(役割選択 / fanout)をさせるなら `agents` 登録は必須**。 これが無いと「候補から LLM が選ぶ」モデル自体が成立しない。 ガードレイルは `hooks` で連携済みなので、欠落の核心は **skills****agents**2つ。 --- ## 2. SDK の連携チャネル(v0.2.141 実型定義) | 関心事 | SDK オプション | 意味 | |--------|---------------|------| | エージェント定義 | `Options.agents: Record<string, AgentDefinition>` | サブエージェント登録。Agent/Task tool 経由で LLM が呼ぶ | | 〃(単一カスタム) | `Options.systemPrompt: string \| string[] \| {preset}` | メインスレッドのカスタム system prompt | | Skills | `Options.skills: string[] \| 'all'` / `AgentDefinition.skills: string[]` | SKILL.md を有効化(progressive disclosure) | | ガードレイル(実行制御) | `Options.hooks: Partial<Record<HookEvent, ...>>` | PreToolUse 等でツール実行を block/deny | | 権限(能力境界) | `Options.allowedTools` / `disallowedTools` / `permissionMode` / `canUseTool` | ツール許可・拒否・確認 | | 設定ロード | `Options.settingSources: ('user'\|'project'\|'local')[]` | `.claude/settings.json`・skills・agents をディスクからロード | | 拡張束 | `Options.plugins: SdkPluginConfig[]` | commands + skills + hooks をまとめる | | MCP | `Options.mcpServers` | MCP サーバ | ### 2.1 `AgentDefinition` のフィールド(`sdk.d.ts:38`) ```ts type AgentDefinition = { description: string; // いつこのエージェントを使うか(LLM ルーティングの判断材料) prompt: string; // このエージェントの system prompt tools?: string[]; // 許可ツール(省略時は親から継承) disallowedTools?: string[]; model?: string; // 'sonnet'|'opus'|'haiku' or full id or 'inherit' skills?: string[]; // このエージェントにプリロードする skill 名 mcpServers?: AgentMcpServerSpec[]; permissionMode?: PermissionMode; maxTurns?: number; effort?: 'low'|'medium'|'high'|'xhigh'|'max' | number; memory?: 'user'|'project'|'local'; initialPrompt?: string; background?: boolean; }; ``` ### 2.2 Skills の重要な性質(`sdk.d.ts:1670-1692`) - skills は **context filter**。名前+説明だけが文脈に入り、本体 SKILL.md は **Skill tool で必要時にロード**される。 - 「ファイルはディスク上にあり Read/Bash で到達可能。サンドボックスではない」。 - **したがって SKILL.md 本文を `systemPrompt` に貼ってはいけない**(progressive disclosure の意味が消え、トークンを浪費する)。`skills: ['x','y']` か `'all'` で有効化する。 - メインセッションは `Options.skills`、サブエージェントは `AgentDefinition.skills`。 --- ## 3. ルーティングモデル(LLM ルーティング一択 = `agents` 登録必須) **決定: LLM 内部ルーティング(モデル B)一択。`Options.agents` 登録は必須要件とする。** 役割選択・ドメイン特定・fanout 対象の選択は**自然言語理解を要する**(context-map と同じ問題で、 実行前に決定できない)。したがって選択は LLM に委ねるしかなく、SDK の subagent 機構に候補 エージェントを登録して **LLM が `description` を見て Agent/Task tool で委譲**する以外に成立しない。 ### 仕様 - 候補エージェント群を `Options.agents: Record<id, AgentDefinition>` に登録する(**必須**)。 - 各 `AgentDefinition` に `description`(ルーティング判断材料)/ `prompt` / `tools` / `model` / `skills` / `permissionMode` を**個別**に持たせる。 - メインセッションの `systemPrompt` は「オーケストレータ/ルータの枠組み」を担い、各専門エージェントの 定義は `agents[id]` 側に置く(**`systemPrompt` に全エージェント定義を丸投げしない**)。 ### 却下: 外部ルーティング(単発 `query()` に systemPrompt 丸投げ) runtime が事前にエージェントを 1 つ選んで `systemPrompt` に入れる方式は**却下**。 事前選択は自然言語タスクを読まないと不可能(前段の議論で確定)。`systemPrompt` 丸投げの現状実装は このモデル専用であり、LLM ルーティングには対応できていない=**作り直し対象**### backend 含意(重要) `agents` 登録必須=**ネイティブ subagent 機構を持つ backend でないと完全には成立しない**| backend | subagent 機構 | モデル B 対応 | |---------|--------------|--------------| | Claude `@anthropic-ai/claude-agent-sdk` | `Options.agents` | ✅ ネイティブ | | OpenAI `@openai/agents` | `Agent.handoffs: Agent[]` | ✅ ネイティブ(handoff へ写像) | | ~~Gemini `@google/genai`(生API)~~ || **不採用**(ADK に置換) | | **Gemini ADK** `google/adk` | `sub_agents` + description 委譲 | ✅ ネイティブ(OpenAI 相当)= Google backend 採用 | | Gemini CLI | subagents(markdown 定義) | △ ローカル定義依存(採用外) | | ~~Cursor `@cursor/sdk`~~ || **サポート廃止**(依存 audit 不通過+subagent 機構なし) | → 「全て SDK 経由でコンポーネント化」を厳密に満たすのは **Claude / OpenAI / Gemini-ADK**。 生 Gemini は runtime が subagent ルーティングを自前実装、Gemini CLI / Cursor は IDE・ローカル定義 束縛として別扱い。全 backend の詳細は **§7 Backend Capability Matrix** 参照。 --- ## 4. DSL → SDK Options マッピング(決定版) ブリッジ層は `systemPrompt` 文字列だけでなく、**`Options` オブジェクト全体**を組み立てる。 | DSL / artifact | → SDK | 備考 | |----------------|-------|------| | agent 定義(role_name/purpose/responsibilities/constraints/rules) | `agents[id].prompt`(Claude)/ `Agent.instructions`(OpenAI handoff) | レンダリングは既存 `buildTaskPrompt` を流用。**`systemPrompt` 丸投げはしない** | | agent の `description` 相当(purpose 要約) | `agents[id].description` | **LLM ルーティングの判断材料**。必須 | | 候補エージェント集合(selector で絞った群) | `Options.agents: Record<id, AgentDefinition>`(Claude)/ `Agent.handoffs`(OpenAI) | **必須**。これが LLM ルーティングの前提 | | `can_execute_tools` | `agents[id].tools` | 現状の readonly 粗判定を DSL 由来へ | | `mode: read-only/read-write` | `permissionMode` / tools 絞り込み | | | `model_class` + `agent-runtime.config.model_mapping` | `agents[id].model` / `Options.model` | | | cursor-skills(SKILL.md / 固定プロンプト手順) | **in-code 合成**: skill = subagent(`AgentDefinition` prompt=本体, description=用途)/ または skill = TS ハンドラ tool(`createSdkMcpServer`+`tool()` / function / ADK function| ネイティブ `skills`(ディスク discovery)は**不使用**in-code 注入で `settingSources:[]` 隔離を維持。手順塊→subagent、決定的処理→tool | | guardrails + policy | `hooks`(既存)+ `disallowedTools` / `permissionMode` | scope を hook matcher / tools 絞りに反映 | | ルータ/オーケストレータ枠組み | `Options.systemPrompt` | 各エージェント定義ではなく「委譲の枠組み」のみ | | 隔離(ローカルファイルを読ませない) | `settingSources: []`(Claude) | コンポーネント化要件。**明示必須**(既定は `.claude/` をロード) | --- ## 5. 最優先の実装ギャップ | 優先 | ギャップ | 対応 | |------|---------|------| | **HIGH** | `agents` 未登録(=LLM ルーティング不能) | 候補エージェントを `AgentDefinition` 群に変換し `Options.agents` に登録。`description` を必ず埋める。`systemPrompt` 丸投げを廃止。OpenAI は `handoffs`、Gemini は runtime 側ルーティング合成 | | **HIGH** | skills 未連携 | SKILL.md をディスク配置し、DSL の skill 参照を `Options.skills` / `AgentDefinition.skills` にマップ。system prompt への本文インラインを廃止 | | **HIGH** | 隔離されていない(ローカル `.claude/` 依存の可能性) | Claude は `settingSources: []` を明示。Cursor は IDE 束縛=非隔離 backend として扱う | | **MED** | tools 権限が粗い(readonly 二択) | `can_execute_tools` 由来の per-agent `tools` に置換 | | LOW | plugins / mcpServers | 必要に応じて `AgentDefinition.mcpServers` / `Options.plugins` にマップ | --- ## 6. 現行実装の所在と変更点 - `src/adapters/claude-agent-sdk.ts` `buildOptions()`: - 追加(必須): `opts.agents`、`opts.skills`、`opts.settingSources: []`。 - 変更: `opts.tools` を DSL 由来の per-agent ツールへ。`opts.systemPrompt` は「ルータ枠組み」のみに縮小(エージェント定義の丸投げをやめる)。 - 既存維持: `opts.hooks`(guardrail)、`permissionMode`。 - `src/adapters/openai-agents-sdk.ts`: 候補エージェントを `Agent` 群に変換し `handoffs` に登録(subagent 委譲)。 - `src/adapters/gemini-sdk.ts`(生 `@google/genai`): **不採用・廃止**。Google backend は ADK に置換。 - `src/adapters/cursor-sdk.ts`: **サポート廃止・撤去**(依存 audit 不通過+subagent 機構なし)。`@cursor/sdk` peer dep も削除。 - **ADK アダプタ(新規)**: 候補エージェントを ADK の `sub_agents` 階層へ変換し description 委譲に乗せる。 runtime は TypeScript のため、ADK-TS を使うか、ADK(Python) を A2A / サブプロセス経由で呼ぶかは要確定(§8)。 - ブリッジ(DSL → Options 変換)は adapter の外(runtime のブリッジ層)に置き、adapter は受け取った Options 片を各 SDK に渡すだけにする。backend capability matrix(§3)で分岐。 - skills 本文は `systemPrompt` から除去(progressive disclosure に委ねる)。 --- ## 7. Backend Capability Matrix(全 SDK) ルーティングは LLM 内部ルーティング(§3)一択のため、**ネイティブ subagent 機構の有無****ローカルファイル依存(隔離可否)**が backend 選定の決め手になる。 凡例: ✅ ネイティブ in-code / △ 可能だが制約あり / ❌ 機構なし(runtime 合成が必要) / 🔒 ローカル依存 | backend | 種別 | subagent ルーティング | tools | skills 相当 | guardrail | ローカル依存 / 隔離 | SDK 経由コンポーネント化 | |---------|------|----------------------|-------|------------|-----------|---------------------|--------------------------| | **Claude** `@anthropic-ai/claude-agent-sdk` | agent SDK | ✅ `Options.agents`(Agent/Task tool) | ✅ `allowedTools`/`disallowedTools`/per-agent `tools` | ✅ `skills`(progressive disclosure) | ✅ `hooks` + `permissionMode`/`canUseTool` | 既定で `.claude/` ロード 🔒 → **`settingSources:[]` で隔離可** | ◎(`settingSources:[]` 前提で完全 in-code) | | **OpenAI** `@openai/agents` | agent SDK | ✅ `Agent.handoffs: Agent[]` | ✅ `tools`(function tools) | ❌(handoff+tools で代替) | ✅ SDK `guardrails` + `mcpServers` | なし(完全 in-code) | ◎ 最もクリーン | | ~~**Gemini (raw)** `@google/genai`~~ | 生成 API | ❌ subagent なし ||||| **不採用(ADK に置換)**。primitive 不足でモデル B を満たせない | | **Gemini (ADK)** `google/adk`(py / ts / go / java) | agent FW | ✅ `sub_agents` 階層 + description 委譲 + Workflow/Task/A2A | ✅ tools | ❌(tools/sub_agents で代替) | △ callback / plugin / policy | なし(**code-first in-code**| ◎(OpenAI 相当)= **Google backend はこれを採用** | | **Gemini CLI** `google-gemini/gemini-cli` | CLI エージェント | ✅ subagents(`@agent` 委譲 + description 自動ルーティング) | ✅ tools(wildcard / MCP isolation) | △(CLI 機能依存) | ✅ Policy Engine | **強い** 🔒(`~/.gemini/agents` の markdown 定義) | △ CLI backend。ローカル定義依存(Cursor と同類) | | ~~**Cursor** `@cursor/sdk`~~ | エージェント SDK |in-code 機構なし(単一 Agent のみ) | ✅ inline MCP || ✅ hook callbacks | 既定隔離 | **サポート廃止**。①依存 audit を通らない ②候補 subagent を in-code 登録できずモデル B 不適 | ### 判断(確定) - **採用 backend は Claude(要 `settingSources:[]`)/ OpenAI / Gemini-ADK の 3**。いずれも ネイティブ subagent + in-code 注入でモデル B・隔離要件を満たす。 - **生 `@google/genai` アダプタは不採用・廃止**。primitive 不足でモデル B を満たせず、runtime 自前合成は ADK で代替できるため保持価値がない。Google backend は **ADK 一本**- **Gemini CLI** はローカル markdown 定義依存(`~/.gemini/agents`)→ 非隔離。採用外。 - **Cursor は サポート廃止**。理由は ①`@cursor/sdk` が依存関係 audit を通らない、 ②候補 subagent を in-code 登録する機構が SDK に無くモデル B 不適、の 2 点。 `src/adapters/cursor-sdk.ts` と `@cursor/sdk`(optional peer dep)を撤去する。 - → ブリッジは採用 3 backend に対し、共通の最小契約(候補 AgentDefinition 群)を各機構 (Claude `agents` / OpenAI `handoffs` / ADK `sub_agents`)へ写像する。 --- ## 8. 残課題(未決) - 軽量 skill(同一 context に instructions を軽くロードしたいだけのもの)を subagent ではなく 「instruction テキストを返す tool」で表すかの線引き基準。 - ADK の組み込み方式: **ADK-TS を使う**か、**ADK(Python) を A2A / サブプロセス経由**で呼ぶか (runtime は TypeScript。ADK-TS の成熟度を確認のうえ確定)。 - 共通抽象(candidate AgentDefinition 群)→ Claude `agents` / OpenAI `handoffs` / ADK `sub_agents` への写像インターフェース型定義。 > **確定事項**: > - ルーティングは **LLM 内部ルーティング(モデル B)一択**。候補登録(`agents`/`handoffs`/`sub_agents`)は必須。 > - 外部ルーティング(systemPrompt 丸投げ)は不採用。 > - **採用 backend = Claude / OpenAI / Gemini-ADK**。生 `@google/genai` アダプタは廃止。 > - **Cursor サポート廃止**(依存 audit 不通過+in-code subagent 機構なし)。`cursor-sdk.ts` と `@cursor/sdk` を撤去。Gemini CLI は採用外(非隔離)。 > - **skills は in-code 合成**(subagent or TS ハンドラ tool)。ネイティブ `skills` ディスク discovery は不使用 → `settingSources:[]` 隔離を維持。skill と agent は同じ subagent 登録機構に統合。