カスタム・コードおよびバックエンドの統合

ここでは、カスタム・コードを記述し、デジタル・アシスタントのバックエンド統合を行うためのベスト・プラクティスをいくつか紹介します。

会話の最後に、ユーザーから収集された情報を使用して何かをする必要があります。その「何か」では、通常、カスタム・コンポーネントを作成する必要があるデータまたは永続データを問い合せるためのバックエンド・サービスへのアクセスが必要です。カスタム・コンポーネントのもう1つの用途は、複雑な検証やその他のユーティリティ関数を処理するカスタム・ロジックを組み込むことです。Oracle Digital Assistantでは、次の2つのタイプのカスタム・コンポーネントがサポートされています。

  • カスタム・ダイアログ・フロー・コンポーネント(CCS)
  • エンティティ・イベント・ハンドラー(EEH)

デジタル・アシスタントの計画および設計フェーズでは、必要なバックエンド・リソースを識別し、使用可能なAPIが十分かどうかを決定する必要があります。

  • デジタル・アシスタントはWebアプリケーションではないため、デジタル・アシスタントの会話に必要なデータ量とデータ量のみを返すには、最適化レイヤーを介して既存のAPIを最適化または抽象化する必要がある場合があります。
  • デジタル・アシスタントの会話に統合するバックエンド機能用のRESTサービスがない場合は、プロジェクトを設計およびトリガーして構築する必要があります。

バックエンド・サービス統合を実装する際、カスタム・コンポーネントをリモートでデプロイするか、埋込みコンポーネント・コンテナをOracle Digital Assistantスキルで使用するかを決定します。

次の図に示すように、バックエンド統合は計画および実装フェーズの必須部分です。

カスタム・ダイアログ・フロー・コンポーネント

カスタム・ダイアログ・フロー・コンポーネントを使用すると、会話のコンテキストでカスタム・コード・ロジックを実行するために、ダイアログ・フローに追加できる独自のユーザー・インタフェース・コンポーネントを作成できます。これらのコンポーネントを記述する際のユースケースは次のとおりです。

  • RESTサービスを介したリモート・バックエンド・サービスの問合せおよび書込み。

  • 会話終了時のユーザー・フィードバックの要求、管理者へのエラーのロギングおよびレポートなど、特定のタスクのすべてのユーザー・インタラクションを処理する即時利用可能なソリューション。

  • ダイアログ・フロー変数に保存されたオブジェクト配列内のデータの管理をサポートします。

コンポーネントおよび入力パラメータに適切な名前を使用

カスタム・コンポーネントの説明を提供するフィールドはありません。カスタム・コンポーネントには、その実行内容と渡す必要のある情報を説明するフィールドがあります。したがって、スキル開発者がコンポーネントを使用できるようにするための最良の方法は、コンポーネントと入力パラメータに適切な名前を使用し、コンポーネントが戻すアクション文字列を慎重に選択することです。

  • 組込みのYAMLコンポーネントでは、名前としてSystem.<name>が使用されます。したがって、特にYAMLベースのダイアログ・フローでは、Custom.<name>を使用して、スキル・レビュー担当者がダイアログ・フローが参照するカスタム・コンポーネントであることを理解できます。または、ネームスペースを使用してコンテキストを提供できます。サンプル・カスタム・コンポーネントでは、多くの場合、oracle.sample.<name>を使用して、これらのコンポーネントが生産品質ではないことを示します。

  • 入力パラメータは、処理するカスタム・コンポーネントにデータを提供します。多くの場合、カスタム・コンポーネントに渡されるデータは、処理する実際の値ではなく、処理するデータ値を保持する変数の名前、またはリモート・サービスから問い合せるデータの書込み先となる変数の名前です。組込みコンポーネントを見ると、コンポーネント結果が書き込まれる変数の名前を保持するプロパティ名としてvariable、または変数参照名を参照するプロパティを示す<name>Var (nlpResultVarなど)が使用されます。これをさらに改善するには、_inおよび_outの接尾辞を使用して、変数がデータを含む変数を参照するか、コンポーネントからのデータを期待するかを指定します。

  • アクション文字列はオプションであり、スキル開発者がナビゲートする次のダイアログ・フロー状態を決定するために使用できます。successまたはfailureをアクション文字列として使用しても、コンテキストがあまり提供されないため、かわりにorderSubmittedorderRejectedまたは userUnauthorizedのようなものを使用することをお薦めします。

