> ## Documentation Index
> Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt
> Use this file to discover all available pages before exploring further.

# リファインメント

> AdCP プロダクトリファインメント — get_products のリファインモードを使って、発見したプロダクトやプロポーザルを繰り返し改善します。メディアバイを作成する前に選択内容を調整し、変更をリクエストします。

リファインメントは、プロダクト発見を対話形式に変える仕組みです。最初の `brief` または `wholesale` による発見の後、`buying_mode: "refine"` を使って特定のプロダクトやプロポーザルを繰り返し改善できます。選択内容の調整、変更のリクエスト、代替案の探索などを行ってから、[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) で確定します。

## リファインメントのライフサイクル

典型的なメディアバイのワークフローは以下のパターンに従う:

```
discover → refine → refine → ... → buy
```

1. **Discover** — `buying_mode: "brief"` または `"wholesale"` で `get_products` を呼び出し、マッチするインベントリを見つける。セラーはプロダクト（オプションでプロポーザルも）を返します。

2. **Refine** — `buying_mode: "refine"` と変更リクエストの `refine` 配列を指定して `get_products` を呼び出す。各エントリはスコープと、バイヤーが求める内容を宣言します。セラーは更新された価格と設定を持つプロダクトを返します。

3. **Repeat** — 必要な回数だけリファインを繰り返します。各呼び出しは独立しており、ステートレスです。

4. **Buy** — 満足したら、`create_media_buy` で最終的な選択を実行します。

<Tip>
  リファインメントは必須ではありません。シンプルなキャンペーンは発見から購入へ直接進むことができます。ただし、複数のプロダクトを含むキャンペーン、予算配分を含むプロポーザル、または反復的な交渉が必要な場合、リファインメントこそが価値を生む場面です。
</Tip>

## refine 配列

`refine` 配列は変更リクエストのリストです。各エントリは `scope` と、バイヤーが求める内容を宣言する:

| スコープ       | 目的               | 必須フィールド       |
| ---------- | ---------------- | ------------- |
| `request`  | 選択全体に対する方向性      | `ask`         |
| `product`  | 特定のプロダクトへのアクション  | `product_id`  |
| `proposal` | 特定のプロポーザルへのアクション | `proposal_id` |

`refine` 配列には少なくとも1つのエントリが必要です。セラーはレスポンスを構成する際にすべてのエントリを総合的に考慮し、`refinement_applied` を通じて各エントリに返答します。

各スコープは独自の id フィールドを使います——プロダクトエントリには `product_id`、プロポーザルエントリには `proposal_id` で、AdCP が他のあらゆる場所で使っている id の命名規約に合わせています。`action` はプロダクトおよびプロポーザルエントリでは任意で、デフォルトは `"include"` です。

### プロダクトアクション

プロダクトスコープのエントリはアクションを宣言してもよい。省略した場合、セラーはそのエントリを `"include"` として扱う:

| アクション            | 動作                                       | `ask`                                                |
| ---------------- | ---------------------------------------- | ---------------------------------------------------- |
| `include`（デフォルト） | このプロダクトを更新された価格とデータで返す                   | オプション — リクエストする具体的な変更（例: "add 16:9 format"）          |
| `omit`           | このプロダクトをレスポンスから除外する                      | 無視される                                                |
| `more_like_this` | このプロダクトに類似した追加のプロダクトを見つける。元のプロダクトも返されます。 | オプション — 「類似」の意味（例: "same audience but video format"） |

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "product_id": "prod_video_premium", "ask": "add 16:9 format option" },
    { "scope": "product", "product_id": "prod_display_ros", "action": "omit" },
    { "scope": "product", "product_id": "prod_native", "action": "more_like_this", "ask": "same audience but video format" }
  ]
}
```

### リクエストレベルの方向性

`scope: "request"` を使って、選択全体に対して求める内容を記述する:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "request", "ask": "good selection but I want more video options and less display" },
    { "scope": "product", "product_id": "prod_video_premium" },
    { "scope": "product", "product_id": "prod_display_ros" },
    { "scope": "product", "product_id": "prod_native" }
  ]
}
```

