# DSLリファレンス

Goa-AI の DSL 関数（エージェント、ツールセット、ポリシー、MCP 連携）を網羅した完全リファレンス。

Source: https://goa.design/ja/docs/2-goa-ai/dsl-reference/

Relative links resolve against the source URL above.


このドキュメントは Goa-AI の DSL 関数の完全なリファレンスを提供します。[ランタイム](./runtime.md) ガイドと合わせて読むことで、デザインがランタイムの挙動にどのように変換されるかを理解できます。

Goa-AI は、型付きの直接アシスタント出力のために、サービス所有の
`Completion(...)` コントラクトもサポートします。`goa gen` を実行すると、
これらのコントラクトは `gen/<service>/completions` 配下のコードを生成します。

completion 名はコントラクトの一部であり、1-64 文字の ASCII、
英字・数字・`_`・`-` のみ、先頭は英字または数字でなければなりません。
生成コードには unary `Complete<Name>(...)` と streaming
`StreamComplete<Name>(...)` / `Decode<Name>Chunk(...)` が含まれます。
`completion_delta` はプレビュー専用で、正規の値は最後の 1 つの
`completion` chunk だけです。
生成されたスキーマは正規のサービス契約のままであり、モデル
アダプターは制約付きデコードのために provider 固有のサブセットへ
正規化できますが、宣言された契約を表現できない provider は
明示的に拒否しなければなりません。

## DSL クイックリファレンス

| 関数 | コンテキスト | 説明 |
|----------|---------|-------------|
| **エージェント関数** | | |
| `Agent` | Service | LLM ベースのエージェントを定義する |
| `Completion` | Service | サービス所有の型付き直接アシスタント出力コントラクトを宣言する |
| `Use` | Agent | ツールセットの利用（消費）を宣言する |
| `Export` | Agent, Service | 他エージェント向けにツールセットを公開する |
| `AgentToolset` | Use 引数 | 他エージェントが公開したツールセットを参照する |
| `Passthrough` | Tool（Export 内） | 決定論的にサービスメソッドへフォワードする |
| `DisableAgentDocs` | API | `AGENTS_QUICKSTART.md` の生成を無効にする |
| **ツールセット関数** | | |
| `Toolset` | トップレベル | プロバイダ所有のツールセットを宣言する |
| `FromMCP` | Toolset 引数 | MCP バックエンドのツールセットとして構成する |
| `FromRegistry` | Toolset 引数 | レジストリ由来のツールセットとして構成する |
| `Description` | Toolset | ツールセットの説明を設定する |
| **ツール関数** | | |
| `Tool` | Toolset, Method | 呼び出し可能なツールを定義する |
| `Args` | Tool | 入力パラメータのスキーマを定義する |
| `Return` | Tool | 出力結果のスキーマを定義する |
| `ServerData` | Tool | ツール結果に付随するサーバーデータ（モデルには送らない）のスキーマを定義する |
| `FromMethodResultField` | ServerData | bound service method result field から server-data を投影する |
| `AudienceTimeline` | ServerData | server-data を timeline/UI eligible としてマークする (default) |
| `AudienceInternal` | ServerData | server-data を internal composition attachment としてマークする |
| `AudienceEvidence` | ServerData | server-data を provenance または audit evidence としてマークする |
| `BoundedResult` | Tool | 結果を「境界付きビュー」としてマークし、bounds フィールドの標準形を適用する（任意で cursor 設定のサブ DSL を指定可能） |
| `Cursor` | BoundedResult | 次ページの opaque cursor を格納する payload フィールド名を指定する（任意） |
| `ContinueWith` | BoundedResult | 機械的な pagination を runtime が cursor を bind する sibling action に委譲する |
| `NextCursor` | BoundedResult | 次ページ cursor を格納する result フィールド名を指定する（任意） |
| `Tags` | Tool, Toolset | メタデータラベルを付与する |
| `Meta` | Tool | `ToolSpec.Meta` に出力される、名前付きで不活性な設計メタデータを付与する |
| `BindTo` | Tool | ツールをサービスメソッドにバインドする |
| `Inject` | Tool | ランタイム注入フィールドを指定する |
| `CallHintTemplate` | Tool | 呼び出し表示用テンプレート（ヒント） |
| `ResultHintTemplate` | Tool | 結果表示用テンプレート（ヒント） |
| `ResultReminder` | Tool | ツール結果後の静的なシステムリマインダ |
| `Confirmation` | Tool | 実行前に明示的な帯域外確認を要求する |
| `TerminalRun` | Tool | ツールを終端かつ bookkeeping としてマークする。成功すると後続プランナーターンなしで run が終了する |
| `Bookkeeping` | Tool | 制御記録としてマークする。retrieval と連続失敗の予算を消費せず、正確な呼び出しと結果は永続化される |
| **ポリシー関数** | | |
| `RunPolicy` | Agent | 実行制約を設定する |
| `DefaultCaps` | RunPolicy | リソース制限を設定する |
| `MaxToolCalls` | DefaultCaps | 最大ツール呼び出し回数 |
| `MaxRecoveryTurns` | DefaultCaps | 拒否された出力の後にプランナーを再実行できる最大回数 |
| `TimeBudget` | RunPolicy | 単純なウォールクロック制限 |
| `Timing` | RunPolicy | 詳細なタイムアウト設定 |
| `Budget` | Timing | 実行全体の予算 |
| `Plan` | Timing | プランナー（推論）アクティビティのタイムアウト |
| `Tools` | Timing | ツール実行アクティビティのタイムアウト |
| `History` | RunPolicy | 会話履歴管理 |
| `KeepRecentTurns` | History | スライディングウィンドウ（直近 N ターン） |
| `CompressAtTurns` | History | モデル支援の要約を開始するターン数トリガー |
| `CompressAtMaxInputTokens` | History | 実行時の入力トークン数で要約を開始するトリガー |
| `KeepMaxTurns` | History | 最新の完全なターンをいくつまで正確に保持するか |
| `KeepMaxInputTokens` | History | 実行時トークン予算内に収まる完全なターンだけを正確に保持する |
| `Cache` | RunPolicy | プロンプトキャッシュ設定 |
| `AfterSystem` | Cache | システムメッセージ後にチェックポイント |
| `AfterTools` | Cache | ツール定義後にチェックポイント |
| `InterruptsAllowed` | RunPolicy | 一時停止/再開を有効化 |
| `OnMissingFields` | RunPolicy | 必須フィールド欠落時の扱い |
| **MCP Functions** | | |
| `MCP` | Service | MCP を有効化する |
| `ProtocolVersion` | MCP option | MCP プロトコルバージョンを設定する |
| `Tool` | Method | MCP が有効なサービス内でメソッドを MCP ツールとして扱う |
| `Toolset(FromMCP(...))` | Top-level | Goa バックエンドの MCP 由来ツールセットを宣言する |
| `Toolset("name", FromExternalMCP(...), func() { ... })` | Top-level | インラインスキーマ付きの外部 MCP ツールセットを宣言する |
| `Resource` | Method | メソッドを MCP リソースとして扱う |
| `StaticPrompt` | Service | 静的プロンプトテンプレートを追加する |
| **レジストリ関数** | | |
| `Registry` | トップレベル | レジストリソースを宣言する |
| `URL` | Registry | エンドポイントを設定する |
| `APIVersion` | Registry | API バージョンを設定する |
| `Timeout` | Registry | HTTP タイムアウトを設定する |
| `Retry` | Registry | リトライポリシーを設定する |
| `SyncInterval` | Registry | カタログ更新間隔を設定する |
| `CacheTTL` | Registry | ローカルキャッシュ有効期間を設定する |
| `Federation` | Registry | 外部レジストリの取り込みを設定する |
| `Include` | Federation | 取り込み対象グロブ |
| `Exclude` | Federation | 除外対象グロブ |
| `PublishTo` | Export | レジストリ公開先を設定する |
| `Version` | Toolset | レジストリツールセットのバージョンを固定する |
| **スキーマ関数** | | |
| `Attribute` | Args, Return, ServerData | スキーマフィールドを定義する（一般） |
| `Field` | Args, Return, ServerData | proto フィールド番号付きで定義する（gRPC） |
| `Required` | Schema | 必須フィールドを指定する |
| `Example` | Schema | 明示的な例を付与する。authored top-level tool payload example は provider-native example と構造化された修正情報になる |

### 評価 DSL

`goa.design/goa-ai/eval/dsl` は application の Goa v3 design に評価 suite を追加
します。`Suite` は design のトップレベルまたは `Agent` 内で suite を宣言し、
`Scenario` は 1 つの case を、`Input` は生成 hook に渡される型付き値を宣言しま
す。Goa v3 の `Description` と `Timeout`、Goa-AI の `Tags` が各 case を完成させ
ます。runner は ID と tag の選択、同時実行数
の制限、回答を評価する前の 4 例による semantic judge の検証を担当します。
hook と実行 contract は[生成型評価](evaluations/)を参照してください。

### ツール payload の例

tool payload schema では、明示的に書いた Goa の top-level `Example(...)` が
provider-facing top-level example の source です。codegen はこの example を
生成 tool spec に raw JSON と parsed object input として保持し、annotated
schema と root の `example` だけを取り除いた schema も出力します。

provider adapter はこれらの projection を直接使います。schema annotation を
使う provider は annotated JSON Schema を使います。Direct Anthropic と
Bedrock Claude は schema-without-root-example projection と native
`input_examples` を使い、Bedrock は beta contract が要求する場合に
Anthropic field を `additionalModelRequestFields` 経由で渡します。生成または
合成された example は top-level provider example には昇格しません。

## Prompt 管理（v1 統合パス）

Goa-AI v1 では、専用の Prompt DSL（`Prompt(...)`, `Prompts(...)`）は **不要** です。
現在の Prompt 管理はランタイム主導で行います。

- ベースラインの prompt spec を `runtime.PromptRegistry` に登録する
- スコープ付き override を `runtime.WithPromptStore(...)` で有効化する
- プランナーから `PlannerContext.RenderPrompt(...)` で prompt を解決・描画する
- 描画した prompt の provenance を `model.Request.PromptRefs` に付与する