コードの仮定を回避

多くの場合、カスタム・コンポーネントの開発者は、カスタム・コンポーネントを使用するスキル開発者でもあります。このため、多くの開発者は、スキルに存在する変数について仮定することで、カスタム・コンポーネントでの作業を簡素化します。そのため、変数名をコンポーネントに渡すかわりに、カスタム・コンポーネント・ロジック内の名前を直接参照します。このような仮定はカスタム・コンポーネントを容易に壊す可能性があるため、お薦めしません。カスタム・コンポーネントとそれを使用するスキルとの間に明確で完全な契約を定義することをお薦めします。

Thinkライブラリ

一般的な質問は、カスタム・コンポーネント・サービス・パッケージに追加するカスタム・コンポーネントの数です。一般に、カスタム・コンポーネント・サービスとそのコンポーネントに含まれるコンポーネントをライブラリと考えることをお薦めします。そのため、タスクに関連するすべてのコンポーネントを単一のカスタム・コンポーネント・サービスに保存できます。しかし、提案は現実に向き合う必要があります。したがって、カスタム・コンポーネントのパッケージ化方法に関する質問は、カスタム・コンポーネントの意図した使用に基づいて回答する必要があります。

  • 再利用は、多くのカスタム・コンポーネント開発者にとってオプションではありません。特定のスキルでカスタム・コンポーネントを開発して使用する場合、それらのすべてのコンポーネントを単一のカスタム・コンポーネント・サービス・デプロイメントにグループ化することは理にかなっています。ただし、他のスキルで実際に再利用されるコンポーネントについては例外です。

  • 埋込みコンポーネント・コンテナ・デプロイメントは、Oracle Digital Assistantインスタンス当たりのカスタム・コンポーネント・サービスの数によって制限されます。したがって、スキルごとに単一のカスタム・コンポーネント・サービスを使用するか、カスタム・コンポーネントのリモート・デプロイメントを探します。

  • 次の理由により、Oracle Cloud InfrastructureのKubernetesへのリモート・カスタム・コンポーネント・サービス・デプロイメントの使用:

    • カスタム・コンポーネント・コードに含まれる機密情報を開示しない。埋込みコンテナにデプロイされたカスタム・コンポーネント・サービスは、Oracle Digital Assistantインスタンスへのフル・アクセス権を持つすべてのユーザーがダウンロードできます。

    • コードのより適切なセグメンテーションを実装します。 カスタム・コンポーネントには、ボットとの対話およびRESTサービスの起動に必要なコードのみを含める必要があります。他のすべてのコードは、外部のJavaScriptファイル(埋込みコンテナを使用している場合)または統合レイヤー(RESTサービス)に格納する必要があります。カスタム・コンポーネントには、次のことを行うコードが含まれます。

      • 入力パラメータの読取り

      • 読み取り/設定変数値

      • コンポーネントが受信したメッセージの処理

      • カスタム・コンポーネントのユーザー・インタフェースのレンダリング

      • 次の状態への移行の決定

      • コンポーネント状態の管理

      • RESTサービスへのアクセス

    • パフォーマンスを向上させるため。カスタム・コンポーネントをスキルにデプロイするための埋込みコンテナでは、コールド・スタート遅延があるOCI関数を使用します。この遅延と、デプロイできるサービス数の制限を回避するために、カスタム・コンポーネントのリモート・デプロイメントでは、心配する必要のない代替手段が提供されます。

    • 共通コンポーネントを共有します。 この経験から、再利用はカスタム・コンポーネント開発者の間で高く評価されていませんが、一般的に使用されるカスタム・コンポーネントを作成してリモート・サーバーにデプロイすることは理にかなっています。エラー処理やエスカレーション、2-legged OAuth2認可処理などの一般的なコンポーネントがある場合があります。