セラーはこの方向性に基づいてプロダクトを追加・削除・再バランスしてもよい。`refine` 配列で参照されていないプロダクトも、セラーが方向性に合うと判断した場合はレスポンスに含まれることがあります。

**優先順位**: プロダクト個別のアクションはリクエストレベルの方向性より優先されます。リクエストレベルで「ディスプレイを減らして」と指定していても、特定のプロダクトにそれを含める明示的なアクションが設定されていれば、そのプロダクトは必ず返されます。

### プロポーザルのリファインメント

プロポーザルを `proposal_id` で参照して、調整や削除をリクエストする。プロダクトエントリと同様に、`action` はデフォルトで `"include"` になる:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "product_id": "prod_video_premium" },
    { "scope": "product", "product_id": "prod_display_ros" },
    { "scope": "proposal", "proposal_id": "prop_balanced_v1", "ask": "shift 20% from display to video" }
  ]
}
```

### スコープの組み合わせ

すべてのスコープは組み合わせて使える。1回のリファインメント呼び出しで、選択への方向性の設定、特定プロダクトへのアクション、プロポーザルへの変更リクエストを同時に行うことができる:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "request", "ask": "increase emphasis on video across the plan" },
    { "scope": "product", "product_id": "prod_video_premium" },
    { "scope": "product", "product_id": "prod_display_ros" },
    { "scope": "product", "product_id": "prod_native", "action": "omit" },
    { "scope": "product", "product_id": "prod_audio_spot" },
    { "scope": "proposal", "proposal_id": "prop_awareness_q2", "ask": "reallocate native budget to video products" }
  ],
  "filters": {
    "budget_range": { "min": 200000, "max": 200000, "currency": "USD" }
  }
}
```

## セラーのレスポンス

バイヤーが `refine` 配列を送信すると、セラーは `refinement_applied` で応答します。これはバイヤーの変更リクエストと位置が一致する配列です。各エントリはリクエストが満たされたかどうかを報告する:

| フィールド         | 型      | 必須                            | 説明                                                                        |
| ------------- | ------ | ----------------------------- | ------------------------------------------------------------------------- |
| `scope`       | string | Yes                           | 対応する `refine` エントリのスコープ（`"request"` / `"product"` / `"proposal"`）をエコーします。 |
| `product_id`  | string | `scope` が `"product"` の場合は必須  | 対応するリファインエントリの `product_id` をエコーします。                                      |
| `proposal_id` | string | `scope` が `"proposal"` の場合は必須 | 対応するリファインエントリの `proposal_id` をエコーします。                                     |
| `status`      | string | Yes                           | `"applied"`: リクエスト充足。`"partial"`: 部分的に充足。`"unable"`: 充足できなかった。            |
| `notes`       | string | No                            | セラーの説明。`"partial"` または `"unable"` の場合に推奨されます。                             |

```json theme={null}
{
  "products": ["..."],
  "proposals": ["..."],
  "refinement_applied": [
    { "scope": "request", "status": "applied", "notes": "Added 3 video products. No CTV inventory for those dates." },
    { "scope": "product", "product_id": "prod_video_premium", "status": "applied" },
    { "scope": "product", "product_id": "prod_display_ros", "status": "applied" },
    { "scope": "product", "product_id": "prod_native", "status": "applied" },
    { "scope": "product", "product_id": "prod_audio_spot", "status": "partial", "notes": "16:9 not available for this placement — returning 4:3 and 1:1" },
    { "scope": "proposal", "proposal_id": "prop_awareness_q2", "status": "applied", "notes": "Shifted 22% to video (nearest allocation boundary)" }
  ]
}
```

