カスタム・コードおよびバックエンドの統合
ここでは、カスタム・コードを記述し、デジタル・アシスタントのバックエンド統合を行うためのベスト・プラクティスをいくつか示します。
会話の最後に、ユーザーから収集された情報に対して何かを行う必要があります。この「何か」は通常、カスタム・コンポーネントを作成する必要があるデータまたは永続データを問い合せるためのバックエンド・サービスへのアクセスを必要とします。カスタム・コンポーネントのもう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
を使用すると、あまりコンテキストが提供されないため、かわりにorderSubmitted
、orderRejected
、userUnauthorized
などのものを使用することをお薦めします。
コードでの仮定の回避
現実には、カスタム・コンポーネントの開発者は、カスタム・コンポーネントを使用するスキル開発者でもあることがよくあります。このため、多くの開発者は、スキルに存在する変数を想定して、カスタム・コンポーネントでの作業を簡略化します。そのため、変数名をコンポーネントに渡すかわりに、カスタム・コンポーネント・ロジックで直接名前を参照します。このような仮定によってカスタム・コンポーネントが簡単に破損する可能性があるため、このことはお薦めしません。カスタム・コンポーネントとそれを使用するスキルとの間に明確で完全な契約を定義することをお薦めします。
思考ライブラリ
一般的な質問は、カスタム・コンポーネント・サービス・パッケージに追加するカスタム・コンポーネントの数に関するものです。一般に、カスタム・コンポーネント・サービスとそれに含まれるコンポーネントをライブラリと考えることをお薦めします。そのため、タスクに関連するすべてのコンポーネントは、単一のカスタム・コンポーネント・サービスに保存できます。しかし、提案は現実と向き合う必要があります。したがって、カスタム・コンポーネントのパッケージ化方法に関する質問は、カスタム・コンポーネントの意図した用途に基づいて回答する必要があります。
-
多くのカスタム・コンポーネント開発者にとって再利用は選択肢ではありません。カスタム・コンポーネントを特定のスキル用に開発して使用する場合、それらのすべてのコンポーネントを1つのカスタム・コンポーネント・サービス・デプロイメントにグループ化することが理にかなっています。ただし、他のスキルで実際に再利用されるコンポーネントについては例外です。
-
埋込みコンポーネント・コンテナ・デプロイメントは、Oracle Digital Assistantインスタンスごとのカスタム・コンポーネント・サービスの数によって制限されます。したがって、スキルごとに1つのカスタム・コンポーネント・サービスを使用するか、カスタム・コンポーネントのリモート・デプロイメントを探します。
-
次の理由により、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を使用する場合です。
- 実装ではビルダー・パターンを使用します。これにより、多数のsetterを連鎖したりメソッドを追加したりできるため、コードをより読みやすくし、コード化する必要がある行数を減らすことができます。
カスタム・コンポーネントのチェックリスト
- ☑ バックエンド・サービスがスキルで使用できるように最適化または抽象化されていることを確認します。
- ☑ カスタム・コンポーネントにはボット関連のコードのみを含める必要があります。その他のすべてのコードは、ユーティリティ・クラスまたはライブラリに移動するか、個々のRESTサービスとしてリモート・サーバーまたはクラウド・サービスにデプロイする必要があります。
- ☑ カスタム・コンポーネントとそれらが使用されているスキルとの間に、明確で完全な契約を作成します。
- ☑ 複雑な評価にはカスタム コンポーネントを使用します。この場合、Apache FreeMarkerは避けてください。
- ☑ 複数リクエストのユーザー・インタラクションのコンポーネントの状態を管理します。
- ☑ すべてのカスタム・コンポーネント入力パラメータを検証します。
- ☑ スキル開発者が問題を処理するためのアクション文字列を返すことで、エラーを処理します。
さらに学ぶ
- チュートリアル: バックエンド統合用のカスタム・コンポーネント開発
- チュートリアル: カスタム・コンポーネントのデバッグ
- カスタム・コンポーネント・サービスの埋込みコンテナ制限
- Bots Node SDKのドキュメント: 様々なUIレスポンス用のMessageFactoryサンプル・コード
エンティティ・イベント・ハンドラ
エンティティ・イベント・ハンドラは、コンポジット・バッグ・エンティティを解決するコンテキストでカスタム・コンポーネント・コードを起動できるカスタム・コンポーネントの一種です。エンティティ・イベント・ハンドラは、モデル駆動型の会話で使用され、ユーザー入力と対話および検証を行い、読取りおよび書込みアクセスのためにリモート・バックエンド・サービスを呼び出します。カスタム・ダイアログ・フロー・コンポーネントとは異なり、イベント・ハンドラの再利用の可能性は最小限です。そのため、エンティティ・イベント・ハンドラのデフォルトの実装は埋込みスキル・コンテナに対するものです。
エンティティ・コンポーネントを解決するための欠落した機能の追加
共通レスポンス・コンポーネントに設定できる機能の多くは、ヘルプ用のグローバル・ボタンや取消用のグローバル・ボタンなど、構成を介してエンティティの解決コンポーネントでは使用できません。
ただし、エンティティ・イベント・ハンドラを使用して、欠落している機能を追加できます。これにより、高度な機能を犠牲にすることなく、ダイアログ・フローの「エンティティの解決」コンポーネントのシンプルさを利用できます。
状態の管理
エンティティ・イベント・ハンドラ関数は、コンポジット・バッグ・エンティティの解決時に「エンティティの解決」および「共通レスポンス」コンポーネントによって起動されます。次に解決する必要があるバッグ・アイテムを追跡する必要はありません。すべて完了しています。
ただし、後で使用するために情報を保存することもできます。そのためには、次の2つの方法があります。
-
コンテキスト解決プロパティは、コンテキスト・オブジェクトで作成する変数です。変数とその値は、コンポジット・バッグ・エンティティが解決されるか、コンポジット・バッグ・エンティティを解決するダイアログ・フロー状態のままになるまで存在します。コンテキスト解決プロパティを使用する利点は、必要なハウスキーピングがないことです。
- 書込みには、
context.setCustomProperty(name, value);
を使用します。 - 読取りには、
context.getCustomProperty(name);
を使用します。
- 書込みには、
-
実行時または設計時に作成されるダイアログ・フロー変数は、コンポジット・バッグ・エンティティの解決を超えて保持する値を格納するために使用できます。ダイアログ・フロー変数に格納されたコンテンツには、ダイアログ・フロー状態(設計時に定義された変数の場合のみ)および他のエンティティ・イベント・ハンドラからアクセスできます。
- 書込みには、
context.variable(name,value);
を使用します。 - 読取りには、
context.variable(name);
を使用します。
- 書込みには、
ログ・メッセージの書込み方法
エンティティ・イベント・ハンドラに実装されるデフォルトのロガーは、コンソール・ロガーです。
context.logger()
のコールを介してロガーにアクセスします。
コンソール・ロガーで使用可能なコール・ロギング関数(.info('...')
や.warn('...')
など)を使用できます。
ユーザー・メッセージの表示
カスタム・ユーザー・メッセージは、context.addMessage()
関数を介して表示されます。カスタム・ダイアログ・フロー・コンポーネントと同様に、チャネル固有のペイロードを出力するかわりに、MessageFactory
クラスを使用してチャネルに依存しないメッセージを作成することをお薦めします。エンティティ・イベント・ハンドラは、タイプ値リスト、カード・レイアウトおよび添付のメッセージもサポートします。
エンティティ・イベント・ハンドラのチェックリスト
- ☑ 後続のダイアログ・フロー状態で必要でないかぎり、解決コンテキストに一時値を格納します。
- ☑スキルで使用されるすべてのエンティティ・イベント・ハンドラに対して、単一のカスタム・コンポーネント・サービスを使用します。
- ☑
MessageFactory
クラスは、ユーザーに表示するメッセージに使用します。
さらに学ぶ
- Oracle Digital Assistant Design Campの動画: エンティティ・イベント・ハンドラの理解
-
チュートリアル: ブラウザでのエンティティ・イベント・ハンドラの開発
- チュートリアル: 外部IDEでのエンティティ・イベント・ハンドラの開発
- 例: 経費精算書の例によるEEHを使用したモデル主導の会話
- Bots Node SDKのドキュメント: 様々なUIレスポンス用の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箇所で管理することのメリットを評価する場合は、これを行う必要があります。イベント・ハンドラの場合、再利用の可能性は最小限です。そのため、エンティティ・イベント・ハンドラでのリソース・バンドル文字列の使用がまったく疑われることはありません。
異なるスキルで再使用されるカスタム・ダイアログ・フロー・コンポーネントの場合、リソース・バンドルに追加されたカスタム・コンポーネントで必要なリソース・バンドル・キー名がスキルにある場合も、翻訳機能が機能します。
別のソリューションを使用すると、リソース・バンドルから読み取られたメッセージを入力パラメータとしてカスタム・ダイアログ・フロー・コンポーネントに渡すことで、カスタム・コンポーネントをスキルに緊密に結合することを回避できます。