ログ・メッセージの書込み方法

カスタム・コンポーネント用に実装されるデフォルトのロガーは、コンソール・ロガーです。ロガーには、context.logger()へのコールを介してアクセスします。".info('…')や.warn('…')などのコンソール・ロガーで使用可能なロギング関数をコールできます。

ノート:埋込みコンテナがこれらのログを正しく表示する方法を知っているため、context.logger()を使用すると、埋込みコンテナにデプロイする際に最も意味があります。外部にデプロイするカスタム・コンポーネントの場合、log4jsなど、別のロギング・ライブラリを使用することをお薦めします。

コンポーネントの内部状態の管理

カスタム・ダイアログ・フロー・コンポーネントでは、ナビゲーションが次のダイアログ・フロー状態に遷移する前に、ユーザーとの対話が長くなることがあります。この相互作用では、初期コールと後続のコールを区別できるように、コンポーネントが内部状態を処理していることを確認する必要があります。これを行うには、次の2つのオプションがあります。

  • ポストバック・メッセージ・ペイロードにトークンを追加します。 ユーザーがアクション・アイテムを押すことができるユーザー・インタフェースがカスタム・コンポーネントによってレンダリングされると、ポストバック・メッセージがカスタム・コンポーネントに送信されます。カスタム・コンポーネントは、受信したポストバック・メッセージを評価して、ポストバックがレンダリングしているユーザー・インタフェースからのものか、別のコンポーネントからのものかを判断する必要があります。このため、アクション・アイテムのレンダリング時に追加されたカスタム・コンポーネントにトークンが含まれているかどうかについて、ポストバック・メッセージを確認できます。

  • ダイアログ・フロー変数を使用します。 ユーザー・メッセージから抽出された値を追跡するなど、カスタム・コンポーネント起動間でより複雑な状態を管理する必要がある場合は、ダイアログ・フロー変数を使用できます。カスタム・コンポーネントは、実行時にcontext.variable('variable name', value)のコールでダイアログ・フロー変数を作成できます。指定された名前の変数が存在しない場合、その変数が作成されます。「値」オブジェクトは、追跡する必要がある任意のものにできます。

入力パラメータの検証

カスタム・コンポーネントに対して定義する入力パラメータは、コンテンツを持つように検証する必要があります。これは、必要に応じて設定したパラメータにも当てはまります。次のケースを確認する必要があります。

  • 入力パラメータに値セットがあります。

  • 値は'$ {'で始まりません。これは、変数から入力パラメータ値を読み取るための式、またはダイアログ・フローでオブジェクトが正しく解決されていないことを示します。

コンポーネント・メッセージに対するMessageFactoryクラスの使用

カスタム・コンポーネントから送信するすべてのボット・レスポンスでは、MessageFactoryクラスを使用する必要があります。MessageFactoryクラスを使用すると、共通レスポンス・コンポーネントと同じタイプのリッチ・ユーザー・メッセージを作成できます。これには、値リスト、添付ファイル、カード・レイアウトおよびテキスト・メッセージが含まれます。

また、MessageFactoryクラスで定義されたメッセージはチャネルに依存しません。つまり、単一のコンポーネント・メッセージを作成し、チャネル固有のコネクタによって、それぞれのクライアント・チャネルで必要な形式に変換されます。

カスタム・コンポーネントからMessageFactoryクラスにアクセスするには、次の参照を使用します。

let MessageFactory = context.MessageFactory();
ノート

MessageFactoryクラスは、非推奨になったMessageModelクラスよりも優先されます。どちらのクラスも汎用は同じですが、MessageFactoryには次の利点があります。
  • サブセットのみでなく、すべての共通メッセージ・モデル(CMM)メッセージ・タイプおよびプロパティをサポートします。
  • その実装はクラス・ベースで、メッセージ定義を変更するクリーンなgetter、setterおよびaddメソッドを提供します。コード補完は、適切な型定義がイベント・ハンドラまたはカスタム・コンポーネントの最上部に含まれている場合に、TypescriptおよびJavaScriptを使用する場合です。
  • 実装ではビルダー・パターンを使用します。このパターンを使用すると、多数のセッターを連鎖したり、メソッドを追加したりできるため、コードが読みやすくなり、コーディングする必要のある行数が減少します。

