[NBA] Next Best Action で「おすすめ」をリアルタイムに生成する

もう先に書いときますけど、今日はかしゆかの誕生日!!!!!111 別に Advent Calendar が書きたくて、年に一度のこの大切な日に、何本もblog書いてるんじゃない、今日は、みんなにかしゆかをお祝いしていただくことを目的に、こうやって朝から晩までblog三昧(それでも終わらないこと多いけど)です。ぜひ、私を讃え、かしゆかに祝福を与えてください。

さて。そんな[Salesforce Advent Calendar 2019](https://qiita.com/advent-calendar/2019/salesforce)ですが、すでに書きたいことは書ききったので、Salesforce Low Codeがどこまで許されるかの限界に挑戦です。昨日はなんと[ばんこパイセン](https://qiita.com/akikobannai) でした。[Trailblazerが世界を変える](https://medium.com/@akikobannai_87167/trailblazer%E3%81%8C%E4%B8%96%E7%95%8C%E3%82%92%E5%A4%89%E3%81%88%E3%82%8B-15bbb3bcf898)というさすがのお題目です。私がこれからやろうとしていることは、天につばを吐きかけるような行為ではないかと恐れをなしています。明日は[@akitomomax](https://qiita.com/akitomomax)さんの[リード全削除するの巻](https://qiita.com/akitomomax/items/2f725d7e0aadbdbf6fc4)です!

# Next Best Action はスゴイ

先日まで同じチームだった、うちの稲葉と、隣のサービスチームの原田くんが提供する[Einstein Next Best Action](https://developer.salesforce.com/events/webinars/EinsteinNextBestAction_jp)に、すべての良さが詰まっていますので、こちらをご覧いただくとわかりやすいでしょう。ぜひ「いいね」くださいませね。

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/1P4J2F8AI_I/0.jpg)](http://www.youtube.com/watch?v=1P4J2F8AI_I)

とは言え、1時間近いこのセミナーを全部見てくれる人は全てではないと思いますので、セミナー概要から「Next Best Actionとは」を拾ってきます。はい、他人の力で私のblogは成り立っています。皆様ありがとうございます。

> Einstein Next Best Action は、その名の通り、次の最良手は何か？を Salesforce 利用者に教えてくれるものです。Salesforce 利用者が判断に有する時間を短縮し、また見落としを防いでくれる優れものです。
この判断ロジックの生成自体に機械学習やディープラーニングの要素は入っていませんが、Einstein Discovery や Einstein 予測ビルダーの結果を判断ロジックで使うことができるので、蓄積されたデータからの予測に基づく最良手の提示も実現できます。

`Next Best Action` は、レコードの詳細表示ページにレイアウトします。よく、右上に置かれます。そこで、対象のレコードの内容や、状況に合わせて最適な「おすすめ」を表示する仕組みです。こんな感じで、かしゆかっぽい(かしゆかではない)何かを表示しつつ、かしゆかをおすすめさせることが可能です。

<img width="970" alt="Screen Shot 2019-12-23 at 11.44.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/3ababcee-8467-73e7-4a3f-19157a6cee05.png">

背後の仕組みとしては、「おすすめ」オブジェクトに登録されたレコードから、取捨選択して表示させることができるものなんです。この「おすすめ」をたくさん準備して、`Next Best Action` の「Strategy Builder」で、どれを、どの順番で、いくつ表示するかを絞り込んでいきます。

この動画にもあるように、このときに予測ビルダーのスコアなども利用して「解約率が高そう」とか「購入しそう」なときに、クーポンを発行できます。また、フェーズに合わせて「このフェーズでさせたいこと」を表示させるということもできます。例えば、商談のフェーズがまだはじめの段階であれば「弊社の紹介資料をお渡しする」ことをリコメンドで期待するわけですね。すごくない？

# でも、それって準備したものしか表示できなくない？

おっしゃるとおりなのです。`Next Best Action`の機能を利用すると、ノーコードでおすすめを準備することが簡単にできる仕組みが提供されています。が、それは「おすすめ」を準備した数しかリコメンドができないということほかなりません。先程の例のように、クーポンを出したり、フェーズごとに何かをしたりということには、使い勝手が非常に良いです。

しかし、おすすめの期待値はどんどん上がります。「いま表示されている商談に関係する商談を表示させたい」「予約したい商品がすでになくなっていたので、別の似たようなものをおすすめさせたい」なんてことを考えてみましょう。すべての商談のおすすめを作りますか？すべての商品のおすすめを作りますか？作ったところで、それらをどのように戦略を作りますか？

そうなのです。数が膨大になってきたときの対処法や、リアルタイムにおすすめしたいものを準備するという期待値には、ちょっと答えにくいのです。残念。ノーコードでできる限界はここまでか。

**そこで**

ローコードで対処してみましょう。さぁ、この「ローコード」の限界を知るのが、今日の目的です。

# Apexアクションはスゴイ

おい「Apex」書くのはプロコードじゃねぇのか。と思われた方、お気持ち察します。がしかし。それは程度によっては「ローコード」でしょう。現に私は、「外部サービス」で「データスキーマ(swagger, [open API2.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md))」レベルで JSON を記述してもらうことを「ノーコード」と宣言してはばかりません。それと比較したら、数行の`Apex`は「ローコード」と言って問題のないレベルであると確信しています。そうです。数行でかけてしまうのです

`Next Best Action` は、フローなどと同様に、`Apexアクション`を利用できます。さきほどの動画でも、後半にでてきますね。動画をちゃんと見てくださったみなさまには、ピンときたかと思います。

「(　´д)ﾋｿ(´д｀)ﾋｿ(д｀ ) でも、コードを書くんでしょう...?」

恐れることなかれ、`Next Best Action` 向けの `Apexアクション` はお作法が決まりまくっているので、カンタンに記述することができます。さぁ、とくと見よ、これがテンプレだ。

```java
global class <クラス名> {
    @InvocableMethod(label='ラベル' 
                     description='説明文章')
    global static List<List<Recommendation>> <メソッド名>(List<String> inputData){
        List<List<Recommendation>> outputs = new List<List<Recommendation>>();

        Account[] accts = [SELECT Name FROM Account];

        List<Recommendation> recs = new List<Recommendation>(); 
        for (Account acct:accts) {
            Recommendation rec = new Recommendation(
                Name = accts.Name;
                Description = accts.Name + 'がおすすめです',
                ActionReference = 'execute_flow_name',
                AcceptanceLabel = '表示'
            );
            recs.add(rec);
        }
        outputs.add(recs);
        return outputs; 
    }
}
```

このままじゃ動きませんが、最低限必要なことはここに書いてあります。必要なことは、次のことを決めることです。

1. クラス名とメソッド名
2. 何をインプットとするか
3. 何を表示するか
4. 何をフローに回すか

です。一つずつ行きましょう。

## 1. クラス名

こんなもん好きに決めておきましょう。`TestBestAction`とかいう最低なクラス名つけたことあります。ダジャレかよ。

ApexはJavaのお作法にならって、[キャメルケース](https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A1%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9)で書きます。一般的に「動詞」+「名詞」で表します。

今回は、取引先のオブジェクトをターゲットにします。特に意味はありません。取引先の(1)従業員数が該当のレコードよりも少なく (2)年間売上が多い、取引先を表示するようにしてみましょう。従業員が少ないのに、売上の高いとこというのは、ちょっとツライですね。そんな相手先が一体誰なのかを、おすすめ表示させましょう。

クラス名は `GetRival` で、メソッドを `recommendYourTask` にします。所詮、ただの判別記号です。好きな名前をつけましょう。

## 2. 何をインプットとするか

「インプット」と言っているのは、この`Apex`コードへ必要な入力項目のことです。メソッドのところにある「List<String> inputData」と書いてある `inputData`です。自分のレコード情報がほしいので、自分を認識できるものがほしいです。取引先のときは `Name` ですかね。これを入力してもらうようにしましょう。

流れとしては、まず自分の情報が必要です。今回は、(1)従業員数 (2)年間売上 が必要なので、このようにしましたが、一つしか必要なければ、それをインプットしても構いません。(1)従業員数、しか使わないのであれば、それをインプットとすることができます。

## 3. 何を表示するか

表示する内容です。「おすすめ」オブジェクトで表示できるものと同じです。これをプログラムの中で定義します。

プログラムの中では、`Recommendation rec = new Recommendation()` で指定された部分が、表示内容を決めるところです。今回は、該当の取引先名を表示して「好敵手です」とでも表示します。

## 4. 何をフローに回すか

`Next Best Action` で表示されたおすすめの「許可」に対する部分をクリックすると、フローが動き出します。今回は、みなさまへの宿題です。年明けに確認するので、満身創痍でチャレンジしてください。

それと、どのようなデータをフローへ回すかも必要になります。特定のレコードの情報などをフローで使いたい場合もあるでしょう。その定義もここで行います。

# 今回の Apexプログラム(ﾊﾞﾊﾞｰﾝ

結論からいきます。

```java
global class GetRival {
    @InvocableMethod(label='Get Rivals' description='該当する取引先の好敵手を見つけ出します')
    global static List<List<Recommendation>> recommendYourTask(List<String> inputData){
        List<List<Recommendation>> outputs = new List<List<Recommendation>>();
        
        Account keyAcct = [SELECT NumberOfEmployees, AnnualRevenue FROM Account WHERE Name = :inputData[0]];
		Integer keyEmployees = keyAcct.NumberOfEmployees;
        Decimal keyRevenue   = keyAcct.AnnualRevenue;
        Account[] accts = [SELECT Name,AnnualRevenue FROM Account WHERE NumberOfEmployees < :keyEmployees];

        List<Recommendation> recs = new List<Recommendation>(); 
        for (Account acct:accts) {
            if (acct.AnnualRevenue > keyRevenue) {
                Recommendation rec = new Recommendation(
                    Name = acct.Name,
                    Description = acct.Name + 'は好敵手です',
                    ActionReference = 'Testing_Apex_Action',
                    AcceptanceLabel = '表示'
                );
                recs.add(rec);
            }
        }
        outputs.add(recs);
        return outputs; 
    }
}
```

主な追加部分は2箇所。１つ目は SOQL でデータを取ってくるとこですね。1行目が、自分自身のレコードの内容から必要なものを取得して、それを変数へ入れています。4行目の SOQLが、実際に対象となる取引先の情報を拾ってきています。

```java
        Account keyAcct = [SELECT NumberOfEmployees, AnnualRevenue FROM Account WHERE Name = :inputData[0]];
		Integer keyEmployees = keyAcct.NumberOfEmployees;
        Decimal keyRevenue   = keyAcct.AnnualRevenue;
        Account[] accts = [SELECT Name,AnnualRevenue FROM Account WHERE NumberOfEmployees < :keyEmployees];
```

あとは `for`ループ内に 1つ `if` 分岐をつけています。これにより、取得された取引先情報からさらに絞り込みをかけています。

「あれ、もしかして SOQL 1行で書けるんじゃね」

と思った方正解です。最終的には、こうなります。

```java
global class GetRival {
    @InvocableMethod(label='Get Rivals' description='該当する取引先の好敵手を見つけ出します')
    global static List<List<Recommendation>> recommendYourTask(List<String> inputData){
        List<List<Recommendation>> outputs = new List<List<Recommendation>>();
        
        Account keyAcct = [SELECT NumberOfEmployees, AnnualRevenue FROM Account WHERE Name = :inputData[0]];
		Integer keyEmployees = keyAcct.NumberOfEmployees;
        Decimal keyRevenue   = keyAcct.AnnualRevenue;
        Account[] accts = [SELECT Name FROM Account WHERE NumberOfEmployees < :keyEmployees and AnnualRevenue > :keyRevenue];

        List<Recommendation> recs = new List<Recommendation>(); 
        for (Account acct:accts) {
            Recommendation rec = new Recommendation(
                Name = acct.Name,
                Description = acct.Name + 'は好敵手です',
                ActionReference = 'Testing_Apex_Action',
                AcceptanceLabel = '表示'
            );
            recs.add(rec);
        }
        outputs.add(recs);
        return outputs; 
    }
}
```

結果、ちょっとクラスメイトメソッド名を直して、4行付け足したら完成した、みたいなものです。これはローコードレベルでしょ。

# 戦略を作る

では、これらを`Next Best Action`の戦略で利用するにはどうするのでしょうか。

結論から行くとこうです。「生成」で`Apexアクション`を呼び出します。この`Apexアクション`では` List<List<Recommendation>>`型の、いわゆる「おすすめ」レコードが返ってくるようになっています。`Next Best Action`は、この「おすすめ」レコードをどの様に処理するかということになっているわけです。

<img width="471" alt="Screen Shot 2019-12-23 at 14.01.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/8cb8420f-c5f0-0b72-a3fc-ed0d8441e879.png">

通常、「読み込み」で該当する「おすすめ」レコードを示します。このように「生成」を利用すると、もととなる「おすすめ」レコードがなくとも、`Apex`で作り上げられた固有の「おすすめ」レコードを利用させることができるようになるんですね。素敵。様々な状況・状態の「おすすめ」レコードを予め作っておかなくて良い、ということが分かりました。

この「生成」の中身も至ってシンプルです。名前を決めて、今回準備した`Apexアクション`を指定します。`inputData` へ必要なレコードの情報も、ここで指定します。`$Record.Name` のように項目を指定します。

<img width="856" alt="Screen Shot 2019-12-23 at 14.07.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/85e8382c-0f87-197c-1e81-70135a989e3b.png">


もう一つ。この個別のフローを識別して、フローでどのように実行させるかについては、もう一考必要です。それが「対応付け」の部分ですね。「おすすめ」のレコードを、「フロー」が受け取る名前に割り当てていきます。

<img width="852" alt="Screen Shot 2019-12-23 at 14.09.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/4743dd95-3e04-4528-0fa1-ae2e24e0990a.png">

例えばこんな感じですね。フロー側では「レコードの取得」を利用して、`{!Name}' を使ってレコードの条件を指定して拾ってくることになります。

# 結果

わたしの取引先の中では、このような結果になりました、とさ。

<img width="936" alt="Screen Shot 2019-12-23 at 14.12.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/8e4b6567-4859-a8fd-547c-1125b6743ee5.png">

`Next Best Action`は、ただおすすめを表示するだけの機能ではありません。その都度、リアルタイムにレコードや、それこそ「[Salesforce で外部サービスを使うときには「指定ログイン情報」を使いましょう](https://qiita.com/sho7650/items/577e54f390bc97e8d751)」でうまいこと外部との連携 `Apexサービス`を作って外部Webサービスとも連携できたりします。

`Next Best Action`の夢が広がりますね？