agent-as-tool フローでは、agent-tool 登録時に `runtime.WithPromptSpec(...)` などの
ランタイムオプションを使い、tool ID と prompt ID を対応付けます。
これは任意です。consumer 側で prompt コンテンツを設定しない場合、ランタイムは
ツールの canonical JSON payload を子 run の user message としてそのまま渡し、
provider 側の planner はサーバー側で注入したコンテキストを使って自分の prompt を
描画できます。

### Field と Attribute の違い

`Field` と `Attribute` はどちらもスキーマフィールドを定義しますが、用途が異なります。

**`Attribute(name, type, description, dsl)`** — 一般目的のスキーマ定義:
- JSON のみのスキーマで使用する
- フィールド番号は不要
- 多くのケースで最も簡潔

```go
Args(func() {
    Attribute("query", String, "Search query")
    Attribute("limit", Int, "Maximum results", func() {
        Default(10)
    })
    Required("query")
})
```

**`Field(number, name, type, description, dsl)`** — gRPC/protobuf 向けの番号付きフィールド:
- gRPC サービスを生成する場合に必要
- フィールド番号は一意かつ安定である必要がある
- サービスが HTTP と gRPC の両トランスポートを公開する場合に使用する

```go
Args(func() {
    Field(1, "query", String, "Search query")
    Field(2, "limit", Int, "Maximum results", func() {
        Default(10)
    })
    Required("query")
})
```

**使い分け:**
- JSON のみのエージェントツールなら `Attribute`（最も一般的）
- Goa サービスが gRPC を持ち、ツールがそのメソッドにバインドされる場合は `Field`
- 同じスキーマ内での混在は可能ですが、推奨しません

## 概要

Goa-AI は、エージェント、ツールセット、ランタイムポリシーを宣言するための関数で Goa の DSL を拡張します。DSL は Goa の `eval` エンジンによって評価されるため、標準のサービス/トランスポート DSL と同じルールが適用されます: 式は適切なコンテキストで呼び出す必要があり、属性定義は Goa の型システム（`Attribute`、`Field`、バリデーション、例など）を再利用します。

### インポートパス

Goa デザインパッケージにエージェント DSL を追加します:

```go
import (
    . "goa.design/goa/v3/dsl"
    . "goa.design/goa-ai/dsl"
)
```

### エントリーポイント

通常の Goa の `Service` 定義の中でエージェントを宣言します。DSL は Goa のデザインツリーを拡張し、`goa gen` の間に処理されます。

### 生成結果

`goa gen` は次を生成します:

- エージェントパッケージ（`gen/<service>/agents/<agent>`）: ワークフロー定義、プランナーアクティビティ、登録ヘルパー
- ツールセットのオーナー・パッケージ（`gen/<service>/toolsets/<toolset>`）: 型付きの payload/result 構造体、specs、codecs、（該当する場合）transforms
- plan/execute/resume ループ用のアクティビティハンドラ
- デザインをランタイムに配線する登録ヘルパー

`DisableAgentDocs()` で無効化しない限り、モジュールルートのコンテキスト付き `AGENTS_QUICKSTART.md` は再生成されます。

`goa example` は別の処理です。実行可能な `cmd/`、bootstrap、planner、example-executor の各ファイルを、まだ存在しない場合にだけ作成します。これらは application 所有で、後の実行でも上書きされません。`gen/` 配下の生成ファイルと `AGENTS_QUICKSTART.md` は、引き続き design から更新されます。

### クイックスタート例

```go
package design

import (
    . "goa.design/goa/v3/dsl"
    . "goa.design/goa-ai/dsl"
)

var DocsToolset = Toolset("docs.search", func() {
    Tool("search", "Search indexed documentation", func() {
        Args(func() {
            Attribute("query", String, "Search phrase")
            Attribute("limit", Int, "Max results", func() { Default(5) })
            Required("query")
        })
        Return(func() {
            Attribute("documents", ArrayOf(String), "Matched snippets")
            Required("documents")
        })
        Tags("docs", "search")
    })
})

var AssistantSuite = Toolset(FromMCP("assistant", "assistant-mcp"))

var _ = Service("orchestrator", func() {
    Description("Human front door for the knowledge agent.")

    Agent("chat", "Conversational runner", func() {
        Use(DocsToolset)
        Use(AssistantSuite)
        Export("chat.tools", func() {
            Tool("summarize_status", "Produce operator-ready summaries", func() {
                Args(func() {
                    Attribute("prompt", String, "User instructions")
                    Required("prompt")
                })
                Return(func() {
                    Attribute("summary", String, "Assistant response")
                    Required("summary")
                })
                Tags("chat")
            })
        })
        RunPolicy(func() {
            DefaultCaps(
                MaxToolCalls(8),
                MaxRecoveryTurns(3),
            )
            TimeBudget("2m")
        })
    })
})
```

`goa gen example.com/assistant/design` を実行すると、例えば次が生成されます:

- `gen/orchestrator/agents/chat`: ワークフロー + プランナーアクティビティ + エージェントレジストリ
- `gen/orchestrator/agents/chat/specs`: エージェントのツールカタログ（`ToolSpec` の集約 + `tool_schemas.json`）
- `gen/orchestrator/toolsets/<toolset>`: サービス所有ツールセットの types/specs/codecs/transforms
- `gen/orchestrator/agents/chat/exports/<export>`: エクスポートされたツールセット（agent-as-tool）パッケージ
- `Toolset(FromMCP(...))` が `Use` で参照された場合の MCP 対応登録ヘルパー

### 型付きツール descriptor

ツールセットごとの specs package は、型付き identifier と、その identifier を生成済み payload/result codec に対応付ける `<Tool>Tool()` descriptor を定義します:

```go
const (
    Search tools.Ident = "orchestrator.search.search"
)

func SearchTool() tools.TypedTool[*SearchPayload, *SearchResult] {
    // Returns fresh generated specs and codecs.
}
```

プランナーが call を作る場合は `planner.NewToolRequest(SearchTool(), payload)` を使います。生成 accessor は毎回新しい copy を返すため、返された schema や example を変更しても、後の model request には影響しません。

### サービス所有の型付き Completion

Goa-AI が所有できる構造化 contract は tool だけではありません。assistant が tool call ではなく型付きの最終回答を直接返すべき場合は、`Completion(...)` を使います:

```go
var Draft = Type("Draft", func() {
    Attribute("name", String, "Task name")
    Attribute("goal", String, "Outcome-style goal")
    Required("name", "goal")
})

var _ = Service("tasks", func() {
    Completion("draft_from_transcript", "Produce a task draft directly", func() {
        Return(Draft)
    })
})
```

completion 名は structured-output contract の一部です。1-64 文字の ASCII で、英字、数字、`_`、`-` を含められ、先頭は英字または数字でなければなりません。

`goa gen` は `gen/<service>/completions` 配下に次を出力します:

- 型付き result 型と union 型
- 非公開 schema と生成 codec
- 公開 `Complete<Name>(ctx, client, req)` helper
- 型付き `StreamComplete<Name>(ctx, client, req)` helper
- root result に authored `Example(...)` がある場合の `<Name>Example()`

unary helper は受理された assistant response を直接 decode し、正確な provider response を `Response.ModelResponse` として公開します。streaming helper は `completion.Streamer[T]` を返します。`Recv` は preview fragment を返し、`Value()` は stream が正常終了して検証を通った後だけ利用できます。未検証 chunk 用の公開 decoder はありません。

生成 completion helper は tool-enabled request と caller-supplied `StructuredOutput` を拒否します。structured output を実装しない provider は `model.ErrStructuredOutputUnsupported` で明示的に失敗します。不正な unary または streaming output は、correction model request を行わず、再試行不能な `planner.OutputContractError` を返します。生成 schema は正規の service contract のままです。model adapter は provider-specific constrained decoding のために normalize できますが、宣言された contract を表現できない provider は拒否しなければなりません。

### クロスプロセスのインライン合成

エージェント A が、エージェント B によってエクスポートされたツールセットを「使用する」と宣言した場合、Goa-AI は合成を自動的に配線します:

- エクスポータ（エージェント B）側パッケージには、生成された `agenttools` ヘルパーが含まれる
- コンシューマ（エージェント A）側のエージェントレジストリは、`Use(AgentToolset("service", "agent", "toolset"))` を使ったときにそれらのヘルパーを使用する
- 生成される `Execute` 関数は、ネストされたプランナーメッセージを構築し、プロバイダーエージェントを子ワークフローとして実行し、ネストされたエージェントの `RunOutput` を `planner.ToolResult` に適合させる

これにより、各エージェント実行は単一の決定論的ワークフローとなり、合成のためのリンクされた実行ツリーが得られます。

---

## エージェント関数

### Agent

`Agent(name, description, dsl)` は `Service` の中でエージェントを宣言します。サービススコープのエージェントメタデータを記録し、`Use` と `Export` によってツールセットを関連付けます。

**コンテキスト**: `Service` の内部

各エージェントはランタイム登録として次を持ちます:
- ワークフロー定義と Temporal アクティビティハンドラ
- DSL 由来のリトライ/タイムアウトオプションを持つ PlanStart/PlanResume アクティビティ
- ワークフロー、アクティビティ、ツールセットを登録する `Register<Agent>` ヘルパー

```go
var _ = Service("orchestrator", func() {
    Agent("chat", "Conversational runner", func() {
        Use(DocsToolset)
        Export("chat.tools", func() {
            // tools defined here
        })
        RunPolicy(func() {
            DefaultCaps(MaxToolCalls(8))
            TimeBudget("2m")
        })
    })
})
```

### Use

`Use(value, dsl)` は、エージェントがツールセットを消費することを宣言します。ツールセットは次のいずれかです:

- トップレベルの `Toolset` 変数
- `Toolset(FromMCP(...))` 参照
- インラインツールセット定義（文字列名 + DSL）
- agent-as-tool 合成のための `AgentToolset` 参照

**コンテキスト**: `Agent` の内部

```go
Agent("chat", "Conversational runner", func() {
    // Reference a top-level toolset
    Use(DocsToolset)

    // Reference with subsetting
    Use(CommonTools, func() {
        Tool("notify") // consume only this tool from CommonTools
    })

    // Reference an MCP toolset
    Use(AssistantSuite)

    // Inline agent-local toolset definition
    Use("helpers", func() {
        Tool("answer", "Answer a question", func() {
            // tool definition
        })
    })

    // Agent-as-tool composition
    Use(AgentToolset("service", "agent", "toolset"))
})
```

### Export

