アクションをゼロから作成 create-action-from-scratch
decorate関数、EDS プロジェクト構造などの基本を学びます。プラットフォームが作成しなかった機能を追加します。 LLM Appsでアクションを定義し、リンクされたリポジトリにそのハンドラーを書き込み、必要に応じてウィジェットを追加します。
このガイドでは、 アクションを定義し、そのハンドラーを実装し、オプションのウィジェットを接続する方法について説明します。 アクションをローカルにテストし、アプリをデプロイし、サポートされているLLM プラットフォームでアクションを試します。
最初のアプリの場合は、 インフラストラクチャの設定から開始し、 アクションの作成を行います。 アクションの作成ガイドでは、web サイトからの自動生成と手動での作成について説明します。
始める前に
次のものが必要です。
- 既存のLLM アプリ。
- リンクされたハンドラーリポジトリ。
- 依存関係がインストールされたリポジトリのローカルクローン。
- アクションがウィジェットを表示する場合のEDS プロジェクト。
- 生産結果を取得するための明確なAPIまたはデータソース。
アクションの計画
ひとつの明確なユーザータスクをもとに、各アクションをデザインします。 UIを開く前に、以下を定義します。
- インテント — ユーザーが達成しようとしているもの。
- 説明 — アクションを選択するタイミング。
- Inputs — ユーザーに必要な最小情報。
- 結果 — ハンドラーによって返されるテキストおよび構造化データ。
- ビヘイビアー — アクションがデータの読み取り、データの変更、または外部システムの呼び出しを行うかどうか。
- Widget – 結果に視覚的なインターフェイスが必要かどうか。
例えば、次のプランを製品を検索 アクションに使用します。
Intent: Find products matching a category or search phrase
Inputs:
category: optional string
query: optional string
Result:
content: text summary
structuredContent: products and total count
Behavior: read-only, idempotent, open-world
Widget: product cards
関連するタスクと異なるタスクの区別。 タスクの情報、リスク、確認要件が異なるため、製品検索と製品購入では個別のアクションを使用します。
アクションメタデータの作成
アプリを開いて アクション を選択し、アクションの作成を選択します。
作成ダイアログには、アクションと ウィジェットメタデータ のタブが含まれています。 各セクションを展開して、フィールドを設定します。
基本情報を入力
次を入力 :
- アクション名 — 製品の検索などの短いタスク名。
- 説明 — アクションを使用するタイミングと返される内容を説明します。
このダイアログでは、名前から コード識別子 が派生します(例:search-products)。 識別子は作成後に修正され、アプリ内で一意である必要があります。 ハンドラーフォルダーに正確な識別子を使用します。
便利な説明は次のとおりです。
Search the product catalog by category or keyword. Returns matching
products with their names, prices, categories, and image URLs.
「製品情報を取得します」といった曖昧な説明は避けます。 LLM プラットフォームは、説明をもとにアクションを選択します。
注釈を選択
注釈は、アクションの動作を説明します。
- 破壊的なヒント — アクションはデータを削除または完全に変更できます。
- Idempotent (same args = no extra effect) – 同じリクエストを繰り返すと、同じ効果が得られます。
- Open world hint — アクションは外部システムと通信します。
- 読み取り専用ヒント — アクションでデータが変更されることはありません。
trueの注釈のみを選択します。 たとえば、商品検索は通常、読み取り専用、等価、オープンワールドです。
OpenAI メタデータの追加
アクションの実行中および完了後に表示される短いメッセージを入力します。
Invoking: Searching products...
Invoked: Products found
ウィジェットを使用するアクションの場合は、ウィジェットの説明を追加します。 ウィジェットの説明は、アクションの説明とは異なります。
- アクションの説明は、モデルがアクションを呼び出すタイミングを決定するのに役立ちます。
- ウィジェットの説明は
_meta["openai/widgetDescription"]にマッピングされ、レンダリングされたコンポーネントが示す内容を要約して、繰り返しナレーションを減らします。
LLM Appsは、ウィジェットの説明をコンポーネントメタデータとして適用します。 ハンドラーから説明を返さないでください。
表示を設定
- AI モデルに公開すると、モデルはアクションを選択できます。
- アプリ サーフェスでウィジェットとして表示すると、設定されたウィジェットが表示されます。
テキストのみのアクションの場合は、ウィジェットメタデータ > タイプの下の ウィジェットなし も選択します。 表示コントロールでは、アクションのウィジェットの種類は選択されません。
入力パラメーターの追加
ハンドラーが受け入れる値ごとに1つのパラメーターを追加します。 各パラメーターには次のものが必要です。
- 名前 — ハンドラーが受け取るキー。
- Type – 文字列、数値、整数またはブール値。
- 説明 – 値の抽出方法。
- 必須 — アクションに値が必要かどうか。
製品を検索の場合:
category
Type: String
Required: No
Description: Product category used to narrow the catalog.
query
Type: String
Required: No
Description: Product name or search phrase.
安定したパラメーター名を使用します。 名前を変更するには、ハンドラーとそのテストも変更する必要があります。
分析の設定 configure-analytics
アクションに至った会話の概要をAnalyticsに含める場合は、ユーザーインテントを収集を有効にします。
独自のAdobe AnalyticsまたはCustomer Journey Analyticsを設定すると、ユーザーインテントもそこに送信されます。 Adobe AnalyticsとCustomer Journey Analyticsの設定を参照してください。
ユーザーインテント収集は、デフォルトで有効になっています。 ユーザーインテントの説明を確認します。概要をリクエストする手順が含まれています。 機密性のない匿名の意図に指示を制限し、プライバシーポリシーにデータ収集を記述します。 説明は最大2,000文字まで使用できます。
完全なフィールド定義については、 アクションとウィジェットフィールド を参照してください。
ウィジェットの設定
テキストのみのアクションの場合は、Widget Metadataを開き、Typeを No widget に設定します。 次に、残りのウィジェット設定をスキップします。
ウィジェットメタデータを開きます。
設定:
- Type — インタラクティブ ウィジェットの Edge Delivery Services ブロック を選択します。
- Widget domain — ウィジェットのサンドボックスの生成元。 UIでは、アプリの提出に必要な項目とアプリごとに一意の項目が識別されます。これは、EDS ページのURLと同じではありません。
- 境界線を優先 — ホスト内の境界線コンテナをリクエストします。
テンプレート URLを展開し、次を入力します。
- スクリプト URL — EDS ウィジェットのエントリポイント。
- ウィジェット URL – このアクションの公開済みEDS ページ。
一般的なURLは次のとおりです。
Script URL:
https://main--<repo>--<owner>.aem.live/scripts/aem-embed.js
Widget URL:
https://main--<repo>--<owner>.aem.live/<widget-page>
必要なブラウザー権限とCSP ドメインのみを付与します。
ウィジェットに位置情報などのブラウザー機能が必要な場合にのみ、権限(MCP固有)を展開します。 未使用の権限はオフのままにします。
CSP設定で、アセットとネットワーク要求に必要なオリジンを追加します。 外部リソースを読み込むウィジェットは、CSPが不完全な場合、レンダリングされない可能性があります。
EDS プロジェクトまたはウィジェットページがまだ存在しない場合は、自分のEDS プロジェクトを作成し、アクションに戻ります。
アクションを保存
「新しいアクションを作成」を選択します。 アクションは、デプロイされていない バッジを持つアクションページに表示されます。
保存するとメタデータが作成されますが、アクションにはまだハンドラーが必要です。
保存したアクションを再度開くと、エディターにはLLM ウィジェットの詳細、アクションの詳細、および Analytics のタブがあります。 後で編集した内容を保存するには、変更を保存を選択します。 作成ダイアログと保存済みアクションエディターのレイアウトは異なります。
ハンドラーの実装
リンクされたハンドラーリポジトリを複製し、その依存関係をインストールします。
npm install
次のファイルを作成します。
actions/
└── search-products/
└── index.js
フォルダー名は、アクションエディターに表示されるアクションのコード識別子と一致する必要があります。
結果のコントラクトとハンドラーとウィジェットの関係について詳しくは、生成ハンドラーのカスタマイズ を参照してください。
ハンドラーコントラクト
1つの非同期関数を書き出します。
module.exports = async (args) => {
return {
content: [
{ type: 'text', text: 'Response for the LLM platform.' }
],
structuredContent: {
// Data for the widget.
}
};
};
ハンドラーは、UIで定義されたパラメーターを受け取ります。
contentを返す
LLM プラットフォームは、contentをテキストフォールバックとして読み取ります。
content: [
{ type: 'text', text: 'Found 3 matching products.' }
]
アクションにウィジェットがある場合でも、常に便利なcontentを返します。
structuredContentを返す
ウィジェットはstructuredContentをプレーンオブジェクトとして使用します。
structuredContent: {
products: [
{ id: 'P-100', name: 'Product A', price: '$20' }
],
total: 1
}
図形は、bridge.toolResultから読み取るEDS ブロックと一致している必要があります。
APIの接続
サーバーサイドハンドラーでAPI呼び出しを保持します。 この例では、パブリック製品APIを呼び出します。 PRODUCT_API_ORIGINという名前の アプリ変数でAPIのHTTPS オリジンを設定します。 この例では、上記で定義されたqueryおよびcategory パラメーターを受け入れ、少なくとも1つのパラメーターを必要とし、資格情報は必要ありません。
const { getVariable } = require('@adobe/llm-apps-runtime');
module.exports = async ({ query = '', category = '' } = {}, extra) => {
const normalizedQuery = typeof query === 'string' ? query.trim() : '';
const normalizedCategory = typeof category === 'string' ? category.trim() : '';
if (typeof query !== 'string' || typeof category !== 'string'
|| (!normalizedQuery && !normalizedCategory)
|| normalizedQuery.length > 200 || normalizedCategory.length > 100) {
return {
isError: true,
content: [{ type: 'text', text: 'Enter a valid product search.' }],
structuredContent: { products: [], total: 0 }
};
}
const apiOrigin = getVariable(extra, 'PRODUCT_API_ORIGIN');
if (!apiOrigin) {
throw new Error('Product API configuration is unavailable.');
}
const origin = new URL(apiOrigin);
if (origin.protocol !== 'https:') {
throw new Error('Product API configuration must use HTTPS.');
}
const url = new URL('/v1/products', origin);
if (normalizedQuery) {
url.searchParams.set('query', normalizedQuery);
}
if (normalizedCategory) {
url.searchParams.set('category', normalizedCategory);
}
const response = await fetch(url, {
signal: AbortSignal.timeout(8000)
});
if (!response.ok) {
throw new Error('Product service request failed.');
}
const payload = await response.json();
if (!payload || !Array.isArray(payload.products)
|| !payload.products.every((product) =>
product
&& typeof product.id === 'string'
&& typeof product.name === 'string'
&& typeof product.price === 'string')) {
throw new Error('Product service returned an unexpected response.');
}
const products = payload.products.map((product) => ({
id: product.id,
name: product.name,
price: product.price
}));
return {
content: [
{ type: 'text', text: `Found ${products.length} matching products.` }
],
structuredContent: {
products,
total: products.length
}
};
};
ソースコード、アクションメタデータ、ウィジェット、JavaScript、ログ、ユーザー向けエラーなどにAPI資格情報を含めないでください。 変数とシークレット UIは、現在、シークレットではなく非機密変数のみをサポートしています。 API トークンを変数に格納しないでください。 アップストリーム APIに資格情報が必要な場合は、接続する前に、承認済みのサーバーサイド資格情報メカニズムを配置します。
実稼動コードの場合は、承認されたフィールドをstructuredContentにマッピングする前に、アップストリームの完全な応答を検証します。
ハンドラーテストの追加
一致するテストを作成します。
test/
└── actions/
└── search-products.test.js
少なくともテスト:
- 有効な入力:
- 入力がないか、無効です。
- 結果が空です。
- APIのタイムアウトまたは失敗。
- 形式が正しくないAPI データ。
- ウィジェットで想定される
structuredContentシェイプ。
実行:
npm test
プロジェクトのレイアウトとローカル MCP テストについては、 ローカルハンドラーの開発とテスト を参照してください。
アクションをローカルでテストする
実行:
npm run dev:local
ローカル actions.jsonがなければ、サーバーは最小限のメタデータと入力スキーマの検証を行わずにハンドラーを検出します。
MCP インスペクターまたはcurlを使用して、次を行います。
- 登録したアクションを一覧表示します。
- 代表引数を使用して新しいアクションを呼び出します。
contentとstructuredContentを確認します。- 無効なリクエストと空のリクエストをテストします。
ウィジェットの接続とテスト
アクションにウィジェットがある場合:
- ウィジェットにハンドラーの
structuredContentを読み取らせます。 textContentなどの安全なDOM APIを使用して、外部値をレンダリングします。- 読み込み、空、エラーの状態を追加します。
- EDS ページをローカルでプレビューします。
- CSP、CORS、およびウィジェットのURLを確認します。
「独自のEDS プロジェクトを作成する」を参照してください。
デプロイとテスト
一致するハンドラーなしでメタデータが存在する場合、デプロイメントはアクションをデフォルトスタブに登録します。 アクションをユーザーが利用できるようにする前に、ハンドラーを追加します。