Skip to main content
クリエイティブプレビューを統合するための高度なワークフロー、キャッシュ戦略、実装上の注意点を紹介します。 基本的な使い方は preview_creative を参照してください。

Common Workflows

フォーマットショーケースページ

利用可能なフォーマットを閲覧できるカタログを構築します。
// 1. List all formats from creative agent
const formats = await creative_agent.list_creative_formats();

// 2. Generate format card previews (batch + HTML)
const formatPreviews = await creative_agent.preview_creative({
  request_type: "batch",
  output_format: "html",
  requests: formats.formats.map(format => ({
    format_id: format.format_id,
    creative_manifest: format.format_card.manifest
  }))
});

// 3. Render in a grid
function FormatCatalog({ formatPreviews }) {
  return (
    <div className="format-grid">
      {formatPreviews.results.map((result, idx) => (
        result.success && (
          <div
            key={idx}
            className="format-card"
            dangerouslySetInnerHTML={{
              __html: result.response.previews[0].renders[0].preview_html
            }}
          />
        )
      ))}
    </div>
  );
}

キャンペーンレビュ―用グリッド

配信前にすべてのクリエイティブを確認します。
const campaignCreatives = await getCreativesForCampaign(campaignId);

const previews = await creative_agent.preview_creative({
  request_type: "batch",
  output_format: "html",
  requests: campaignCreatives.map(c => ({
    format_id: c.format_id,
    creative_manifest: c.manifest
  }))
});

function CampaignReview({ previews }) {
  return (
    <div className="review-grid">
      {previews.results.map((result, idx) => (
        <div className="creative-card">
          <div dangerouslySetInnerHTML={{
            __html: result.response.previews[0].renders[0].preview_html
          }}/>
          <button onClick={() => approve(idx)}>Approve</button>
          <button onClick={() => reject(idx)}>Reject</button>
        </div>
      ))}
    </div>
  );
}

Web コンポーネントとの統合

遅延読み込みを行う本番アプリケーション向けの例です。
<script src="https://creative.adcontextprotocol.org/static/rendered-creative.js"></script>

<div class="grid">
  <rendered-creative
    src="https://creative-agent.example.com/preview/abc123"
    width="300"
    height="400"
    lazy="true">
  </rendered-creative>
</div>
メリット:
  • CSS 分離のための Shadow DOM
  • ビューポートに入ったときだけ読み込む遅延読み込み
  • フレームワークに依存しない

出力形式の選択

以下のケースでは output_format: "url"(デフォルト)を使用します。
  • セキュリティが最優先(サードパーティ製クリエイティブなど)
  • インタラクティブなプレビューツールを構築する場合
  • iframe での分離が必要な場合
以下のケースでは output_format: "html" を使用します。
  • 10 件以上のフォーマットカタログを構築する場合
  • 20 件以上のクリエイティブを並べるキャンペーンレビューグリッドを作る場合
  • サーバーサイドレンダリングを行う場合
  • 信頼できるクリエイティブエージェントのみを扱う場合

キャッシュ戦略

format_id とマニフェストのハッシュの組み合わせで個別のプレビュー結果をキャッシュします。
function cachePreviewResults(results, formatIds, manifests) {
  results.forEach((result, idx) => {
    if (result.success) {
      const cacheKey = `${formatIds[idx]}:${hashManifest(manifests[idx])}`;
      cache.set(cacheKey, result.response, result.response.expires_at);
    }
  });
}

async function getPreviewsWithCache(formatIds, manifests) {
  const cached = [];
  const toFetch = [];

  formatIds.forEach((id, idx) => {
    const cacheKey = `${id}:${hashManifest(manifests[idx])}`;
    const cachedResult = cache.get(cacheKey);

    if (cachedResult && !isExpired(cachedResult.expires_at)) {
      cached[idx] = cachedResult;
    } else {
      toFetch.push({ idx, id, manifest: manifests[idx] });
    }
  });

  // Batch fetch only missing previews
  if (toFetch.length > 0) {
    const fetched = await client.preview_creative({
      request_type: "batch",
      output_format: "html",
      requests: toFetch.map(f => ({
        format_id: f.id,
        creative_manifest: f.manifest
      }))
    });

    fetched.results.forEach((result, i) => {
      cached[toFetch[i].idx] = result.response;
    });
  }

  return cached;
}
ポイント:
  • バッチではなく format_id + マニフェストハッシュごとにキャッシュする
  • [A,B,C] をリクエストしたらそれぞれ個別にキャッシュする
  • 後で [B,C,D] をリクエストしたら D だけ取得する
  • キャッシュしたプレビューを使う前に必ず expires_at を確認する

