Experience Cloud「チャット窓」で、Agentforceからの回答を「UIリッチ」にするには

みなさんこんにちは。エンジニアの佐藤です。今回はExperience Cloudの「チャット窓」のお話です。

Experience CloudはSalesforceCRMデータと連携したポータルサイトを迅速に構築できるプラットフォームで、会員サイトの構築などに向いています。会員サイトにはさまざまな問い合わせが寄せられますので、サイト運営者なら、ここへチャットボットを配置してサポート業務を効率化したいと、考えることでしょう。この希望に応えるのが「チャット窓」こと、「組み込みメッセージング コンポーネント(英語名: Embedded Messaging)」です。

組み込みメッセージングコンポーネントは、Agentforce登場以前からService Cloudの一部として存在していたもので、有人オペレーターとのチャットや旧Einstein Botとの会話で活用されてきました。ここへAgentforceエージェントを接続することができます。うまくいけば、平易な質問に無人で回答させることができそうです。

チャットインターフェイスは、さまざまな質問を持ち込むことができる柔軟なインターフェイスですが、質問するためにはテキストを入力しなければなりません。提示された選択肢を選択するだけで返答できるようにできないものでしょうか。よくある選択肢を案内されれば、何を言ったら良いかわからない状況でも会話が進みそうです。

この目的で使えるのが、Lightning種別(英語名: Lightning Type)と呼ばれる機能です。これは簡単に言うと、これまでお話しした「チャット窓」で、任意のLightning Web Component(LWC)を利用できるようにする機能です。この機能を活用して、多肢選択のUIをLWCで表現し、クリック動作であらかじめ設定したメッセージを投稿するようにできれば、目的の機能が実現できそうです。

今回はこれに挑戦してみました。結果はこの通りです。メニューが提示され、ユーザーはそれをクリックすることで注文を行います。

目論見通りメニューは表示され、メニュー項目をクリックするとユーザーの代わりに注文が入力されます。

以下、この動作を実現する実装を解説します。

公式情報で近いサンプルを発見

Agentforce Developer Guideのサンプルページに、目的とする「メニュー項目と選択動作」に近い例があります。フライト予約ホテル予約の2つのシナリオで、それらしいユーザーインターフェイスが表示されています。これらのサンプルページには、丁寧なことにサンプルコードのアーカイブまで用意されています。また、Nobuyuki Watanabe氏による日本語ブログでは、これらを実際に組み込む手順が詳しく紹介されています。この手順を忠実に実行すれば、サンプルを動かしてみることは可能です。

これらのサンプルページとその周辺文書では、以下のことが説明されています。

  • LWC(Lightning Web Component)を使う。
  • このLWCを、Lightning種別で指定する。
  • Agentforceのアクションの入出力で、このLightning種別を指定する。

しかし、上記サンプルページおよびブログは、ビルダーで作成したAgentforceエージェントのプレビュー動作の確認までで、Experience Cloudの組み込みメッセージングコンポーネントに接続する手順については説明していません。また、サンプルコードを展開して動いた(=実行系として一つの成功例を確保した)だけでは、これらがどうやって関連しているのか、筆者は今ひとつ理解できませんでした。

そこでこれらの点について、以下の観点から整理してみました。

  • AgentforceとExperience Cloud組み込みメッセージングコンポーネントの関係
  • LWC、Lightning種別、Agentforceアクションの関係

AgentforceとExperience Cloud組み込みメッセージングコンポーネントの関係

AgnetforceエージェントはどのようにExperience Cloudに配置された組み込みメッセージングコンポーネントへと接続されるのでしょうか。この接続は、「組み込みサービスリリース(英語名: Embedded Service Deploymment)」「拡張チャットメッセージング(英語名: Enhanced Chat Messaging)」「オムニチャネル メッセージングフロー」という3つによって接続されています。

この3つの設定手順は、一昨年の弊社ブログ「Service Agent を Web ページから利用する」で説明しています。(ご存知ない方は、これより先を読まれる前に、まずこの弊社ブログをお読みになると理解がしやすくなると思います。なお、この弊社ブログの中の「Messaging for In-App and Web(MIAW)」は「Enhanced Chat」へと改名されました。)

この手順の最後に、組み込みメッセージングコンポーネントの「組み込みWebリリース」欄に組み込みサービスリリースを設定すると、設定は完了です。

Salesforceのこの部分の機能は似たような用語が何箇所にも登場し、過去にリブランドされてきた経緯もあるので、全貌を理解するのはなかなか難しくなっています。筆者なりに調査結果をまとめると、ユーザーから近い順に以下のような構成になっています。

# 名称 主な役割 リファレンス(英語)
1 Experience Cloudサイト チャット窓のホスティング Experience Cloud
2 組み込みメッセージングコンポーネント 組み込みサービスリリースのホスティング Embedded Messaging
3 組み込みサービスリリース 拡張チャットメッセージングのWebホスティング Embedded Chat
4 拡張チャットメッセージング メッセージング機能 Add Flexibility and Power with Enhanced Chat
5 オムニチャネル メッセージングフロー メッセージング機能とAgentforceエージェントの接続 Route Messages Directly to Agentforce
6 Agentforceエージェント 会話機能の提供 (省略)

