ウィジェットの設定(EDS)
このガイドでは、EDS ウィジェットをエンドツーエンドで作成する方法について説明します。LLM Apps UIでのアクションの設定から、EDS プロジェクトの設定、LLM プラットフォーム内でのデータをレンダリングするブロックコードの記述まで。 概要については、 コアコンセプト を参照してください。
ザ LLM Apps SDK
すべてが@adobe/llmapps-sdk npm パッケージで始まります。 SDKは、ウィジェットとLLM ホスト間の双方向通信チャネルを強化するJavaScript ライブラリです。
SDKには、aem-embed.jsも含まれています。これは、SDKを標準のEDS ブロックパイプラインに接続するEDS固有のエントリポイントです。 npm install @adobe/llmapps-sdkの場合、インストール後のスクリプトによって、2つのファイルがプロジェクトに自動的にコピーされます。
scripts/
└── llm-apps/
├── aem-embed.js ← EDS widget entry point, ships with the SDK
└── llmapps-sdk.js ← core SDK, loaded internally by aem-embed.js
EDS プロジェクトでは、ブロックコードでSDKを直接使用することはできません。 aem-embed.jsは、SDK接続を作成および管理し、完全に接続されたLLMApp インスタンスをdecorate(block, bridge)のbridge引数としてブロックに渡します。 完全なSDK APIはbridgeで利用できます。インポートは必要ありません。
EDS (標準バンドラーまたはTypeScript プロジェクト)を使用せずにウィジェットを作成する場合は、SDKを直接使用できます。
import { LLMApp } from '@adobe/llmapps-sdk';
const app = new LLMApp({ appInfo: { name: 'MyWidget', version: '1.0.0' } });
await app.connect();
const { structuredContent } = await app.toolResult;
アドビのツールと統合すれば
AIがアクションを呼び出し、ハンドラーがstructuredContentを返すと、LLM プラットフォームは会話内でインタラクティブウィジェットをレンダリングします。 この連携を実現するには、次の3つの要素が必要です。
LLM Apps UI — アクションを作成する際、「ウィジェットのメタデータ」タブに スクリプト URL と ウィジェット URL を入力します。 スクリプト URLはaem-embed.jsを指しています。これは、SDKに付属し、scripts/llm-apps/aem-embed.jsにあるEDS リポジトリ内にあるファイルです。 これは、アクションが呼び出されたときに読み込むスクリプトをLLM プラットフォームに伝えます。
aem-embed.js — LLM プラットフォームは、このスクリプトをサンドボックス化されたウィジェットサーフェスに読み込みます。 aem-embed.jsは、ウィジェットのEDS対応エントリポイントとして機能するカスタム HTML要素(<aem-embed>)です。 SDKを使用してLLM ホストとのハンドシェイクを実行し、通常のEDS ページパイプライン(ヘッダー/フッターなし)を抑制し、Widget URLからEDS ページコンテンツを取得し、EDS ブロックパイプラインを実行し、各ブロックのdecorate()関数にライブ bridge オブジェクトを配信します。
ブロックコード — decorate(block, bridge)関数を書き出す標準のEDS ブロックを作成します。 bridgeは、接続されたSDK インスタンスです。アクションの構造化された結果を提供し、メッセージを会話に送り返すことができます。
既存のEDS プロジェクトに追加
既にEDS プロジェクトがある場合、ブロックの書き込みを開始するには2つの手順しかありません。
-
@adobe/llmapps-sdkをインストールします。 インストール後のスクリプトはaem-embed.jsとllmapps-sdk.jsをscripts/llm-apps/にコピーします:code language-bash npm install @adobe/llmapps-sdk -
LLM プラットフォームがクロスオリジンでウィジェットページとスクリプトを読み込めるようにCORS ヘッダーを設定します。以下のCORS ヘッダーの設定を参照してください。
次に、decorate(block, bridge) コントラクト に従ってブロックを作成し、ウィジェットページをオーサリングして、アクションを作成ダイアログにURLを入力します。
新しいEDS プロジェクトの設定
リポジトリの作成
-
AEM ボイラープレート テンプレートに基づいて、新しいGitHub リポジトリを作成します。
-
AEM Code Sync GitHub Appをリポジトリに追加します。
-
ローカル開発
npm install -g @adobe/aem-cli用のAEM CLIをインストールします。 -
@adobe/llmapps-sdkをインストールします。 インストール後のスクリプトはaem-embed.jsとllmapps-sdk.jsをscripts/llm-apps/にコピーします:code language-bash npm install @adobe/llmapps-sdk
EDS プロジェクトに関する完全なガイドについては、AEM developer tutorialおよびproject anatomyを参照してください。
設定が完了すると、EDS サイトは次の場所で利用できます。
- プレビュー:
https://main--<repo>--<owner>.aem.page/ - ライブ:
https://main--<repo>--<owner>.aem.live/
リポジトリ構造
my-brand-eds/
├── scripts/
│ ├── llm-apps/
│ │ ├── aem-embed.js # Widget entry point — copied by post-install
│ │ └── llmapps-sdk.js # Core SDK — copied by post-install
│ ├── aem.js # AEM core library
│ └── scripts.js # Site-level decoration and loading
├── blocks/
│ └── search-products/ # One folder per widget block
│ ├── search-products.js
│ └── search-products.css
├── styles/
│ └── styles.css
├── head.html
└── package.json
CORS ヘッダーの設定
EDS ウィジェットページは、LLM プラットフォームによってサンドボックス化されたウィジェットサーフェス内に読み込まれます。 ホストがウィジェットのコンテンツをクロスオリジンで取得できるように、EDS サイトは正しいaccess-control-allow-origin ヘッダーを返す必要があります。
ヘッダーは、Configuration Serviceを使用して、admin.hlx.pageのAEM管理パネルを介して設定されます。 ウィジェットページとSDK スクリプトが存在するパスにカスタム応答ヘッダーを追加します。
{
"/<your-widget-pages-path>/**": [
{ "key": "access-control-allow-origin", "value": "*" }
],
"/scripts/**": [
{ "key": "access-control-allow-origin", "value": "*" }
]
}
*をオリジン値として使用することは、.aem.live ドメイン上の公開ウィジェットコンテンツで許容されます。 サイトに保護されたコンテンツが含まれる場合は、オリジンを特定のドメインに制限します。ウィジェットページの作成
EDS オーサリングツールでページを作成し、ブロックを追加します。 ページ URLは、アクションで設定した ウィジェット URL になります。これは、アクションとブロックの間の唯一の接続です。 ブロックとアクション名の間に名前を付ける必要はありません。
アクションを作成ダイアログでURLを入力します
EDS リポジトリを設定したら、アクションの作成時に Widget Metadata → Template URL に移動します。
スクリプト URL — EDS リポジトリ内のaem-embed.jsを指します。 これは、同じEDS プロジェクト内のすべてのアクションに対して同じ値です。
https://main--<repo>--<owner>.aem.live/scripts/llm-apps/aem-embed.js
ウィジェット URL – このウィジェット用に作成したEDS ページのURL。 アクションごとに一意:
https://main--<repo>--<owner>.aem.live/<path-to-your-widget-page>
LLM プラットフォームは、スクリプト URLからaem-embed.jsを読み込みます。 次に、aem-embed.jsはウィジェット URLから.plain.htmlを取得して、ブロック コンテンツを取得します。
データフロー
ハンドラーからレンダリングされたウィジェットへの完全なパス:
- アクションハンドラーが
structuredContentを返します:
// actions/search-products/index.js
return {
structuredContent: {
products: [
{ id: 'COF-001', name: 'Single Origin Ethiopian Coffee', price: '$18', rating: 4.7 },
{ id: 'COF-002', name: 'Colombia Huila Natural', price: '$22', rating: 4.5 },
],
total: 2,
category: 'coffee'
}
};
-
LLM プラットフォームがウィジェットサーフェスを開き、スクリプト URLから
aem-embed.jsを読み込みます。 -
aem-embed.jsはSDKを介してホストに接続し、Widget URLから.plain.htmlを取得し、EDS ブロックパイプラインを実行し、ブロックでdecorate(block, bridge)を呼び出します。 -
ブロックは
bridge.toolResultからデータを読み取り、UIをレンダリングします。 -
ユーザーインタラクションが
bridge.sendMessage(...)またはbridge.callTool(...)をトリガーし、会話のフォローアップを送信します。
decorate(block, bridge)契約
すべてのEDS ウィジェットブロックは、デフォルトのdecorate関数を書き出す必要があります。 これは、2番目の引数で拡張された標準的なEDS ブロック署名です。接続されたbridgeは、使用可能な完全なAPIを備えたLLMApp SDK インスタンスです。
export default async function decorate(block, bridge) {
// ...
}
bridgeは、LLM プラットフォーム ウィジェット サーフェス内で実行する場合にのみ存在します。 ブラウザーまたはローカル開発サーバーで直接プレビューする際にも、ブロックがレンダリングされるように、常にブリッジ呼び出しを保護します。
アクション結果からのデータのレンダリング
bridge.toolResultは、structuredContentを含む、ハンドラーが返した結果の全体を解決するPromiseです。
const SAMPLE_PRODUCTS = [
{ id: 'COF-001', name: 'Single Origin Ethiopian Coffee', price: '$18', rating: 4.7 },
];
export default async function decorate(block, bridge) {
let products = SAMPLE_PRODUCTS;
if (bridge) {
const result = await bridge.toolResult;
products = result?.structuredContent?.products ?? [];
}
block.innerHTML = products.map(p => `
<div class="product-card">
<h3>${p.name}</h3>
<p class="price">${p.price}</p>
<button data-id="${p.id}">Tell me more</button>
</div>
`).join('');
}
ホストテーマの適用
decorateの早い段階でbridge.applyHostStyles()を呼び出して、ホストのCSS変数とフォント(ライト/ダークテーマ、タイポグラフィ)をウィジェットに挿入します。 これにより、ウィジェットは周囲のLLM プラットフォーム UIと視覚的に一致します。
export default async function decorate(block, bridge) {
if (bridge) {
bridge.applyHostStyles();
}
// ...
}
実行時にテーマの変更に対応するには(例えば、ユーザーがライトモードとダークモードを切り替えるとき):
if (bridge) {
bridge.onContextChange(ctx => {
block.dataset.theme = ctx.theme; // 'light' | 'dark'
});
}
フォローアップメッセージの送信
bridge.sendMessage(text)さんが会話にユーザーメッセージを挿入します。 これは、商品カードをクリックして詳細を確認するなど、ウィジェットトリガーがAIとのやり取りを促進する主な方法です。
block.querySelectorAll('button[data-id]').forEach(btn => {
btn.addEventListener('click', () => {
bridge.sendMessage(`Show me details for product ${btn.dataset.id}`);
});
});
別のアクションを直接呼び出す
bridge.callTool(name, args)は、ユーザーメッセージを経由せずに、ウィジェット内から別のアクションを呼び出します。 関連データをオンデマンドで読み込むのに便利です。
btn.addEventListener('click', async () => {
const result = await bridge.callTool('get-product-details', { id: product.id });
renderDetails(result.structuredContent);
});
ウィジェットの自動サイズ変更
LLM プラットフォームは、レポート内容に応じてウィジェットのサイズを調整します。 bridge.autoResize(element)を使用して、コンテンツの変更に合わせてウィジェットの高さを同期させます。内部ではResizeObserverを使用します。 最初のレンダリング後に呼び出します。
export default async function decorate(block, bridge) {
// ... render content ...
if (bridge) {
bridge.autoResize(block);
}
}
または固定サイズを手動で報告します。
bridge.reportSize(block.offsetWidth, block.offsetHeight);
プレビューモードとローカル開発
ブラウザーまたはローカル開発サーバーでEDS ページを直接プレビューする場合、bridgeはundefinedです。 上記のサンプルデータフォールバックパターンを使用して、ライブハンドラーなしでブロックがすぐにレンダリングされるようにします。
ローカル開発サーバーを起動するには:
npm install -g @adobe/aem-cli
aem up
これでhttp://localhost:3000が開き、ウィジェットページに移動して、サンプルデータを使用したブロックのレンダリングを確認できます。 ブロック JSおよびCSSの変更は、すぐに反映されます。