`refinement_applied` 配列は `refine` 配列と同じ数のエントリを同じ順序で含まなければなりません（MUST）。各エントリは、オーケストレーターがアライメントをクロスバリデーションできるよう、`scope` と対応する id（プロダクトスコープでは `product_id`、プロポーザルスコープでは `proposal_id`）をエコーしなければなりません（MUST）。このフィールド全体はオプションであり、リクエストごとの結果を追跡しないセラーは省略してもよい——ただし、それを返すセラーは、有効で位置が一致したエントリを返さなければなりません（MUST）。

オーケストレーターは、位置の順序だけを信頼するのではなく、エコーされた id でエントリをクロスチェックすべきだ（SHOULD）——そうでなければ、エントリを並べ替えてしまうセラーのバグが、各結果を黙って取り違えてしまいます。

## よくあるリファインメントパターン

### 類似プロダクトを見つける

`more_like_this` を使って、気に入ったプロダクトに類似したプロダクトを発見します。セラーは元のプロダクトに加えて、その特性に合った追加の選択肢を返します:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "product_id": "prod_video_premium", "action": "more_like_this", "ask": "same premium audience but different formats" }
  ]
}
```

### フィルターを調整します

リファインリクエストのフィルターは、差分ではなく完全な目標状態を表します。適用したいフィルターセット全体を常に送信すること:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "product_id": "prod_video_premium" },
    { "scope": "product", "product_id": "prod_display_ros" }
  ],
  "filters": {
    "start_date": "2026-04-01",
    "end_date": "2026-06-30",
    "budget_range": { "min": 150000, "max": 150000, "currency": "USD" }
  }
}
```

### プロポーザルを絞り込む、または拡張します

プロダクトエントリは、セラーがプロポーザルに対して考慮すべきプロダクトを定義します。プロポーザルエントリと組み合わせることで、プロポーザルのプロダクトセットを絞り込んだり拡張したりできる:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "product_id": "prod_video_premium" },
    { "scope": "product", "product_id": "prod_display_ros" },
    { "scope": "proposal", "proposal_id": "prop_balanced_v1", "ask": "rebalance for just these two products" }
  ]
}
```

## Finalize は `refine[]` 内で排他的

`action: "finalize"` はバイヤーの受諾ではなくセラーのコミットです——ドラフトのプロポーザルを、確定した価格と expires\_at の保留ウィンドウを持つコミット済みへ遷移させます。バイヤーは後で `create_media_buy(proposal_id)` を通じて、コミット済みのプロポーザルを受諾/実行します。バイヤーがファイナライズしたい場合、仕様は `refine[]` 配列が finalize エントリ**のみ**を含むことを要求します:

* いずれかのエントリが `action: "finalize"` を持つ場合、配列内の**すべて**のエントリがプロポーザルスコープで `action: "finalize"` でなければなりません（MUST）。finalize を `include` / `omit` エントリと、またはリクエストスコープやプロダクトスコープのエントリと混在させることは、セラーによって `INVALID_REQUEST` で拒否されなければなりません（MUST）。
* リファインメント*と*コミットを近接した連続で行う必要があるバイヤーは、呼び出しを**順序立てて**行います: まずリファイン呼び出し（finalize なし）、次に結果の `proposal_id` に対する finalize 呼び出しです。この二つの意図は別個の決定であり、仕様はそれらを別個の呼び出しとして扱います。

```yaml theme={null}
# ✅ Refine only — no finalize present, mixed scopes allowed
refine:
  - { scope: proposal, proposal_id: p1, ask: "shift more to ctv" }
  - { scope: request, ask: "frequency cap 3/day across all products" }

# ✅ Finalize only — multiple finalize entries against different proposals
refine:
  - { scope: proposal, proposal_id: p1, action: finalize }
  - { scope: proposal, proposal_id: p2, action: finalize }