`Export(value, dsl)` は、他エージェントまたは他サービスへ公開するツールセットを宣言します。エクスポートされたツールセットは、他エージェントが `Use(AgentToolset(...))` を介して消費できます。

**コンテキスト**: `Agent` または `Service` の内部

```go
Agent("planner", "Planning agent", func() {
    Export("planning.tools", func() {
        Tool("create_plan", "Create a plan", func() {
            Args(func() {
                Attribute("goal", String, "Goal to plan for")
                Required("goal")
            })
            Return(func() {
                Attribute("plan", String, "Generated plan")
                Required("plan")
            })
        })
    })
})
```

### AgentToolset

`AgentToolset(service, agent, toolset)` は、他エージェントがエクスポートしたツールセットを参照します。これにより agent-as-tool 合成が可能になります。

**コンテキスト**: `Use` への引数

`AgentToolset` を使う場面:
- エクスポートされたツールセットへの式ハンドルがない
- 複数エージェントが同名のツールセットをエクスポートしている
- デザインで明示して可読性を上げたい

```go
// Agent A exports tools
Agent("planner", func() {
    Export("planning.tools", func() { /* tools */ })
})

// Agent B uses Agent A's tools
Agent("orchestrator", func() {
    Use(AgentToolset("service", "planner", "planning.tools"))
})
```

### Passthrough

`Passthrough(toolName, target, methodName)` は、エクスポートされたツールを Goa のサービスメソッドへ決定論的にフォワードする設定です。これはプランナーを完全にバイパスします。

**コンテキスト**: `Export` の下にネストされた `Tool` の内部

```go
Export("logging-tools", func() {
    Tool("log_message", "Log a message", func() {
        Args(func() {
            Attribute("message", String, "Message to log")
            Required("message")
        })
        Return(func() {
            Attribute("logged", Boolean, "Whether the message was logged")
        })
        Passthrough("log_message", "LoggingService", "LogMessage")
    })
})
```

### DisableAgentDocs

`DisableAgentDocs()` は、モジュールルートに `AGENTS_QUICKSTART.md` を生成しないようにします。

**コンテキスト**: `API` の内部

```go
var _ = API("orchestrator", func() {
    DisableAgentDocs()
})
```

---

## ツールセット関数

### Toolset

`Toolset(name, dsl)` は、プロバイダ所有のツールセットをトップレベルで宣言します。トップレベルで宣言されたツールセットは再利用可能になり、エージェントは `Use` で参照します。

**コンテキスト**: トップレベル

```go
var DocsToolset = Toolset("docs.search", func() {
    Description("Tools for searching documentation")
    Tool("search", "Search indexed documentation", func() {
        Args(func() {
            Attribute("query", String, "Search phrase")
            Required("query")
        })
        Return(func() {
            Attribute("documents", ArrayOf(String), "Matched snippets")
            Required("documents")
        })
    })
})
```

ツールセットは、標準の `Description()` DSL 関数を使って説明を含めることもできます:

```go
var DataToolset = Toolset("data-tools", func() {
    Description("Tools for data processing and analysis")
    Tool("analyze", "Analyze dataset", func() {
        Args(func() {
            Attribute("dataset_id", String, "Dataset identifier")
            Required("dataset_id")
        })
        Return(func() {
            Attribute("insights", ArrayOf(String), "Analysis insights")
            Required("insights")
        })
    })
})
```

### Tool

`Tool(name, description, dsl)` はツールセット内の呼び出し可能なケイパビリティを定義します。

**コンテキスト**: `Toolset` または `Method` の内部

コード生成は次を出力します:
- `tool_specs/types.go` の payload/result Go 構造体
- JSON コーデック（`tool_specs/codecs.go`）
- プランナーが消費する JSON Schema 定義
- ヘルパープロンプトとメタデータを含むツールレジストリエントリ

```go
Tool("search", "Search indexed documentation", func() {
    Title("Document Search")
    Args(func() {
        Attribute("query", String, "Search phrase")
        Attribute("limit", Int, "Max results", func() { Default(5) })
        Required("query")
    })
    Return(func() {
        Attribute("documents", ArrayOf(String), "Matched snippets")
        Required("documents")
    })
    CallHintTemplate("Searching for: {{ .Query }}")
    ResultHintTemplate("Found {{ len .Result.Documents }} documents")
    Tags("docs", "search")
})
```

### Args と Return

`Args(...)` と `Return(...)` は、標準の Goa 属性 DSL を使って payload/result 型を定義します。

**コンテキスト**: `Tool` の内部

次のいずれかを使用できます:
- `Attribute()` 呼び出しでインラインのオブジェクトスキーマを定義する関数
- 既存の型定義を再利用する Goa のユーザー型（Type, ResultType, etc.）
- 単純な単一値入出力のためのプリミティブ型（String, Int など）

```go
Tool("search", "Search documentation", func() {
    Args(func() {
        Attribute("query", String, "Search phrase")
        Attribute("limit", Int, "Max results", func() {
            Default(5)
            Minimum(1)
            Maximum(100)
        })
        Required("query")
    })
    Return(func() {
        Attribute("documents", ArrayOf(String), "Matched snippets")
        Attribute("count", Int, "Number of results")
        Required("documents", "count")
    })
})
```

`Return` は、service method に result がない method-backed tool では省略できます。その場合 code generation は空の result `TypeSpec`、つまり result schema も result codec もない spec を生成します。executor は JSON 値を捏造せず、`&planner.ToolResult{Name: call.Name}` で成功を報告します。

**型の再利用:**

```go
var SearchParams = Type("SearchParams", func() {
    Attribute("query", String)
    Attribute("limit", Int)
    Required("query")
})

Tool("search", "Search documents", func() {
    Args(SearchParams)
    Return(func() {
        Attribute("results", ArrayOf(String))
    })
})
```

### ServerData

`ServerData(kind, val, args...)` は、tool result に付随して emit される型付き server-only output を定義します。server-data は model provider へは送信されません。
timeline server-data は、model-facing result を bounded で token 効率よく保ちながら、observer-facing UI card、chart、table、map などへ投影されることが多いです。evidence と internal audience により、下流 consumer は kind の命名規約に頼らず provenance や composition-only data を route できます。

**コンテキスト**: `Tool` の内部

**パラメータ:**

- `kind`: server-data kind の文字列識別子 (例: `"metrics.time_series"`, `"control.narrative"`, `"audit.evidence"`)。consumer が異なる server-data projection を識別して扱えるようにします。
- `val`: `Args` と `Return` と同じ pattern に従う schema 定義。`Attribute()` を持つ関数、Goa user type、primitive type のいずれかです。

**Audience routing (`Audience`*):**

各 `ServerData` entry は audience を宣言します。下流 consumer は kind の命名規約に頼らず payload を route できます:

- `"timeline"`: 永続化され、observer-facing projection (UI/timeline card など) の対象になります
- `"internal"`: tool-composition attachment。永続化も render もしません
- `"evidence"`: provenance reference。timeline card とは別に永続化されます

`ServerData` DSL block 内で audience を設定します:

```go
ServerData("metrics.time_series.chart_points", TimeSeriesServerData, func() {
    AudienceInternal()
    FromMethodResultField("chart_sidecar")
})

ServerData("audit.evidence", ArrayOf(Evidence), func() {
    AudienceEvidence()
    FromMethodResultField("evidence")
})
```

**ServerData を使う場面:**

- UI (chart、graph、table) のために full-fidelity data を含めつつ、model payload を bounded に保ちたい場合
- model context limit を超える大きな result set を attach したい場合
- model が見る必要はないが、下流 consumer が structured data を必要とする場合

```go
Tool("get_time_series", "Get time series data", func() {
    Args(func() {
        Attribute("device_id", String, "Device identifier")
        Attribute("start_time", String, "Start timestamp (RFC3339)")
        Attribute("end_time", String, "End timestamp (RFC3339)")
        Required("device_id", "start_time", "end_time")
    })
    Return(func() {
        Attribute("summary", String, "Summary for the model")
        Attribute("count", Int, "Number of data points")
        Attribute("min_value", Float64, "Minimum value in range")
        Attribute("max_value", Float64, "Maximum value in range")
        Required("summary", "count")
    })
    ServerData("metrics.time_series", func() {
        Attribute("data_points", ArrayOf(TimeSeriesPoint), "Full time series data")
        Attribute("metadata", MapOf(String, String), "Additional metadata")
        Required("data_points")
    })
})
```

**Goa type を server-data schema に使う例:**

```go
var TimeSeriesServerData = Type("TimeSeriesServerData", func() {
    Attribute("data_points", ArrayOf(TimeSeriesPoint), "Full time series data")
    Attribute("unit", String, "Measurement unit")
    Attribute("resolution", String, "Data resolution (e.g., '1m', '5m', '1h')")
    Required("data_points")
})

Tool("get_metrics", "Get device metrics", func() {
    Args(func() {
        Attribute("device_id", String, "Device identifier")
        Required("device_id")
    })
    Return(func() {
        Attribute("summary", String, "Metrics summary for the model")
        Attribute("point_count", Int, "Number of data points")
        Required("summary", "point_count")
    })
    ServerData("metrics.query", TimeSeriesServerData)
})
```

**Runtime access:**

実行時には、tool が emit した server-data は `planner.ToolResult.ServerData` に運ばれます。tool が宣言した kind 用の生成 server-data codec で、それらの canonical JSON bytes を decode します:

```go
// In a stream subscriber or result handler
func handleToolResult(result *planner.ToolResult) {
    if len(result.ServerData) > 0 {
        // Decode with the generated server-data codecs for this tool.
    }
}
```

### BoundedResult

`BoundedResult()` は、現在の tool result が潜在的に大きな data set に対する bounded view であることを示します。runtime-owned bounds contract を宣言しつつ、作成者が定義した result type は semantic で domain-specific なままにできます。

**コンテキスト**: `Tool` の内部

正規 model-visible field:

- `returned` (required, `Int`)
- `truncated` (required, `Boolean`)
- `total` (optional, `Int`)
- `refinement_hint` (optional, `String`)
- `next_cursor` (direct `Cursor` contract が `NextCursor(...)` を公開する場合は optional `String`)

`BoundedResult` はこの contract の single source of truth です:

- codegen は生成 `tools.ToolSpec.Bounds` に記録します
- codegen は生成 JSON result schema へ正規 bounded field を project します
- successful bounded execution は `planner.ToolResult.Bounds` を設定する必要があります
- runtime は provider-owned bounds を encoded `tool_result` JSON、result-hint template data、hook、stream event へ project します
- `ContinueWith` は cursor を model contract から除外し、一意な live chain head が続行可能な間だけ引数なし action を公開します。正確な cursor lineage によって連続 page を進めます。source call は並列実行できますが、複数の live head がある間は引数なし action を公開しません
- direct `Cursor` は provider の opaque cursor を `next_cursor` に公開します

```go
Tool("list_devices", "List devices with pagination", func() {
    Args(func() {
        Attribute("site_id", String, "Site identifier")
        Attribute("cursor", String, "前ページが返した次ページ用の opaque cursor")
        Required("site_id")
    })
    Return(func() {
        Attribute("devices", ArrayOf(Device), "List of devices")
        Required("devices")
    })
    BoundedResult(func() {
        Cursor("cursor")
        NextCursor("next_cursor")
    })
})
```

tool-facing return type は、model に見せるためだけに `returned`、`total`、`truncated`、`refinement_hint`、`next_cursor` を宣言してはいけません。semantic result は domain data に集中させてください。method-backed tool は内部的により rich な service method result type を使えますが、Goa-AI tool contract は重複した tool return field ではなく `BoundedResult(...)` から来ます。bound method result の中で required にできるのは `returned` と `truncated` だけです。`total`、`refinement_hint`、`next_cursor` は bounds contract の optional part のままです。

**Service Responsibility:**

service は次を担います:

1. 独自の truncation logic (pagination、limit、depth cap) を適用する
2. `planner.ToolResult.Bounds` を populate する
3. 別ページがある場合に `Bounds.NextCursor` へ provider cursor を設定する
4. result が truncated のとき、任意で `RefinementHint` を提供する

runtime は subset や truncation を計算しません。tool が報告した bounds metadata を validate/project するだけです。

**BoundedResult を使う場面:**

- paginated list (device、user、record) を返す tool
- result limit 付きで large dataset を query する tool
- nested structure に depth/size cap を適用する tool
- result が incomplete かもしれないことを model が理解する必要がある tool

**Runtime Behavior:**

```go
result := &planner.ToolResult{
    Result: &ListDevicesResult{
        Devices: devices,
    },
    Bounds: &agent.Bounds{
        Returned:       len(devices),
        Total:          ptr(total),
        Truncated:      truncated,
        NextCursor:     nextCursor,
        RefinementHint: refinementHint,
    },
}
```

`agent.Bounds.NextCursor` の型は `*string` です。生成 tool spec が paging を宣言し、`Truncated` が true で、pointer の指す cursor が空でない場合だけ設定します。完全な result と paging しない bounded tool では nil のままにしてください。runtime はこの規則に違反する bounds を拒否します。

bounded tool が実行されると:

1. runtime は successful bounded tool が `planner.ToolResult.Bounds` を返したことを検証します。
2. runtime は `BoundedResult(...)` の field name を使い、emitted JSON に bounds を merge します。
3. `ContinueWith` では cursor は runtime 所有のままで、空の action は一意な live chain head に対してのみ公開されます。
4. direct `Cursor` では `Bounds.NextCursor` が `next_cursor` に出力され、model がその opaque value を次の call に指定します。

tool は標準 `Title()` DSL function を使って display title を持てます:

```go
Tool("web_search", "Search the web", func() {
    Title("Web Search")
    Args(func() { /* ... */ })
})
```

### Confirmation

`Confirmation(dsl)` は、ツール実行前に明示的な帯域外承認が必要であることを宣言します。これは **オペレータセンシティブ** なツール（書き込み、削除、コマンド）向けです。

**コンテキスト**: `Tool` の内部

生成時に Goa-AI は確認ポリシーを生成されたツールスペックに記録します。実行時には、確認要求を最初の pending item に含む `RunSuspension` でワークフローが終了します。呼び出し側が `AgentClient.Continue` で明示的な承認を送信した場合にのみ、継続ワークフローがツールを実行します。

最小例:

```go
Tool("dangerous_write", "Write a stateful change", func() {
    Args(DangerousWriteArgs)
    Return(DangerousWriteResult)
    Confirmation(func() {
        Title("Confirm change")
        PromptTemplate(`Approve write: set {{ .key }} to {{ json .value }}`)
        DeniedResultTemplate(`{"summary":"Cancelled","key":{{ json .key }}}`)
    })
})
```

注記:

- 確認の要求方法はランタイムが所有します。最初の pending item の kind が `confirmation` の場合に表示し、`api.PendingInputResponse{Confirmation: ...}` を `AgentClient.Continue` に渡します。期待される payload と実行フローはランタイムガイドを参照してください。
- `PromptTemplate` と `DeniedResultTemplate` は Go の `text/template` 文字列であり、`missingkey=error` で実行されます。標準のテンプレート関数（例: `printf`）に加えて、Goa-AI は次を提供します:
  - `json v` → `v` を JSON エンコード（任意のポインタフィールドや構造化値の埋め込みに有用）
  - `quote s` → Go エスケープ済みの引用文字列を返す（`fmt.Sprintf("%q", s)` と同等）
- `runtime.WithToolConfirmation(...)` によって、実行時に動的に Confirmation を有効化することもできます（環境別ポリシーやデプロイ単位の上書きなど）。

### CallHintTemplate と ResultHintTemplate

`CallHintTemplate(template)` と `ResultHintTemplate(template)` は、ツール呼び出し/結果の表示テンプレート（ヒント）を設定します。テンプレートは Go の `text/template` 文字列で、ツールの型付き payload/result 構造体に対して評価され、実行中および実行後に表示される簡潔なヒントを生成します。

**コンテキスト**: `Tool` の内部

**ポイント:**

- テンプレートは生の JSON ではなく型付き Go 構造体を受け取ります。JSON キー（例: `.query`, `.device_id`）ではなく Go のフィールド名（例: `.Query`, `.DeviceID`）を使ってください。
- ヒントは簡潔に保ちましょう（UI 表示のため、推奨 ≤140 文字）
- テンプレートは `missingkey=error` でコンパイルされます（参照するフィールドは必ず存在している必要があります）
- 任意フィールドには `{{ if .Field }}` や `{{ with .Field }}` を使ってください

**ランタイム契約:**

- hooks のイベントコンストラクタはヒントをレンダリングしません。ツール呼び出しのスケジュールイベントは既定で `DisplayHint==""` です。
- ランタイムは、payload のデコードに成功した場合、型付きテンプレートから公開時に **永続的な** 呼び出しヒントを付与して保存します。
- ツール登録には空でないメタデータ title が必要です。型付きデコードに失敗する、またはテンプレートが登録されていない場合、ランタイムはその title を display hint として使用します。不正な payload は引き続きツール境界で失敗します。メタデータ title は、試行された作業を表示可能に保つためだけに使われます。ヒントは生の JSON に対してレンダリングされません。
- producer が hook イベントを公開する前に `DisplayHint`（非空）を明示的に設定した場合、ランタイムはそれを権威ある値として扱い、上書きしません。
- consumer ごとの文言変更（例: UI の表現）にはランタイムで `runtime.WithHintOverrides` を設定します。override は、ストリームの `tool_start` イベントにおいて DSL テンプレートより優先されます。

**基本例:**

```go
Tool("search", "Search documents", func() {
    Args(func() {
        Attribute("query", String, "Search phrase")
        Attribute("limit", Int, "Maximum results", func() { Default(10) })
        Required("query")
    })
    Return(func() {
        Attribute("count", Int, "Number of results found")
        Attribute("results", ArrayOf(String), "Matching documents")
        Required("count", "results")
    })
    CallHintTemplate("Searching for: {{ .Query }}")
    ResultHintTemplate("Found {{ .Result.Count }} results")
})
```

**型付き構造体フィールド:**

テンプレートは生成された Go の payload/result 構造体を受け取ります。フィールド名は JSON の命名（snake_case/camelCase）ではなく Go の命名（PascalCase）に従います:

```go
// DSL definition
Tool("get_device_status", "Get device status", func() {
    Args(func() {
        Attribute("device_id", String, "Device identifier")      // JSON: device_id
        Attribute("include_metrics", Boolean, "Include metrics") // JSON: include_metrics
        Required("device_id")
    })
    Return(func() {
        Attribute("device_name", String, "Device name")          // JSON: device_name
        Attribute("is_online", Boolean, "Online status")         // JSON: is_online
        Attribute("last_seen", String, "Last seen timestamp")    // JSON: last_seen
        Required("device_name", "is_online")
    })
    // Use Go field names (PascalCase), not JSON keys
    CallHintTemplate("Checking status of {{ .DeviceID }}")
    ResultHintTemplate("{{ .Result.DeviceName }}: {{ if .Result.IsOnline }}online{{ else }}offline{{ end }}")
})
```

**任意フィールドの扱い:**

テンプレートエラーを避けるため、任意フィールドには条件ブロックを使ってください:

```go
Tool("list_items", "List items with optional filter", func() {
    Args(func() {
        Attribute("category", String, "Optional category filter")
        Attribute("limit", Int, "Maximum items", func() { Default(50) })
    })
    Return(func() {
        Attribute("items", ArrayOf(Item), "Matching items")
        Attribute("total", Int, "Total count")
        Attribute("truncated", Boolean, "Results were truncated")
        Required("items", "total")
    })
    CallHintTemplate("Listing items{{ with .Category }} in {{ . }}{{ end }}")
    ResultHintTemplate("{{ .Result.Total }} items{{ if .Result.Truncated }} (truncated){{ end }}")
})
```

**組み込みテンプレート関数:**

ランタイムはヒントテンプレートのために次のヘルパー関数を提供します:

| 関数 | 説明 | 例 |
|----------|-------------|---------|
| `join` | 文字列スライスをセパレータで結合する | `{{ join .Tags ", " }}` |
| `count` | スライスの要素数を数える | `{{ count .Results }} items` |
| `truncate` | 文字列を N 文字で切り詰める | `{{ truncate .Query 20 }}` |

**すべての機能を含む例:**