カスタム・コンポーネントのチェックリスト

  • ☑スキルで使用するためにバックエンド・サービスが最適化または抽象化されていることを確認します。
  • ☑ カスタム・コンポーネントにはボット関連のコードのみを含める必要があります。他のすべてのコードは、ユーティリティ・クラスまたはライブラリに移動するか、個々のRESTサービスとしてリモート・サーバーまたはクラウド・サービスにデプロイする必要があります。
  • ☑ カスタム・コンポーネントとそれらが使用しているスキルとの間に、明確で完全な契約を作成します。
  • ☑ 複雑な評価にはカスタム コンポーネントを使用します。このような場合は、Apache FreeMarkerを使用しないでください。
  • ☑マルチリクエスト・ユーザー・インタラクションのコンポーネント状態を管理します。
  • ☑ すべてのカスタム・コンポーネント入力パラメータを検証します。
  • ☑スキル開発者が問題に対処するためのアクション文字列を返して、エラーを処理します。

エンティティ・イベント・ハンドラ

エンティティ・イベント・ハンドラは、コンポジット・バッグ・エンティティを解決するコンテキストでカスタム・コンポーネント・コードを起動できるカスタム・コンポーネントのタイプです。エンティティ・イベント・ハンドラは、モデル駆動型の会話で使用され、ユーザー入力との対話と検証、および読取りおよび書込みアクセス用のリモート・バックエンド・サービスの起動を行います。カスタム・ダイアログ・フロー・コンポーネントとは異なり、イベント・ハンドラでは再利用の可能性は最小限です。そのため、エンティティ・イベント・ハンドラのデフォルトの実装は埋込みスキル・コンテナに対するものです。

エンティティ・コンポーネントを解決するための欠落している機能の追加

共通レスポンス・コンポーネントに設定できる多くの機能(ヘルプや取消用のグローバル・ボタンなど)は、構成を介してエンティティの解決コンポーネントで使用できません。

ただし、エンティティ・イベント・ハンドラを使用して、不足している機能を追加できます。これにより、高度な機能を犠牲にすることなく、ダイアログ・フローの「エンティティの解決」コンポーネントのシンプルさを利用できます。

状態の管理

エンティティ・イベント・ハンドラ関数は、コンポジット・バッグ・エンティティを解決するときに、エンティティの解決コンポーネントおよび共通レスポンス・コンポーネントによって起動されます。次に解決する必要があるバッグ・アイテムをすべて追跡する必要はありません。

それでも、後で使用するためにいくつかの情報を保存することもできます。そのためには、次の2つのオプションがあります。

  • コンテキスト解決プロパティは、コンテキスト・オブジェクトに対して作成する変数です。変数とその値は、コンポジット・バッグ・エンティティが解決されるか、コンポジット・バッグ・エンティティを解決するダイアログ・フロー状態のままになるまで存在します。コンテキスト解決プロパティを使用する利点は、必要なハウスキーピングがないことです。

    • 書込みには、context.setCustomProperty(name, value);を使用します。
    • 読取りには、context.getCustomProperty(name);を使用します。
  • 実行時または設計時に作成されるダイアログ・フロー変数を使用して、コンポジット・バッグ・エンティティの解決を超えて永続化する値を格納できます。ダイアログ・フロー変数に格納されたコンテンツには、ダイアログ・フローの状態(設計時に定義された変数のみ)および他のエンティティ・イベント・ハンドラからアクセスできます。

    • 書込みには、context.variable(name,value);を使用します。
    • 読取りには、context.variable(name);を使用します。

ログ・メッセージの書込み方法