# ❌ Rejected — finalize mixed with non-finalize
refine:
  - { scope: proposal, proposal_id: p1, action: finalize }
  - { scope: proposal, proposal_id: p2, ask: "shift more to ctv" }
```

**複数ファイナライズは観測点でアトミックです。** 複数の finalize エントリが一つの呼び出しで異なるプロポーザルを対象とする場合、契約は次のとおりです: セラーは、名指しされたすべてのプロポーザルが完了しコミット済みとして永続化されていない限り、成功レスポンスを返してはなりません（MUST NOT）。コミット前のバリデーションは、いかなる副作用（インベントリのプル、条件のロック、ガバナンスのアテステーション）よりも前に実行されます。いずれかのプロポーザルがバリデーションに失敗した場合、セラーはどれもコミットせずに呼び出し全体を拒否しなければなりません（MUST）。`unfinalize` 操作はありません——アトミック性はコミット後の巻き戻しではなく、コミット前のバリデーションゲートで担保されます。アトミックなコミット前バリデーションを保証できないセラーは、複数ファイナライズの配列を `MULTI_FINALIZE_UNSUPPORTED`（推奨——クライアント側のミスではなくセラー側のケイパビリティのギャップを明示的に示します）または `INVALID_REQUEST`（3.1 より前のエラーカタログのセラー向けの許容されるフォールバック）で拒否しなければならず（MUST）、バイヤーはその場合、単一ファイナライズの呼び出しを順序立てて行うべきです（SHOULD）。

**コミット途中の失敗（バリデーション後、永続化前）。** 下流のシステムがコミット 1 とコミット 2 の間で失敗した場合——例えば、最初のシステムがすでにインベントリをロックした後に 2 番目のアドサーバーがタイムアウトした場合——セラーは、位置ごとの結果を運ぶ `refinement_applied[]` とともに `INTERNAL_ERROR` を返さなければなりません（MUST）。仕様はリカバリのパスを定義**しません**: バイヤーは、結果として生じる状態を未定義として扱い、リトライの前に `get_media_buys` / 同等の手段で再読み込みすべきです（SHOULD）。このケースからのリカバリは運用上のものであり、プロトコルが定義するものではありません。

**バイヤーの意図に関する注意。** 意図が明確にアトミックなコミットを必要とするバイヤー（例: 一方だけがファイナライズされると意味を成さない予算共有のプロポーザル）は、セラーが `MULTI_FINALIZE_UNSUPPORTED` を返した場合にその意図を放棄する用意がなければなりません（MUST）。フォールバックのパス——単一ファイナライズの呼び出しを順序立てて行うこと——は、元のアトミックな意図よりも緩いコミットの保証です。その意図の喪失に対しては、より緩い保証を受け入れるか、コミットを完全に断念する以外のリカバリはありません。複数ファイナライズのサポートを示すケイパビリティフラグはありません——失敗レスポンスが発見のためのサーフェスなので、バイヤーは最初の試行が成功するまでサポートを前提としてはなりません（MUST NOT）。

## リファインモードにおけるプロポーザル

セラーはバイヤーがプロポーザルエントリを含めなかった場合でも、リファインされたプロダクトと一緒にプロポーザルを返してもよい（MAY）。例えば、3つのプロダクトをリファインしているバイヤーが、それらのプロダクトを更新された価格で受け取ると同時に、それらを組み合わせる方法を提案するプロポーザルも受け取ることがあります。

重要なポイント:

* **プロポーザルは保証されない。** セラーはリファインモードでプロポーザルを生成することを要求されない。配分とキャンペーン最適化は主にオーケストレーター（バイヤーサイドエージェント）の責任です。
* **リクエストレベルの ask で関心を示します。** `{ "scope": "request", "ask": "suggest how to combine these products" }` を含めることで、プロポーザルを歓迎することを示せる。
* **求めていないプロポーザルはリファインするか無視できます。** セラーがリクエストしていないプロポーザルを返した場合、フォローアップ呼び出しでリファインするか、単純に無視して `create_media_buy` でパッケージを手動で構築することができます。

パブリッシャーは通常、バイヤーが自身でターゲティングと配分を指示する `wholesale` モードではプロポーザルを省略します。

## ステートレス性

`buying_mode: "refine"` を使った各 `get_products` リクエストは独立しています。各リクエストの `refine` 配列と `filters` がリファインメントの意図を完全に指定します。セールスエージェントはトランスポートレベルのセッション状態（例: 前のリクエストで送信された内容を記憶すること）に依存してはなりません（MUST NOT）。

セラーは引き続き独自のプロダクトおよびプロポーザルレジストリを管理します。「ステートレス」とは、*プロトコル交換*が呼び出し間で暗黙的な状態を持たないことを意味します。

この設計により以下が可能になる:

* **ステートレス実装** — セラーはリファインメントセッションを追跡する必要がない
* **安全なリトライ** — 失敗したリファインメント呼び出しは同じパラメーターで再試行できます
* **並列探索** — オーケストレーターが複数のリファインメントパスを同時に探索できます

## クライアントバリデーション

オーケストレーターはリファインメントリクエストを送信前にバリデーションすべきだ（SHOULD）:

* **空でない refine** — `refine` 配列には少なくとも1つのエントリが必要です。空の `[]` はスキーマバリデーションで拒否されます。
* **有効なエントリ** — 各プロダクトエントリには `scope` と `product_id` が必要です。各プロポーザルエントリには `scope` と `proposal_id` が必要です。リクエストレベルエントリには `scope` と `ask` が必要です。`action` はプロダクトおよびプロポーザルエントリでは任意です（デフォルトは `"include"`）。有効な値は、プロダクトでは `include` / `omit` / `more_like_this`、プロポーザルでは `include` / `omit` / `finalize` です。
* **フィルターは絶対値** — 前のリクエストからの差分ではなく、適用したいフィルターセット全体を送信すること。

クライアント実装は送信前に[リクエストスキーマ](/docs/building/schemas-and-sdks)に対してリファインメントリクエストをバリデーションすべきだ（SHOULD）。

## エラーハンドリング

| エラーコード                       | 発生条件                                                                          | 対処法                                                                                |
| ---------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `PRODUCT_NOT_FOUND`          | 参照されたプロダクト ID の1つ以上が不明または期限切れ                                                 | 無効な ID を削除して再試行するか、`brief` リクエストで再発見する                                             |
| `PROPOSAL_EXPIRED`           | 参照されたプロポーザル ID が `expires_at` を過ぎている                                          | 新しい `brief` または `wholesale` リクエストで再発見する                                            |
| `PROPOSAL_NOT_FOUND`         | 参照された `proposal_id` がセラーにとって不明（ファイナライズされたことがない、テナントが誤っている、またはキャッシュから追い出された）   | `action: 'finalize'` 付きの `refine` モードで `get_products` を再発行し、現在の proposal\_id を取得する |
| `MULTI_FINALIZE_UNSUPPORTED` | `refine[]` が複数の `action: 'finalize'` エントリを運んだが、セラーが複数プロポーザルのアトミックなコミットを保証できない | 単一プロポーザルのファイナライズ呼び出しを順序立てて行う（`get_products` 呼び出しごとに 1 つの finalize）                 |
| `INVALID_REQUEST`            | `brief` または `wholesale` モードで `refine` が指定された、`refine` 配列が空、または必須フィールドが欠落している  | `buying_mode` と必須フィールドを確認する                                                        |

### トラブルシューティング: `refine[].id` に対する "must NOT have additional properties"

`refine[]` の各スコープ分岐は `additionalProperties: false` です。これは、3.0-rc より前のリファインの形からの余分な `id` フィールドが——黙って無視されるのではなく——次のようなエラーで拒否されることを意味します:

```
/refine/0: must NOT have additional properties { additionalProperty: "id" }
/refine/0: must match oneOf schema { required: ["product_id"] }
```

これが見えた場合、オーケストレーターがまだ汎用の `id` フィールドでプロダクトまたはプロポーザルのリファインエントリを構築しています。`scope: "product"` の下では `product_id` へ、`scope: "proposal"` の下では `proposal_id` へリネームしてください。現在の形については[タスクリファレンス](/docs/media-buy/task-reference/get_products#refine-array)を参照してください。セラー側でエコーしている場合、同じリネームが `refinement_applied[]` にも適用されます。

### セラーのマイグレーション

`refinement_applied` を返すセラーには、バイヤーと並行して破壊的な作業があります:

* 各レスポンスエントリは、今や `scope` を運ばなければならず、プロダクト/プロポーザルスコープについては `product_id` / `proposal_id` をエコーしなければなりません。フラットな `{status, notes}` エントリはレスポンススキーマによって拒否されます。
* 受信する `refine[]` エントリで `action` が欠けている場合、エラーとしてパースするのではなく、`action: "include"` として扱わなければなりません。
* 3.0 リクエストスキーマに対するセラーの適合性テストは、まだ汎用の `id` フィールドを使う残存したオーケストレーターのペイロードを拒否します——アップグレード後はフィクスチャのコーパスを更新してください。

## 規範的要件

[メディアバイ仕様](/docs/media-buy/specification#get_products) はリファインメントに関して以下の規範的要件を定義している:

**オーケストレーター:**

* `buying_mode` が `"refine"` の場合、`refine` を含めなければなりません（MUST）
* `buying_mode` が `"brief"` または `"wholesale"` の場合、`refine` を含めてはなりません（MUST NOT）
* 各プロダクトエントリに `scope` と `product_id` を、各プロポーザルエントリに `scope` と `proposal_id` を提供しなければなりません（MUST）
* プロダクトおよびプロポーザルエントリの `action` を省略してもよい（MAY）——セラーは欠けている `action` を `"include"` として扱います
* 1つの `refine` 配列に同じプロダクト ID またはプロポーザル ID を持つ複数のエントリを含めてはなりません（MUST NOT）

**セールスエージェント:**

* `action: "omit"` のプロダクトをレスポンスから除外しなければなりません（MUST）
* `action: "omit"` のプロポーザルをレスポンスから除外しなければなりません（MUST）
* `action: "include"` のプロダクトを更新された価格で返さなければなりません（MUST）
* `action: "include"` のプロダクトエントリの `ask` を満たすべきだ（SHOULD）
* `action: "more_like_this"` のプロダクトに類似した追加のプロダクトを元のプロダクトと共に返すべきだ（SHOULD）
* レスポンスを構成する際にリクエストレベルの ask を考慮すべきだ（SHOULD）。これにより、明示的に参照されたプロダクト以外の追加プロダクトが含まれることがある（MAY）。プロダクト個別のアクションはリクエストレベルの方向性より優先されます。
* `action: "include"` のプロポーザルエントリの `ask` を満たすべきだ（SHOULD）
* バイヤーが `refine` を提供する場合、位置で一致した1エントリあたりの変更リクエストを持つ `refinement_applied` をレスポンスに含めるべきだ（SHOULD）
* バイヤーがプロポーザルエントリを含めなかった場合でもプロポーザルを返してもよい（MAY）

## 関連情報

* [`get_products` タスクリファレンス](/docs/media-buy/task-reference/get_products#refinement) — リクエスト/レスポンススキーマを含む API リファレンス
* [メディアプロダクト](/docs/media-buy/product-discovery/media-products) — プロダクトモデルとプロポーザル構造
* [メディアバイ仕様](/docs/media-buy/specification#get_products) — 規範的要件
* [オーケストレーター設計](/docs/building/operating/orchestrator-design) — バイヤーサイドエージェントの構築