図にしてみると以下のような感じでしょうか。

これらの設定を全て行うと、組み込みメッセージングコンポーネントにAgentforceエージェントが自動入室するようになります。

以下の2点に注意しましょう。

  • 組み込みサービスリリースの作成では「V2」を選択する必要がある。V1ではLightning種別は動作しない。
  • 後述のLWCやAgentforceアクションのデータ型で変更があった場合は、都度組み込みサービスリリースの「公開」操作を実行する必要がある。

(筆者の推測ですが、この組み込みサービスリリースの公開処理では、組み込みサービスリリースが接続している拡張チャットメッセージングに接続しているAgentforceエージェントが調査され、Lightning種別で定義されているLWCの情報を、組み込みメッセージングコンポーネントで使用されるJavascriptアーカイブなどの形で永続化しているのではないかと思います。)

LWC、Lightning種別、Agentforceアクションの関係

次に、LWC、Lightning種別、Agentforceアクションの関係についてです。筆者の理解では、公式文献で最も詳しいのはCustom Lightning Typesですが、もう少しわかりやすい説明が欲しいところです。

筆者なりに解説を試みてみます。

今回の「メニュー項目と選択動作」を実現するAgentforceエージェントの立場になって考えてみましょう。以下のようなやり取りです。

ユーザー: メニューはありますか?
エージェント: こちらです。
|うどん|焼きそば|ラーメン|

「メニューはありますか?」に応じるため、トピック「メニューの紹介」とアクション「メニューの表示」を定めます。(この部分はAgnetforceの基本的内容ですので、割愛します。)今回はアクションはApexで以下のように実装しました。

public with sharing class MenuChoiceAction {
    // Requestクラスは省略

    public class MenuActionOutput {
        // ここが重要
        @InvocableVariable(label='menu')
        public MenuChoiceResponse menu; 
    }

    @InvocableMethod(
        label='Show Menu (Hard-coded)'
        description='Returns a hard-coded menu list for the agent action output.'
    )
    public static List<MenuActionOutput> showMenu(List<Request> reqs) {
        MenuChoiceResponse m = new MenuChoiceResponse(
            'メニューを選んでください。',
            new List<String>{ 'うどん', '焼きそば', 'ラーメン' }
        );
        MenuActionOutput outp = new MenuActionOutput();
        outp.menu = m;
        return new List<MenuActionOutput>{ outp };
    }
}

(全体はこちら)

「ここが重要」とコメントした部分に注目してください。ここではMenuChoiceResponseという、Apexクラスのオブジェクトが、@InvocableVariableでアノテーションされ、MenuActionOutputという別のクラスでラップされてApexアクションから戻り値として返されています。(今回はサンプル簡略化のため、バルク化は実施しておらず、戻り値が長さ1の配列になっています。)

このMenuChoiceResponseは、以下のように定義された、メニューのコンテンツ情報です。(これは単にデータを構造化して配置するための箱にすぎません。)

public class MenuChoiceResponse {
  public String prompt;

  public List<String> items;

  public MenuChoiceResponse(String prompt, List<String> items) {
      this.prompt = prompt;
      this.items = items;
  }
}

さて、Agentforceの立場に戻りましょう。アクション実行の結果を評価すると、MenuActionOutputにラップされたMenuChoiceResponse型オブジェクトが見つかりました。

これを、どうやって画面に出したら良いでしょうか?この部分を決めるのこそ、Lightning種別です。

Lightning種別とは何者か?

Lightning種別は、Salesforce組織に設定されたメタデータの一種です。Salesforce組織の設定メニューで「Lightning 種別」(「Lightning」と「種別」の間に半角空白を入れましょう)で検索することで、組織に設定されているLightning種別を確認することができます。このメタデータはMetadata APIマニフェスト(package.xmlファイル)では、「LightningTypeBundle」と呼ばれるメタデータ型名となっていて、Salesforce CLIではforce-app/main/default/lightningTypesフォルダに展開されます。

リソースは2階層のフォルダ(リソース名のフォルダと、その下のlightningDesktopGenAiフォルダ)に分かれて配置され、例えばリソース名が「menuResponse」だった場合は、以下の構成になっています。

  • force-app/main/default/lightningTypes/menuResponse/schema.json
  • force-app/main/default/lightningTypes/menuResponse/lightningDesktopGenAi/renderer.json

(renderer.jsonは出力用コンポーネントを定義する場合で、入力用はeditor.jsonという名称になりますが、今回は取り扱いません。)

それぞれのファイルの役割を端的に説明すると、以下のようになります。

ファイル名 役割
schema.json Lightning種別の名称、説明、適用するデータ型を設定する
lightningDesktopGenAi/renderer.json 使用するLWCを設定する(出力用)

中身を見てみましょう。schema.jsonの中身は、例えば以下のようになっています。

{
  "title": "Menu Response",
  "description": "Hard-coded menu choices for the user to click.",
  "lightning:type": "@apexClassType/c__MenuChoiceResponse"
}