エンティティ・イベント・ハンドラ用に実装されるデフォルトのロガーは、コンソール・ロガーです。

ロガーには、context.logger()へのコールを介してアクセスします。

.info('…').warn('…')などのコンソール・ロガーで使用可能なロギング関数をコールできます。

ユーザーメッセージの表示

カスタム・ユーザー・メッセージは、context.addMessage()関数を介して表示されます。カスタム・ダイアログ・フロー・コンポーネントと同様に、チャネル固有のペイロードを出力するのではなく、チャネルに依存しないメッセージを作成するためにMessageFactoryクラスを使用することをお薦めします。エンティティ・イベント・ハンドラでは、値リスト、カード・レイアウトおよび添付タイプのメッセージもサポートされます。

エンティティ・イベント・ハンドラのチェックリスト

  • ☑ 後のダイアログ・フローの状態で必要でないかぎり、解決コンテキストに一時値を格納します。
  • ☑スキルで使用されるすべてのエンティティ・イベント・ハンドラに対して単一のカスタム・コンポーネント・サービスを使用します。
  • MessageFactoryクラスを使用して、ユーザーに表示するメッセージを表示します。

どのコンポーネントを使用しますか。

エンティティ・イベント・ハンドラはコンポジット・バッグ・エンティティで使用し、カスタム・ダイアログ・フロー・コンポーネントはダイアログ・フロー状態間で遷移する会話のコンテキストで使用されます。結局、両方使うことになるでしょう。モデルドリブンな会話を使用する推奨事項に従っている場合、カスタムのダイアログ・フロー・コンポーネントよりもエンティティ・イベント・ハンドラを使用する可能性が高くなります。

機能の観点から、カスタム・ダイアログ・フロー・コンポーネント(CCS)とエンティティ・イベント・ハンドラ(EEH)はよく似ています。次の表に、2つのカスタム・コンポーネント・タイプを比較します。

機能 CCS EEH
Node.jsモジュールのサポート/JavaScript開発 はい はい
TypeScriptサポート はい はい
ブラウザベースの開発 いいえ はい
外部IDEでの開発 はい はい
ダイアログ・フローで使用 はい いいえ
コンポジット・バッグ・エンティティで使用 いいえ はい
入力パラメータ はい いいえ
アクション遷移へのプログラムによるナビゲーション はい いいえ
RESTサービスの呼出し はい はい
ダイアログ・フロー変数からの読取り/書込み はい はい
解決コンテキストで値を一時的に格納 いいえ はい
リソース・バンドル/多言語サポートの使用 はい はい
ユーザーと対話するためのリッチ・ユーザー・インタフェースおよびプロンプトのレンダリング はい はい
スキル・コンテナ・デプロイメントのサポート はい はい
リモート・デプロイメントのサポート はい はい
ローカルデバッグのサポート(NGROKまたはその他のトンネルが必要) はい はい
カスタム・イベント いいえ はい
ポストバック・アクションのサポート はい はい

CCSおよびEEHに対するリソース・バンドルの使用

ユーザーにボット・メッセージを表示するカスタム・ダイアログ・フロー・コンポーネントおよびエンティティ・イベント・ハンドラでは、デジタル・アシスタントでサポートされている言語でメッセージを表示する必要があります。

最近まで、カスタム・コンポーネントからスキルに定義されたリソース・バンドルを簡単に使用する方法はありませんでした。しかし、コード内のリソース・バンドル・キーを参照できる新しいプログラミング・インタフェースがあります。次の2つの既知の制限があります。

  • リソース・バンドル文字列の使用は、パラメータのないリソース・バンドルまたは位置パラメータのあるリソース・バンドルに制限されます。ICUメッセージ・バンドルで使用される名前付きパラメータは、新しいAPIではまだサポートされていません。

  • APIは、ボット・レスポンスとして返されると、検出された言語の参照メッセージ・バンドル文字列に置き換えられる式を生成します。

新しいAPIをコールするには、次のいずれかのコンテキスト・オブジェクト・コールを使用します。

  • let expression = context.translate('resource_bundle_key_name');
  • let expression = context.translate('resource_bundle_key_name', param1, param2);