エラーハンドリング

const response = await client.preview_creative({
  request_type: "batch",
  requests: formatRequests
});

const succeeded = response.results.filter(r => r.success);
const failed = response.results.filter(r => !r.success);

if (failed.length > 0) {
  console.log(`${failed.length} previews failed`);
  failed.forEach((result) => {
    console.error(`  - ${result.error.code}: ${result.error.message}`);
  });
}

// Display successful previews, show error states for failures
function displayPreviews(results) {
  return results.map((result, idx) => {
    if (result.success) {
      return <Preview html={result.response.previews[0].renders[0].preview_html} />;
    } else {
      return <PreviewError
        code={result.error.code}
        message={result.error.message}
        onRetry={() => retryPreview(idx)}
      />;
    }
  });
}

単一リクエストからバッチへの移行

以前(逐次):
previews = []
for format in formats:
    preview = await client.preview_creative(
        request_type="single",
        format_id=format.format_id,
        creative_manifest=format.format_card.manifest
    )
    previews.append(preview)
# Total time: N × 250ms = 5000ms for 20 formats
移行後(バッチ):
response = await client.preview_creative(
    request_type="batch",
    output_format="html",
    requests=[
        {
            "format_id": fmt.format_id,
            "creative_manifest": fmt.format_card.manifest
        }
        for fmt in formats
    ]
)
# Total time: ~500ms for 20 formats

ユースケースパターン

デバイス別バリエーション

{
  "inputs": [
    { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } },
    { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } },
    { "name": "CTV", "macros": { "DEVICE_TYPE": "ctv" } }
  ]
}

地域別バリエーション

{
  "inputs": [
    { "name": "NYC", "macros": { "CITY": "New York", "DMA": "501" } },
    { "name": "LA", "macros": { "CITY": "Los Angeles", "DMA": "803" } }
  ]
}

プライバシー対応テスト

{
  "inputs": [
    { "name": "Full consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "CPc7TgP..." } },
    { "name": "No consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "" } },
    { "name": "LAT enabled", "macros": { "LIMIT_AD_TRACKING": "1" } }
  ]
}

AI 生成コンテンツのバリエーション

{
  "inputs": [
    { "name": "Morning commute", "context_description": "User commuting to work" },
    { "name": "Evening relaxation", "context_description": "User relaxing at home" }
  ]
}

実装メモ

クリエイティブエージェント向け

必須:
  1. preview_url から完全な HTML ページを返す
  2. すべてのメディアタイプ(画像・動画・音声・インタラクティブ)を処理する
  3. 入力パラメーターをレスポンスにエコーする
  4. レンダリング前にマニフェストを検証する
  5. マクロ値を適用する(またはデフォルトを使用)
  6. セキュリティサンドボックスを実装する
  7. 適切な有効期限を設定する(24–48 時間)
オプションの拡張:
  • hints オブジェクトを提供する(メディアタイプ、寸法、時間など)
  • embedding メタデータを提供する(サンドボックス方針、CSP)
  • レスポンシブデザインをサポートする
  • アクセシビリティ要素を含める

バイヤー向け

  1. preview_url を iframe で表示するだけで特別なレンダリングは不要
  2. 特定シナリオでは inputs 配列を利用する
  3. input フィールドを確認しマクロ適用を検証する
  4. 承認のためプレビュー URL をクライアントと共有する
  5. 高度なテストには interactive_url を活用する

パブリッシャー向け

  1. プレビュー URL から一貫した HTML を返す
  2. レスポンシブなプレビューページを実装する
  3. フォーマット内の supported_macros で対応マクロを明記する
  4. プレビューと本番の違いを明確にする
  5. テスト用に interactive_url の提供を検討する

関連ドキュメント