```go
Tool("analyze_data", "Analyze dataset", func() {
    Args(func() {
        Attribute("dataset_id", String, "Dataset identifier")
        Attribute("analysis_type", String, "Type of analysis", func() {
            Enum("summary", "detailed", "comparison")
        })
        Attribute("filters", ArrayOf(String), "Optional filters")
        Required("dataset_id", "analysis_type")
    })
    Return(func() {
        Attribute("insights", ArrayOf(String), "Analysis insights")
        Attribute("metrics", MapOf(String, Float64), "Computed metrics")
        Attribute("processing_time_ms", Int, "Processing time in milliseconds")
        Required("insights", "processing_time_ms")
    })
    CallHintTemplate("Analyzing {{ .DatasetID }} ({{ .AnalysisType }})")
    ResultHintTemplate("{{ count .Result.Insights }} insights in {{ .Result.ProcessingTimeMs }}ms")
})
```

### ResultReminder

`ResultReminder(text)` は、ツール結果が返った後に会話へ注入される静的なシステムリマインダを設定します。これは、結果の解釈や UI 表示の前提など、モデルが「舞台裏の前提」を理解した上で応答できるようにするために使用します。

**コンテキスト**: `Tool` の内部

リマインダテキストはランタイムにより `<system-reminder>` タグで自動的にラップされます。テキスト内にタグを含めないでください。

**静的 vs 動的リマインダ:**

`ResultReminder` は、毎回適用される設計時（デザイン時）の静的リマインダです。実行時の状態やツール結果に依存する動的リマインダが必要な場合は、プランナー実装で `PlannerContext.AddReminder()` を使用してください。動的リマインダは次をサポートします:
- レート制限（ターン間の最小間隔）
- ランごとの上限（1 ランあたりの最大回数）
- 条件に基づく追加/削除
- 優先度（安全 vs ガイダンス）

**基本例:**

```go
Tool("get_time_series", "Get time series data", func() {
    Args(func() {
        Attribute("device_id", String, "Device identifier")
        Attribute("start_time", String, "Start timestamp")
        Attribute("end_time", String, "End timestamp")
        Required("device_id", "start_time", "end_time")
    })
    Return(func() {
        Attribute("series", ArrayOf(DataPoint), "Time series data points")
        Attribute("summary", String, "Summary for the model")
        Required("series", "summary")
    })
    ResultReminder("The user sees a rendered graph of this data in the UI.")
})
```

**ResultReminder を使う場面:**

- UI がツール結果を特別な表示（チャート/グラフ/テーブル）で描画し、モデルがそれを知る必要があるとき
- すでにユーザーに見えている情報をモデルが繰り返さない方がよいとき
- 表示方法に関する重要な前提が応答の仕方に影響する場合
- 毎回同じガイダンスを適用したい場合

**複数ツールのリマインダ:**

同一ターン内に複数のツールが result reminder を持つ場合、ランタイムは 1 つのシステムメッセージに統合します:

```go
Tool("get_metrics", "Get device metrics", func() {
    Args(func() { /* ... */ })
    Return(func() { /* ... */ })
    ResultReminder("Metrics are displayed as a dashboard widget.")
})

Tool("get_alerts", "Get active alerts", func() {
    Args(func() { /* ... */ })
    Return(func() { /* ... */ })
    ResultReminder("Alerts are shown in a priority-sorted list with severity indicators.")
})
```

**PlannerContext による動的リマインダ:**

実行時条件に依存する場合は、プランナー API を使用します:

```go
// In your planner implementation
func (p *MyPlanner) PlanResume(ctx context.Context, input *planner.PlanResumeInput) (*planner.PlanResult, error) {
    // Add a dynamic reminder based on tool results
    for _, tr := range input.ToolOutputs {
        if tr.Name != specs.GetTimeSeries || tr.Failure != nil {
            continue
        }
        result, err := specs.UnmarshalGetTimeSeriesResult(tr.Result)
        if err != nil {
            return nil, err
        }
        if hasAnomalies(result) {
            input.Agent.AddReminder(reminder.Reminder{
                ID:   "anomaly_detected",
                Text: "Anomalies were detected in the time series. Highlight these to the user.",
                Priority: reminder.TierGuidance,
            })
        }
    }
    // ... rest of planner logic
}
```

### Tags

`Tags(values...)` は、汎用的なポリシーおよび UI フィルタリングのための
フラットなラベルをツールまたはツールセットに付与します。ツールセットのタグは
そのツールに継承されます。

**コンテキスト**: `Tool` または `Toolset` の内部

一般的なタグパターン:
- ドメイン: `"nlp"`, `"database"`, `"api"`, `"filesystem"`
- 能力: `"read"`, `"write"`, `"search"`, `"transform"`
- リスク: `"safe"`, `"destructive"`, `"external"`

```go
Tool("delete_file", "Delete a file", func() {
    Args(func() { /* ... */ })
    Tags("filesystem", "write", "destructive")
})
```

### Meta

Goa 標準 DSL の `Meta(name, values...)` は、現在のツールに名前付きの
設計時アノテーションを付与します。Goa-AI のコード生成はこれを
`ToolSpec.Meta` の `map[string][]string` として保持します。

```go
Tool("resolve_source", "Resolve a selectable source", func() {
    Args(ResolveSourceArgs)
    Return(ResolveSourceResult)
    Meta("example.chat.supplies_tool_input")
})
```

`Meta` は、汎用ポリシーカテゴリではなく、特定のコンシューマが所有する
安定した契約に使用します。メタデータは不活性です。キーが存在するだけで
Goa-AI のスケジューリング、可視性、予算、リトライ、終端動作が変わることは
ありません。キーを解釈するプランナー、ポリシー、または UI が、その意味を
所有して文書化します。

組み込み契約は正規のまま使用してください。

- 汎用的な許可・拒否と能力フィルタリングには `Tags` を使用する
- 計上と終端動作には `Bookkeeping` と `TerminalRun` を使用する
- 結果ごとの失敗処理には `ToolFailure` とその `Recovery` action を使用する
- バッチごとの遷移には `SynthesizeAfterTools` などのプランナーフィールドを使用する

### BindTo

`BindTo("Method")` または `BindTo("Service", "Method")` は、ツールを Goa のサービスメソッドに関連付けます。

**コンテキスト**: `Tool` の内部

ツールがメソッドにバインドされる場合:
- ツールの `Args` スキーマはメソッドの `Payload` と異なってよい
- ツールの `Return` スキーマはメソッドの `Result` と異なってよい
- 生成されるアダプタがツール型とメソッド型の間を変換する

```go
var _ = Service("orchestrator", func() {
    Method("Search", func() {
        Payload(SearchPayload)
        Result(SearchResult)
    })

    Agent("chat", func() {
        Use("docs", func() {
            Tool("search", "Search documentation", func() {
                Args(SearchPayload)
                Return(SearchResult)
                BindTo("Search") // binds to method on same service
            })
        })
    })
})
```

### Inject

`Inject(fields...)` は、特定の payload フィールドをサーバー側で設定するものとしてマークします。注入フィールドは:

1. LLM から隠される（JSON Schema と、モデル向けの必須フィールド一覧から除外される）
2. ツールの実効ペイロード（明示的な `Args()` があればそれ、なければバインド先メソッドの payload）上の必須 `String` フィールドである
3. 生成コードによって、コード生成時に決まる 2 つのソースのいずれかから設定される：`runtime.ToolCallMeta` またはランのラベル

**コンテキスト**: `Tool` の内部

Goify した結果が `runtime.ToolCallMeta` の 5 つの固定フィールド（`run_id`/`runId`、`session_id`/`sessionId`、`turn_id`/`turnId`、`tool_call_id`/`toolCallId`、`parent_tool_call_id`/`parentToolCallId`）のいずれかに一致する名前は **メタデータ由来** となり、そのメタデータを直接読み取るコードにコンパイルされます。それ以外の名前はすべて **ラベル由来** です：ランのラベルを検索するコードにコンパイルされ（ラベルキーは設計上の名前そのまま）、そのフィールド自身に宣言されたバリデーション（`Pattern`、`Length`、enum など）がラベル値に適用され、呼び出し側はラン開始時に `runtime.WithLabels(...)` でその値を渡さなければなりません。ラベル由来のフィールドは `BindTo` ツールでは宣言できません。レジストリ経由で提供されるバインド済みツールが使うレジストリのワイヤープロトコルには、ランのラベルが一切含まれないためです。

```go
Tool("get_data", "Get data for current session", func() {
    Args(func() {
        Attribute("session_id", String, "Current session ID")
        Attribute("query", String, "Data query")
        Required("session_id", "query")
    })
    Return(func() {
        Attribute("data", ArrayOf(String))
    })
    BindTo("data_service", "get")
    Inject("session_id") // メタデータ由来: LLM から隠され、ランタイムが設定する
})

Tool("lookup_household", "Lookup scoped to a household", func() {
    Args(func() {
        Attribute("household_id", String, "Household to scope the search to.", func() {
            Pattern("^[a-z0-9-]+$")
        })
        Attribute("query", String, "Search query.")
        Required("household_id", "query")
    })
    Inject("household_id") // ラベル由来: WithLabels("household_id", ...) で設定する
})
```

コード生成は、注入を行う各ツールごとに `Inject<Tool>` 関数をツールセットの生成済み `inject.go` に出力し、両方の実行トポロジー（インプロセスのローカル executor と、レジストリ経由で提供される provider）はデコードと実行の間でこれを同一に呼び出します。ツールセットは生成済みの `RequiredLabels` 一覧も持ち、`Runtime.Start`/`StartOneShot` はワークフローやアクティビティをスケジュールする前に、`WithLabels(...)` で渡されたラベルをこの一覧と照合し、不足しているキーをすべて 1 つのエラーにまとめて早期に失敗します。

手書きの `ToolCallExecutor`（`BindTo` を持たず、ランタイムに直接登録するツール向け）には、生成済みの呼び出し口がありません。これらのツールの payload は、生の payload コーデックの `FromJSON` ではなく、生成された `Decode<Tool>(payload, meta, labels) (*<Tool>Payload, error)` 関数でデコードするべきです：`Decode<Tool>` はコーデックと `Inject<Tool>` を 1 回の呼び出しに合成するため、注入が黙って省略されることはありません。コーデックだけでデコードすると、注入対象フィールドはワイヤータグが `json:"-"` であるためにエラーなしで Go のゼロ値のまま残ってしまいます。