式は、テキスト・レスポンス、ボタン・ラベル、およびMessageFactoryクラスを使用するカードで使用できます。

  • エンティティ・イベント・ハンドラのサンプル:

    const messageModel = context.getMessageFactory();
    //create a conversation message format text object that references a key name
    const message = messageModel.createTextMessage(context.translate('resource_bundle_key'));
    //display the message to the user keeping the turn, which means the composite bag entity
    //proceeds with the next bag item to resolve
    context.addMessage(message,true);
  • カスタム・ダイアログ・フローの例:

    const messageModel = context.getMessageFactory();
    //create a conversation message format text object that references a key name
    const message = messageModel.createTextMessage(context.translate('resource_bundle_key'));
    //display the message to the user keeping the turn, which means the composite bag entity
    //proceeds with the next bag item to resolve
    context.reply(message); context.keepTurn(true);
    context.transition(); done();

TechExchangeの記事「入力パラメータを使用した翻訳済リソース・バンドル文字列のカスタム・コンポーネントへの渡し」も参照してください。

名前付きパラメータの使用方法

リソース・バンドル内の名前付きパラメータにアクセスする方法を次に示します:

//Entity event handler sample
let expression = "${rb('key_name','param_name1,param_name2',"+value1+","+value2+")}";
let message = messageFactory.createTextMessage(expression);
context.addMessage(message,true);
//Custom dialog flow component sample
let expression = "${rb('key_name','param_name1,param_name2',"+value1+","+value2+")}";
let message = messageFactory.createTextMessage(expression);
context.reply(message);

リソース・バンドルおよびカスタム・コンポーネントに関する推奨事項

このガイドでは、リソース・バンドルをどこでも使用することが一般的なテーマです。ただし、スキルに格納されているリソース・バンドルを使用すると、カスタム・ダイアログ・フロー・コンポーネントまたはイベント・ハンドラとスキルの密接な結合が作成されます。この依存関係と価値に問題がない場合は、密結合の問題を回避するよりも、リソース文字列を1箇所で管理するメリットがあります。イベント・ハンドラの場合、再利用の可能性は最小限です。そのため、エンティティ・イベント・ハンドラでのリソース・バンドル文字列の使用に疑問はありません。

異なるスキルで再利用されるカスタム・ダイアログ・フロー・コンポーネントの場合、カスタム・コンポーネントに必要なリソース・バンドル・キー名がスキルにリソース・バンドルに追加されている場合は、翻訳機能も機能します。

代替ソリューションを使用すると、リソース・バンドルから読み取られたメッセージを入力パラメータとしてカスタム・ダイアログ・フロー・コンポーネントに渡すことで、カスタム・コンポーネントをスキルに密結合しないようにできます。

エンティティ・イベント・ハンドラに移行する必要がありますか。

カスタム・ダイアログ・フロー・コンポーネントからエンティティ・イベント・ハンドラに移動する場合は、新しいテクノロジであるだけでなく、特定の理由によるものである必要があります。「いいね」を変更しても、スキルは向上しません。現在の会話フローに不満があり、コンポジット・バッグ・エンティティを使用してダイアログ・フローの会話の一部を置き換えることを検討している場合は、コード・ロジックをカスタム・ダイアログ・フロー・コンポーネントからエンティティ・イベント・ハンドラに移動するのがよい理由の1つです。

エンティティ・イベント・ハンドラに移行する際のベスト・プラクティス

会話フローを改善するために、カスタム・ダイアログ・フロー・コンポーネントからエンティティ・イベント・ハンドラに既存の機能を移動する場合は、カスタム・ダイアログ・フロー・コンポーネントおよび共通レスポンス・コンポーネントで実装した動作を模倣するだけではないことを確認してください。かわりに、エンティティの解決コンポーネントの使用を開始し、エンティティ・イベント・ハンドラ関数を使用して、会話型ユース・ケースに必要なすべての検証およびロジックを実装します。