この「@apexClassType/c__MenuChoiceResponse」こそ、このLightnign種別に対応するデータ型を指定している部分です。「c__」はSalesforceがあらかじめ定義したもの以外のカスタムタイプを表す接頭辞で、今回は私が定めたApexクラスMenuChoiceResponseが使われますので、この接頭辞が付けられます。

これでLightning種別とApexクラスが紐づきました。

次にrenderer.jsonを見てみましょう。

{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "c/menuChoiceRenderer"
      }
    }
  }
}

ここに書かれている「menuChoiceRenderer」は、LWCの名称です。

これで、ApexクラスとLWCがつながりました。先ほどのAgentforceの立場に立てば、このLightning種別から、アクションの戻り値のMenuChoiceResponse型オブジェクトの値を、LWC menuChoiceRendererに紐づけることができます。

LWCそのものについてはここでは解説しませんが、menuChoiceRendererは以下のようになっています。内容はごく簡単なものです。

menuChoiceRenderer.html

<template>
  <template if:true={prompt}>
    <p>{prompt}</p>
  </template>

  <template for:each={items} for:item="item">
    <lightning-button
      key={item}
      label={item}
      data-choice={item}
      onclick={handlePick}>
    </lightning-button>
  </template>
</template>

menuChoiceRenderer.js

import { LightningElement, api } from 'lwc';

export default class MenuChoiceRenderer extends LightningElement {
  @api value;

  get prompt() {
    return this.value?.prompt;
  }

  get items() {
    return this.value?.items || [];
  }

  handlePick(event) {
    const choice = event.currentTarget.dataset.choice;
    window.parent.sendMessage(choice);
  }
}

「@api value;」が期待しているデータ構造と項目名が、先ほど説明したAgentforceに設定されるApexアクションが返すMenuChoiceResponseの、公開フィールドに一致していることに注目してください。

AgentforceでLightning種別を設定する

これでAgentforceが、Apexアクションの戻り値に対応するLWCをLightning種別を経由して呼び出す準備ができました。

Apexアクションの出力欄の「高度な設定」を展開すると、「データ型」に「c__menuResponse」が表示され、Lightning種別が設定されていることがわかります。また、「会話に表示」の下にオプションが選択できるようになり、「MenuResponse」が選択できるようになります。(この名称はLightning種別の先頭を大文字にしたもののようです。)

ユーザーの代わりに発言するには?

menuChoiceRenderer.jsの「handlePick」メソッドに注目してください。このメソッドは以下の処理を行っています。

  1. event.currentTarget.dataset.choiceを評価し、選択されたボタンに設定されたメニューを読み取ります。(これはLWCの標準的な動作ですので、説明は割愛します。)
  2. window.parent.sendMessageを呼び出します。

window.parent.sendMessageは、以下のようにExperience Cloudサイトのヘッドマークアップで定義されたものです。組み込みメッセージングコンポーネントはiframeで組み込みサービスリリースを展開していますので、組み込みサービスリリースで実行されるLWCから見ると、Experience Cloudサイトは親フレームにあたります。

<script type='text/javascript'>
function sendMessage(text) {
  embeddedservice_bootstrap.utilAPI.sendTextMessage(text)
    .then(() => {
        console.log(`sendMessage(${text}) success`);
    }).catch(() => {
        console.log(`sendMessage(${text}) catch`);
    }).finally(() => {
        console.log(`sendMessage(${text}) finally`);
    });
}
</script>

この手順についてはEnhanced Web Chat開発ガイドSend Message as the End User in Enhanced Web Chatに書かれています。また、この設定にあたっては、Experience Cloudサイト セキュリティとプライバシーの設定で、コンテンツセキュリティポリシー(CSP)を「緩和されたCSP: インラインスクリプトと許可されたホストへのアクセスを許可」に設定する必要があります。

振り返って、どうか

今回ご紹介した手法は、「チャット窓」にリッチなUIを追加する非常に便利な方法です。広範囲な知識が要求される実装ですが、一度わかってしまえば苦労せずに応用できるでしょう。

筆者が以前に書いたブログ「Agentforceを「普通のWeb」に接続するには」と比較すると、以下のメリット・デメリットがあると思います。

比較観点\手法 Experience Cloud組み込みメッセージングコンポーネント(今回) Agent API + ReactJS応用(前回)
開発難度 低い(LWCとApex) 高い(ReactJS + Redux)
画面の自由度 低い(組み込みメッセージングコンポーネントの範囲内) 高い
必要ライセンス Agentforce + Digital Engagement Agentforceのみ

なお、組み込みメッセージングコンポーネントは、実のところ組み込みサービスリリースをホスティングしているだけで、チャット窓の機能実装の本体は組み込みサービスリリース側にあります。そしてこの組み込みサービスリリースは、スニペットを展開すると任意のWebサイトに配置できるとされています。こちらもいずれ試してみたいところです。

今回ご紹介した開発内容全体は、こちらで公開していますので、興味のある方は適宜ご覧いただければと思います。

最後までお読みいただき、ありがとうございました。