`WithInterceptors`（[Toolsets](toolsets.md#注入フィールドinjected-fields) 参照）は、`Inject<Tool>` の後に、生成されたサービス executor の型付き payload に対して実行される、ユーザー定義フック用の別の（引き続きサポートされる）仕組みです。これは、設計がどの `Inject()` フィールドを宣言しているかとは独立しています。

### TerminalRun

`TerminalRun()` は現在のツールを run の終端としてマークします。ツールが
成功すると、ランタイムは結果の公開直後に run を終了し、追加の
`PlanResume`/ファイナライズターンを要求しません。Goa-AI はすべての終端
ツールを自動的に bookkeeping として分類するため、宣言は `TerminalRun()`
だけで十分です。

**コンテキスト**: `Tool` の内部

最終的なユーザー向け出力がツール結果そのものであるようなツール（最終レポートのレンダラーや、「この run をコミット」するツールなど）に `TerminalRun()` を使用します。ツール結果がランの終端アーティファクトであり、モデルによる追加のナレーションは不要です。

```go
Tool("commit_task", "タスクの終端アーティファクトをコミット", func() {
    Args(TaskCompletionArgs)
    Return(TaskCompletionResult)
    TerminalRun()
})
```

**ランタイム動作:**

- コード生成はこのフラグを `tools.ToolSpec.TerminalRun` に記録します。
- 終端ツール呼び出しが成功した後、ランタイムは `PlanResume` を呼ばずにランを完了します。
- コード生成はこのツールを bookkeeping としても記録します。したがって終端ツールは retrieval 予算も連続失敗予算も消費せず、retrieval 予算が尽きた後でも受け入れられます。

### Bookkeeping

`Bookkeeping()` は現在のツールを制御記録としてマークします。その成功だけで
次のプランナーターンをスケジュールすることはありません。run レベルの
`MaxToolCalls` retrieval 予算も、連続失敗の許容量も消費しません。

**コンテキスト**: `Tool` の内部

構造化されたステータスマーカー、遷移宣言、findings、終端コミットに
`Bookkeeping()` を使用します。成功後に追加のプランナー推論をスケジュール
すべき snapshot や lookup result には使用しません。

```go
Tool("set_step_status", "ステップのステータスを更新", func() {
    Args(SetStepStatusArgs)
    Return(SetStepStatusResult)
    Bookkeeping()
})
```

**ランタイム動作:**

- コード生成はこのフラグを `tools.ToolSpec.Bookkeeping` に記録します。
- bookkeeping 呼び出しの `MaxToolCalls` コストはゼロで、連続失敗カウンタも変更しません。
- モデルが生成した各 tool-call batch は原子的です。予算対象の全呼び出しが収まる場合は batch 全体を受け入れ、収まらない場合は batch 全体を拒否します。provider response から個々の呼び出しを除去することはありません。
- 呼び出しと結果は durable な stream/run-log event として、また provider transcript 内に残ります。成功した bookkeeping 結果だけが、将来の compact な `ToolOutputs` から除外されます。
- 失敗した bookkeeping 結果は `ToolFailure.Recovery` に従って次の planner turn へ進みます。同じ call を修正する、その tool を使わずに replan する、または終了します。
- 未知のツールは予算対象として扱われます。DSL で `Bookkeeping()` として宣言された（またはランタイムの `ToolSpec` で bookkeeping としてマークされた）ツールのみが免除されます。
- bookkeeping だけのターンは、同一ターン内で解決する必要があります（`TerminalRun()`、`FinalResponse`、`FinalToolResult`、await/pause）。

**終端コミット:**

終端ツールは bookkeeping を暗黙に含むため、終端コミットには
`TerminalRun()` だけが必要です。

```go
Tool("commit_task", "タスクの終端アーティファクトをコミット", func() {
    Args(TaskCompletionArgs)
    Return(TaskCompletionResult)
    TerminalRun()  // retrieval コストなし。成功すると run を終了する
})
```

コミットツールは retrieval 予算が残っていなくても受け入れられます。成功すると、
後続プランナーターンなしで run が終了します。

## ポリシー関数

### RunPolicy

`RunPolicy(dsl)` は、実行時に適用される実行制限を設定します。`Agent` の内部で宣言され、上限、時間予算、履歴管理、中断処理などのポリシー設定を含みます。

**コンテキスト**: `Agent` の内部

**利用可能なポリシー関数:**
- `DefaultCaps` — リソース制限（ツール呼び出し、連続失敗）
- `TimeBudget` — 実行全体の単純なウォールクロック制限
- `Timing` — 予算、計画、ツール実行の詳細タイムアウト（上級）
- `History` — 会話履歴管理（スライディングウィンドウ or 圧縮）
- `InterruptsAllowed` — ヒューマンインザループの一時停止/再開を有効化
- `OnMissingFields` — 必須フィールド欠落時のバリデーション挙動

```go
Agent("chat", "Conversational runner", func() {
    RunPolicy(func() {
        DefaultCaps(
            MaxToolCalls(8),
            MaxRecoveryTurns(3),
        )
        TimeBudget("2m")
        InterruptsAllowed(true)
        OnMissingFields("await_clarification")

        History(func() {
            KeepRecentTurns(20)
        })
    })
})
```

### DefaultCaps

`DefaultCaps(opts...)` は、暴走ループを防止し実行制限を強制するためのケイパビリティ上限を適用します。

**コンテキスト**: `RunPolicy` の内部

```go
RunPolicy(func() {
    DefaultCaps(
        MaxToolCalls(8),
        MaxRecoveryTurns(3),
    )
})
```

**MaxToolCalls(n)**: *予算対象のツール* について許可する最大呼び出し回数。超過した場合、ランタイムは中断します。DSL で `Bookkeeping()` として宣言されたツールはこの上限から免除され、`RemainingToolCalls` を消費しません。そのため、構造化されたステータス更新、進捗マーカー、終端コミットツールは常に実行可能です。

**MaxRecoveryTurns(n)**: ツール結果またはモデル回答が拒否された後に、プランナーを再実行できる最大回数を設定します。予算対象ツールが成功すると、この回数はリセットされます。上限到達後の最終処理呼び出しは `n` に含まれません。

### TimeBudget

`TimeBudget(duration)` は、エージェント実行にウォールクロックの制限を適用します。期間は文字列（例: `"2m"`, `"30s"`）で指定します。

**コンテキスト**: `RunPolicy` の内部

```go
RunPolicy(func() {
    TimeBudget("2m") // 2 minutes
})
```

個々のアクティビティのタイムアウトを細かく制御したい場合は、代わりに `Timing` を使用します。

### Timing

`Timing(dsl)` は、`TimeBudget` の代替として詳細なタイムアウト設定を提供します。`TimeBudget` が単一の全体制限を設定する一方、`Timing` は 3 つのレベルで制御できます: 実行全体の予算、プランナーアクティビティ（LLM 推論）、ツール実行アクティビティ。

**コンテキスト**: `RunPolicy` の内部

**Timing と TimeBudget の使い分け:**
- 単一のウォールクロック制限で十分なら `TimeBudget`
- 計画とツール実行で異なるタイムアウトが必要なら `Timing`（例: ツールが遅い外部 API を叩くが LLM 応答は速くしたい）

```go
RunPolicy(func() {
    Timing(func() {
        Budget("10m")   // overall wall-clock budget for the entire run
        Plan("45s")     // timeout for Plan/Resume activities (LLM inference)
        Tools("2m")     // default timeout for ExecuteTool activities
    })
})
```

`Timing` はランタイムのセマンティック層にとどまります。`Plan(...)`
と `Tools(...)` は、健全なプランナー/ツールの 1 回の試行が開始後に
どれだけ実行できるかを表す予算です。キュー待ちタイムアウトや
heartbeat による liveness など、ワークフローエンジン固有の仕組みは
設定しません。Temporal アダプタを使う場合、それらの仕組みは
`temporal.Options.ActivityDefaults` で設定します。

**Timing 関数:**

| 関数 | 説明 | 影響範囲 |
|----------|-------------|---------|
| `Budget(duration)` | 実行全体のウォールクロック予算 | ランのライフサイクル全体 |
| `Plan(duration)` | Plan/Resume アクティビティのタイムアウト | プランナーの LLM 推論呼び出し |
| `Tools(duration)` | ExecuteTool アクティビティのデフォルトタイムアウト | ツール実行（サービス、MCP、agent-as-tool） |

**Timing がランタイムに与える影響:**

ランタイムはこれらの DSL 値を、エンジン非依存の「試行予算」に変換します:
- `Budget` は run 全体のセマンティックな wall-clock 予算を設定します。
  ランタイムはこの予算をプランナー/ツール作業に適用し、さらにエンジン
  の run timeout を `Budget + FinalizerGrace + エンジンの余裕分`
  として導出します。これにより、最後の `PlanResume` ターンと終端
  クリーンアップにも完了の余地が残ります。
- `Plan` は `PlanStart` / `PlanResume` の試行予算になります
- `Tools` は `ExecuteTool` のデフォルト試行予算になります

Temporal 固有のキュー待ちや liveness の挙動は、Temporal アダプタ側で
別途積み上げられます。

**完全な例:**

```go
Agent("data-processor", "Processes large datasets", func() {
    Use(DataToolset)
    RunPolicy(func() {
        DefaultCaps(MaxToolCalls(20))
        Timing(func() {
            Budget("30m")   // long-running data jobs
            Plan("1m")      // LLM decisions should be quick
            Tools("5m")     // data operations may take time
        })
    })
})
```

### Cache

`Cache(dsl)` は、エージェントのプロンプトキャッシュ挙動を設定します。キャッシュをサポートするプロバイダに対して、システムプロンプトやツール定義のどこにチェックポイント境界を置くべきかを指定します。

**コンテキスト**: `RunPolicy` の内部

プロンプトキャッシュは、プロバイダが以前に処理したコンテンツを再利用できるようにすることで、推論コストとレイテンシを大きく削減できます。`Cache` は、プロバイダが「どこまでがキャッシュ可能か」を判断するための境界を定義します。

```go
RunPolicy(func() {
    Cache(func() {
        AfterSystem()  // checkpoint after system messages
        AfterTools()   // checkpoint after tool definitions
    })
})
```

**キャッシュチェックポイント関数:**

| 関数 | 説明 |
|----------|-------------|
| `AfterSystem()` | すべてのシステムメッセージの後にチェックポイントを置く。プロバイダはこれを「システム前置き直後のキャッシュ境界」と解釈する。 |
| `AfterTools()` | ツール定義の後にチェックポイントを置く。プロバイダはこれを「ツール設定セクション直後のキャッシュ境界」と解釈する。 |

**プロバイダ対応:**

すべてのプロバイダがプロンプトキャッシュをサポートするわけではなく、対応はチェックポイント種別によって異なります:

| Provider | AfterSystem | AfterTools |
|----------|-------------|------------|
| Bedrock (Claude models) | ✓ | ✓ |
| Bedrock (Nova models) | ✓ | ✗ |

キャッシュ非対応のプロバイダはこれらのオプションを無視します。ランタイムはプロバイダ固有の制約も検証します。例えば Nova モデルで `AfterTools` を要求するとエラーになります。

**Cache を使う場面:**

- `AfterSystem()` を使う: システムプロンプトがターン間で安定しており、再処理を避けたい
- `AfterTools()` を使う: ツール定義が安定しており、ツール設定をキャッシュしたい
- 両方を併用する: 対応プロバイダで最大のキャッシュ効果を得たい

**完全な例:**

```go
Agent("assistant", "Conversational assistant", func() {
    Use(DocsToolset)
    Use(SearchToolset)
    RunPolicy(func() {
        DefaultCaps(MaxToolCalls(10))
        TimeBudget("5m")
        Cache(func() {
            AfterSystem()  // cache the system prompt
            AfterTools()   // cache tool definitions (Claude only)
        })
    })
})
```

### History

`History(dsl)` は、プランナーが各アクティビティで初めて `PrepareMessages()` を呼ぶ時に、ランタイムが会話履歴をどう準備するかを定義します。メッセージが不要で呼び出しを省略した場合、ポリシーは実行しません。詳細は[メッセージの準備](../runtime/#preparing-conversation-messages)を参照してください。履歴ポリシーは次を保持しつつ、メッセージ履歴を変換します:

- 会話先頭のシステムプロンプト
- 論理的なターン境界（ユーザー + アシスタント + ツール呼び出し/結果を 1 単位とする）

エージェントごとに設定できる履歴ポリシーは最大 1 つです。

**コンテキスト**: `RunPolicy` の内部

標準ポリシーは 2 つあります:

**KeepRecentTurns（スライディングウィンドウ）:**

`KeepRecentTurns(n)` は、システムプロンプトとツールのやりとりを保ちながら、直近 N 個のユーザー/アシスタントターンのみを保持します。コンテキストサイズを制限する最も簡単な方法です。

```go
RunPolicy(func() {
    History(func() {
        KeepRecentTurns(20) // Keep the last 20 user/assistant turns
    })
})
```

**パラメータ:**
- `n`: 保持する直近ターン数（> 0 必須）

**圧縮（モデル支援の要約）:**

圧縮は、古いターンをモデルで要約しつつ、最新の完全なターンを正確なまま bounded tail として保持します。設定は 2 つの判断に分かれます:

- `CompressAtTurns(n)` と `CompressAtMaxInputTokens(n)` は、いつ要約を実行するかを決めます。両方を設定した場合、どちらか一方の条件で圧縮が開始されます。
- `KeepMaxTurns(n)` と `KeepMaxInputTokens(n)` は、要約後にどの最新の完全なターンを正確に残すかを決めます。両方を設定した場合、両方の上限が適用されます。

token budget は tokenization が model 固有であるため、設定済み history model を通じて runtime が数えます。1 回の count には、保持する system message、候補となる完全な turn、現在 advertised されている tool が含まれます。`CompressAtMaxInputTokens` は exclusive です。request が threshold と同じなら収まり、それを超えると compression を開始します。`KeepMaxInputTokens` は turn を途中で切りません。runtime は最新 turn から逆向きに走査し、budget に収まる完全な turn だけを保持します。

```go
RunPolicy(func() {
    History(func() {
        CompressAtMaxInputTokens(120_000)
        KeepMaxInputTokens(40_000)
        KeepMaxTurns(12)
    })
})
```

**圧縮関数:**
- `CompressAtTurns(n)`: 少なくとも `n` 個の論理ターンがあるときに開始します。
- `CompressAtMaxInputTokens(n)`: provider-visible transcript が `n` 入力トークンを超えたときに開始します。
- `KeepMaxTurns(n)`: 最新の完全なターンを最大 `n` 個まで正確に保持します。
- `KeepMaxInputTokens(n)`: 実行時の入力トークン数が `n` に収まる最新の完全なターンを保持します。

圧縮には、少なくとも 1 つの `CompressAt...` トリガーと、少なくとも 1 つの `KeepMax...` 保持予算が必要です。

**HistoryModel の要件:**

圧縮を使う場合、生成されるエージェント設定の `HistoryModel` field に `model.Client` を渡す必要があります。runtime はこの client を `ModelClassSmall` とともに使い、古い turn を要約し、token budget が設定されている場合は provider-visible request を count します。token-budget compression には正確な `model.TokenCounter` 対応が必要です。Bedrock は利用可能な場合 Runtime `CountTokens` を使いますが、structured-output request と Claude Opus 4.7、Sonnet 5、Mythos 5 では `model.ErrTokenCountingUnsupported` を返します。これらは AWS の別の Mantle endpoint を必要とします:

```go
cfg := chat.ChatAgentConfig{
    Planner:      &ChatPlanner{},
    HistoryModel: smallModelClient,
}
if err := chat.RegisterChatAgent(ctx, rt, cfg); err != nil {
    log.Fatal(err)
}
```

DSL の値は生成されるデフォルトです。選択したモデルの context window や運用予算が異なる場合、デプロイ時に上書きできます:

```go
cfg := chat.ChatAgentConfig{
    Planner:      &ChatPlanner{},
    HistoryModel: smallModelClient,
    HistoryCompression: &runtime.HistoryCompressionConfig{
        CompressAtMaxInputTokens: 180_000,
        KeepMaxInputTokens:       60_000,
        KeepMaxTurns:             16,
    },
}
```

圧縮が設定されているにもかかわらず `HistoryModel` が提供されない場合、登録は失敗します。

**ターン境界の保持:**

どちらのポリシーも論理的なターン境界を「原子単位」として保持します。ターンは次で構成されます:
1. ユーザーメッセージ
2. アシスタントの応答（テキストおよび/またはツール呼び出し）
3. その応答に紐づくツール結果

これにより、モデルは常に完全な相互作用シーケンスを見られ、文脈を混乱させる部分的なターンは見ません。

### InterruptsAllowed

`InterruptsAllowed(bool)` は、ヒューマンインザループの割り込みを尊重すべきであることを示します。有効にすると、ランタイムは一時停止/再開（pause/resume）をサポートし、確認/明確化ループや durable await 状態に重要となります。

**コンテキスト**: `RunPolicy` の内部

**主な利点:**
- 必須情報が不足している場合に、実行を **一時停止** できる（`OnMissingFields` 参照）
- 明確化ツール経由でユーザー入力を **待機** できる
- 一時停止中は状態が保持され、再開まで計算資源を消費しない

```go
RunPolicy(func() {
    // Enable pause/resume capability
    InterruptsAllowed(true)

    // Automatically pause when required tool arguments are missing
    OnMissingFields("await_clarification")
})
```

### OnMissingFields

`OnMissingFields(action)` は、ツール呼び出しのバリデーションで必須フィールドの欠落が検出されたときに、エージェントがどう応答するかを設定します。

**コンテキスト**: `RunPolicy` の内部

有効値:
- `"finalize"`: 必須フィールドがない場合に実行を停止する
- `"await_clarification"`: 一時停止し、ユーザーが不足情報を提供するのを待つ
- `"resume"`: 不足があっても実行を継続する
- `""`（空）: コンテキストに基づいてプランナーに判断させる

```go
RunPolicy(func() {
    OnMissingFields("await_clarification")
})
```

### 完全なポリシー例

```go
Agent("chat", "Conversational runner", func() {
    RunPolicy(func() {
        DefaultCaps(
            MaxToolCalls(8),
            MaxRecoveryTurns(3),
        )
        Timing(func() {
            Budget("5m")
            Plan("30s")
            Tools("1m")
        })
        InterruptsAllowed(true)
        OnMissingFields("await_clarification")
        History(func() {
            CompressAtMaxInputTokens(120_000)
            KeepMaxInputTokens(40_000)
            KeepMaxTurns(12)
        })
    })
})
```

---

## MCP 関数

Goa-AI は、Goa サービス内で Model Context Protocol（MCP）サーバを宣言するための DSL 関数を提供します。

### MCP

`MCP(name, version, opts...)` は現在のサービスで MCP を有効化します。MCP プロトコルを通じてツール、リソース、プロンプトを公開するようサービスを構成します。


**コンテキスト**: `Service` の内部

```go
Service("calculator", func() {
    Description("Calculator MCP server")

    // MCP を使う例
    MCP("calc", "1.0.0")
    JSONRPC(func() {
        POST("/mcp")
    })

    Method("add", func() {
        Payload(func() {
            Attribute("a", Int, "First number")
            Attribute("b", Int, "Second number")
            Required("a", "b")
        })
        Result(func() {
            Attribute("sum", Int, "Result of addition")
            Required("sum")
        })
        Tool("add", "Add two numbers")
    })
})
```

### ProtocolVersion

`ProtocolVersion(version)` は、サーバがサポートする MCP プロトコルバージョンを設定します。`MCP` に渡す設定関数を返します。

**コンテキスト**: `MCP` へのオプション引数

```go
Service("calculator", func() {
    // Use the default protocol supported by Goa-AI.
    MCP("calc", "1.0.0")
    JSONRPC(func() {
        POST("/mcp")
    })
})
```

### Tool (in Method Context)

`Tool(name, description)` は、現在のメソッドを MCP ツールとしてマークします。メソッドの payload がツールの入力スキーマになり、result が出力スキーマになります。

**コンテキスト**: `Method` の内部（サービスは MCP が有効である必要があります）

```go
Method("search", func() {
    Payload(func() {
        Attribute("query", String, "Search query")
        Attribute("limit", Int, "Maximum results", func() { Default(10) })
        Required("query")
    })
    Result(func() {
        Attribute("results", ArrayOf(String), "Search results")
        Required("results")
    })
    Tool("search", "Search documents by query")
})
```

### Toolset(FromMCP(...))

`Toolset(FromMCP(service, toolset))` は、Goa の MCP サーバから導出された MCP 定義ツールセットを宣言します。

**コンテキスト**: トップレベル

利用パターンは 2 つあります。

**Goa バックエンドの MCP サーバ:**

```go
var AssistantSuite = Toolset(FromMCP("assistant", "assistant-mcp"))

var _ = Service("orchestrator", func() {
    Agent("chat", func() {
        Use(AssistantSuite)
    })
})
```

**外部 MCP サーバ（インラインスキーマ定義）:**

```go
var RemoteSearch = Toolset("remote-search", FromExternalMCP("remote", "search"), func() {
    Tool("web_search", "Search the web", func() {
        Args(func() { Attribute("query", String) })
        Return(func() { Attribute("results", ArrayOf(String)) })
    })
})

Agent("helper", "", func() {
    Use(RemoteSearch)
})
```

### Resource

`Resource(name, uri, mimeType)` は、メソッドを MCP リソースプロバイダとしてマークします。

**コンテキスト**: `Method` の内部（サービスは MCP が有効である必要があります）

```go
Method("readme", func() {
    Result(String)
    Resource("readme", "file:///docs/README.md", "text/markdown")
})
```

### StaticPrompt

`StaticPrompt(name, description, messages...)` は静的プロンプトテンプレートを追加します。

**コンテキスト**: `Service` の内部

```go
Service("assistant", func() {
    MCP("assistant", "1.0")
    JSONRPC(func() {
        POST("/mcp")
    })

    StaticPrompt("greeting", "Friendly greeting",
        "system", "You are a helpful assistant",
        "user", "Hello!")
})
```

### 完全な MCP サーバ例

```go
var _ = Service("assistant", func() {
    Description("MCP server example")

    MCP("assistant", "1.0.0")
    JSONRPC(func() {
        POST("/mcp")
    })

    StaticPrompt("greeting", "Friendly greeting",
        "system", "You are a helpful assistant",
        "user", "Hello!")

    Method("search", func() {
        Description("Search documents")
        Payload(func() {
            Attribute("query", String, "Search query")
            Required("query")
        })
        Result(func() {
            Attribute("results", ArrayOf(String), "Search results")
            Required("results")
        })
        Tool("search", "Search documents by query")
    })

    Method("get_readme", func() {
        Result(String)
        Resource("readme", "file:///README.md", "text/markdown")
    })

})
```

---

## レジストリ関数

Goa-AI は、ツールレジストリ（MCP サーバ、ツールセット、エージェントの中央カタログ）を宣言・消費するための DSL 関数を提供します。レジストリは発見され、エージェントによって消費できます。

### Registry

`Registry(name, dsl)` は、ツール発見のためのレジストリソースを宣言します。

**コンテキスト**: トップレベル

DSL 内で次を使用します:
- `URL`: レジストリエンドポイント URL（必須）
- `Description`: 人間可読の説明
- `APIVersion`: レジストリ API バージョン（デフォルト `"v1"`）
- `Security`: 認証のための Goa セキュリティスキーム参照
- `Timeout`: HTTP リクエストタイムアウト
- `Retry`: 失敗時のリトライポリシー
- `SyncInterval`: カタログ更新間隔
- `CacheTTL`: ローカルキャッシュ保持期間
- `Federation`: 外部レジストリの取り込み設定

```go
var CorpRegistry = Registry("corp-registry", func() {
    Description("Corporate tool registry")
    URL("https://registry.corp.internal")
    APIVersion("v1")
    Security(CorpAPIKey)
    Timeout("30s")
    Retry(3, "1s")
    SyncInterval("5m")
    CacheTTL("1h")
})
```

**設定オプション:**

| 関数 | 説明 | 例 |
|----------|-------------|---------|
| `URL(endpoint)` | レジストリエンドポイント URL（必須） | `URL("https://registry.corp.internal")` |
| `APIVersion(version)` | API バージョンのパスセグメント | `APIVersion("v1")` |
| `Timeout(duration)` | HTTP リクエストタイムアウト | `Timeout("30s")` |
| `Retry(maxRetries, backoff)` | 失敗したリクエストのリトライポリシー | `Retry(3, "1s")` |
| `SyncInterval(duration)` | カタログ更新間隔 | `SyncInterval("5m")` |
| `CacheTTL(duration)` | ローカルキャッシュ保持期間 | `CacheTTL("1h")` |

### Federation

`Federation(dsl)` は外部レジストリ取り込み設定を構成します。Registry 宣言内で Federation を使い、フェデレーションされたソースから取り込むネームスペースを指定します。

**コンテキスト**: `Registry` の内部

Federation DSL 内で使用します:
- `Include`: 取り込むネームスペースのグロブパターン
- `Exclude`: スキップするネームスペースのグロブパターン

```go
var AnthropicRegistry = Registry("anthropic", func() {
    Description("Anthropic MCP Registry")
    URL("https://registry.anthropic.com/v1")
    Security(AnthropicOAuth)
    Federation(func() {
        Include("web-search", "code-execution", "data-*")
        Exclude("experimental/*", "deprecated/*")
    })
    SyncInterval("1h")
    CacheTTL("24h")
})
```

**Include と Exclude:**

- `Include(patterns...)`: 取り込むネームスペースのグロブパターンを指定します。Include が指定されない場合、デフォルトではすべてのネームスペースを含みます。
- `Exclude(patterns...)`: スキップするネームスペースのグロブパターンを指定します。Exclude は Include の後に適用されます。

### FromRegistry

`FromRegistry(registry, toolset)` は、レジストリ由来のツールセットとして構成します。Toolset を宣言する際のプロバイダオプションとして `FromRegistry` を使用します。

**コンテキスト**: `Toolset` への引数

```go
var CorpRegistry = Registry("corp", func() {
    URL("https://registry.corp.internal")
})

// Basic usage - toolset name derived from registry toolset name
var RegistryTools = Toolset(FromRegistry(CorpRegistry, "data-tools"))

// With explicit name
var MyTools = Toolset("my-tools", FromRegistry(CorpRegistry, "data-tools"))

// With additional configuration
var ConfiguredTools = Toolset(FromRegistry(CorpRegistry, "data-tools"), func() {
    Version("1.2.3")
})
```

レジストリ由来のツールセットは、標準の `Version()` DSL 関数で特定バージョンに固定できます:

```go
var CorpRegistry = Registry("corp", func() {
    URL("https://registry.corp.internal")
})

var PinnedTools = Toolset("stable-tools", FromRegistry(CorpRegistry, "data-tools"), func() {
    Version("1.2.3")
})
```

名前付き `Toolset(FromRegistry(...))` を `Use` で利用するか、`Registry` 全体を直接利用できます。`Use` 内に `Deferred()` を追加するとネイティブ検索を使います。生成済みエージェントは各計画アクティビティで現在の契約を1回解決します。構築済みの分散レジストリと Pulse クライアントを `rt.RegisterRegistry` で接続します。`Definition()` と `NewClient(rt)` にカタログ引数はありません。

プロバイダーは確認、ページネーション、フィールド情報、サーバー専用データを含む `ToolSchemas()` を公開します。動的なサービスツールは保存済み契約を使い、エージェント・制御連携はコンパイル済みのままです。名前付きソースは存在し、指定版と一致する必要があります。レジストリ全体は空でも有効です。重複・重なりのある利用やレジストリ参照内のインライン・エクスポート宣言は拒否されます。

現在のソース解決、生成済み契約、プロバイダー動作、移行については[ツール検索と動的カタログ](../tool-search/)を参照してください。

### PublishTo

`PublishTo(registry)` は、エクスポートされたツールセットをレジストリへ公開する設定を行います。Export DSL 内で `PublishTo` を使用して公開先レジストリを指定します。

**コンテキスト**: `Toolset` の内部（エクスポートされるとき）

```go
var CorpRegistry = Registry("corp", func() {
    URL("https://registry.corp.internal")
})

var LocalTools = Toolset("utils", func() {
    Tool("summarize", "Summarize text", func() {
        Args(func() { Attribute("text", String) })
        Return(func() { Attribute("summary", String) })
    })
})

Agent("data-agent", "Data processing agent", func() {
    Use(LocalTools)
    Export(LocalTools, func() {
        PublishTo(CorpRegistry)
        Tags("data", "etl")
    })
})
```

### 完全なレジストリ例

```go
package design

import (
    . "goa.design/goa/v3/dsl"
    . "goa.design/goa-ai/dsl"
)

// Define registries
var CorpRegistry = Registry("corp-registry", func() {
    Description("Corporate tool registry")
    URL("https://registry.corp.internal")
    APIVersion("v1")
    Security(CorpAPIKey)
    Timeout("30s")
    Retry(3, "1s")
    SyncInterval("5m")
    CacheTTL("1h")
})

var AnthropicRegistry = Registry("anthropic", func() {
    Description("Anthropic MCP Registry")
    URL("https://registry.anthropic.com/v1")
    Federation(func() {
        Include("web-search", "code-execution")
        Exclude("experimental/*")
    })
    SyncInterval("1h")
    CacheTTL("24h")
})

// Consume toolsets from registries
var DataTools = Toolset(FromRegistry(CorpRegistry, "data-tools"), func() {
    Version("2.1.0")
})

var SearchTools = Toolset(FromRegistry(AnthropicRegistry, "web-search"))

// Local toolset to publish
var AnalyticsTools = Toolset("analytics", func() {
    Tool("analyze", "Analyze dataset", func() {
        Args(func() {
            Attribute("dataset_id", String, "Dataset identifier")
            Required("dataset_id")
        })
        Return(func() {
            Attribute("insights", ArrayOf(String), "Analysis insights")
            Required("insights")
        })
    })
})

var _ = Service("orchestrator", func() {
    Agent("analyst", "Data analysis agent", func() {
        Use(DataTools)
        Use(SearchTools)
        Use(AnalyticsTools)

        // Export and publish to registry
        Export(AnalyticsTools, func() {
            PublishTo(CorpRegistry)
            Tags("analytics", "data")
        })

        RunPolicy(func() {
            DefaultCaps(MaxToolCalls(10))
            TimeBudget("5m")
        })
    })
})
```

---

## 次のステップ

- **[ランタイム](./runtime.md)** - 設計が実行時の挙動にどう変換されるかを理解する
- **[ツールセット](./toolsets.md)** - ツールセットの実行モデルを深掘りする
- **[MCP 連携](./mcp-integration.md)** - MCP サーバのランタイム配線を理解する

