CodeGenベースのSalesforceコード生成AI、Einstein for Developers とは？


# Einstein for Developers ベータ版がリリース
2023年9月 Dreamforceにて、Salesforce開発者向けツールとしては初めてLLMを使ったプログラミングコード生成AI、Einstein for Developersが発表されました。

現在はベータ版にはなりますが、すでに日本語に対応した自然言語プロンプトからSalesforceコードを素早く生成することができ、開発者体験が大きく向上することが期待できそうです。

生成AIといって多くの方が思い浮かべるのは、OpenAI社のChatGPTだと思いますが、Salesforceでも、AI研究開発チームであるSalesforce AI Researchが独自の生成AIエンジンを開発しています。その中でも、コード開発に特化した生成AIエンジンが、この Einstein for Developers では使われています。


### プログラミングコード生成LLM - CodeGen
そのプログラミングに特化した生成AIエンジンは、CodeGenというもので、オープンソースなのでGithubで確認できます。

https://github.com/salesforce/CodeGen

初代バージョンにあたるCodeGen1.0は、実は2022年3月には公開されており、この時点でなんと、C、C++、Go、Java、JavaScript、Python ６つのプログラミング言語で、コード生成することができていたんです。

https://arxiv.org/abs/2203.13474


2022年5月にCodeGen2.0。そして、2023年7月にはCodeGen2.5がリリースされています。大規模言語モデル(LLM)での学習は、1エポック、つまり学習データの観測を複数回行わず1度だけ行うことが主流のようですが、CodeGen2.5では特別なチューニングを施し、1エポック以上のトレーニングを行います。そうすることで少ないデータ量から効率的に学習することができ、CodeGen2.5では150億のパラメータが使われるCodeGen2.0の半分以下となる70億のパラメータで、CodeGen2.0と同等の性能を引き出すことに成功しています。

将来的にはローカルマシンで動かすことで、各開発者が高度にパーソナライズされたCodeGenベースのコード補完アシスタントを利用できるところまで見据えており、ベンチマークではすでに、小型LLMのローカル展開に適したフレームワークのもとで実現可能なスループットを記録しているようです。詳細はこちら。

https://blog.salesforceairesearch.com/codegen25/


Einstein Developersでは、このCodeGenをベースに、ApexやLWCなどのSalesforceでのユースケースに対応するよう再トレーニングされ、改良されたものが利用されています。
※ベータ版ではApexのみ対応です。


# Einstein for Developers を使う準備

それでは、どのようなことができるのか？
機能を有効化して利用環境を整えるところから見てみましょう。


### 前提
スクラッチ組織を用いたソース駆動開発モデルでの検証になるため、以下の前提知識が必要になります。また、Salesforce CLIコマンドはsfdxに変わる新しいsfコマンドを用います。
 - [ソース駆動型開発](https://trailhead.salesforce.com/ja/content/learn/modules/sfdx_dev_model)
 - [Salesforce DX](https://trailhead.salesforce.com/ja/content/learn/projects/quick-start-salesforce-dx)
 - [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli)
 - [Migrate sfdx-Style Commands to the New sf-Style](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_migrate.htm)


## 設定手順

3ステップで設定します。
1. VS Code 拡張機能のインストール
2. DevHub組織での機能有効化
3. プロジェクトでの機能有効化

### 1. VS Code 拡張機能のインストール
VS Codeに拡張機能 `Einstein for Developers` をインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/18ec52ab-240c-911f-d1cb-adc8a86e0bfe.png)


### 2. DevHub組織での機能有効化

DevHub組織にて、Einstein for Developers 機能を有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/6c58dd4a-b3de-bbb5-d26e-15322fa3e7a4.png)

### 3. プロジェクトでの機能有効化
sfコマンドでの手順です。

#### 3-1. プロジェクト作成
まずは、Salesforceプロジェクトを作成します。
```sh
sf project generate -n testEinsteinForDevelopers
```

#### 3-2. 機能有効化したスクラッチ組織の作成
作成したプロジェクトディレクトリに移動し、VS Codeでプロジェクトディレクトリを開きます。
```sh
cd testEinsteinForDevelopers
code .
```
`config/project-scratch-def.json` を開き、featuresに `EinsteinGPTForDevelopers` を追加して保存します。
```json
{
  "orgName": "Test company",
  "edition": "Developer",
  "features": ["EnableSetPasswordInApi", "EinsteinGPTForDevelopers"],
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    },
    "mobileSettings": {
      "enableS1EncryptedStoragePref2": false
    }
  }
}
```

上記作成した設定ファイルを元にスクラッチ組織を作成します。
```sh
sf org create scratch -d -f config/project-scratch-def.json -a testScratchOrg
```
以上で準備は完了です。

# Einstein for Developersの試し方
２種類の方法で実行可能です。
 - サイドバーでのプロンプト
 - コマンドパレット

### サイドバーでのプロンプト
サイドバーで `Einsteinアイコン` をクリックすると、Einstein for Developers のサイドバーが開きます。ここにプロンプトを入力し、生成コードも専用画面から確認できます。コードを利用する場合はコピーボタンをクリックし、エディタ上でペーストして貼り付けます。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/7e85b035-08d1-b2c4-b36a-ba4bddfa3e38.gif)


### コマンドパレット
`Command + R` のショートカットでコマンドパレットが開きます。プロンプトを入力すると、生成コードはコードエディタ上に半透過状態で表示されます。コードを利用する場合は`Accept`をクリックすればOKです。
![output2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/a941c01a-8520-99ff-ab08-e2d28be4d508.gif)


# Einstein for Developers が生成するコード
:::note warn
現在、正式にサポートされている言語は英語のみです
:::
どのようなアウトプットが得られるのか？いくつかユースケースを想定して試してみました。
 - サンプルコードを確認したい
 - 具体的な仕様を伝えて作成してもらう

### サンプルコードを確認したい
```プラットフォームキャッシュを使うサンプルコードを教えて```

```apex
public class CacheExample {
    
    public static void putMethod(){
        // Put something in the cache
        Cache.Org.put('local.MyCache.cacheKey', 'someValue');
    }
    
    public static String getMethod(){
        // Get something from the cache
        String cacheValue = (String) Cache.Org.get('local.MyCache.cacheKey');
        return cacheValue;
    }
}
```
***
```LWCから呼び出すApexクラスのサンプルコードを教えてください。AuraEnabledのキャッシュあり/なしバージョンも確認したいです。```

```apex
public class myFirstClass {
    
    @AuraEnabled(cacheable=true)
    public static List<Account> getAccounts() {
        return [SELECT Id, Name, BillingCity FROM Account WHERE BillingCity = 'San Francisco'];
    }
    
    @AuraEnabled
    public static List<Account> getAccountsNonCached() {
        return [SELECT Id, Name, BillingCity FROM Account WHERE BillingCity = 'San Francisco'];
    }
    
    public static List<Contact> getContacts() {
        return [SELECT Id, Name, Email FROM Contact WHERE Email LIKE '%example.com%'];
    }
    
    @AuraEnabled
    public static List<Contact> getContactsNonCached() {
        return [SELECT Id, Name, Email FROM Contact WHERE Email LIKE '%example.com%'];
    }
    
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getOpportunities() {
        return [SELECT Id, Name, StageName FROM Opportunity WHERE StageName = 'Closed Won'];
    }
    
    @AuraEnabled
    public static List<Opportunity> getOpportunitiesNonCached() {
        return [SELECT Id, Name, StageName FROM Opportunity WHERE StageName = 'Closed Won'];
    }
    
}
```
***
```指定ログイン情報を使って外部APIを呼び出す方法を教えて```

```apex
public static HttpResponse callExternalAPIUsingLoginCredentials() {
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http();
        
        // Set the endpoint URL and method
        req.setEndpoint('callout:ExternalAPI');
        req.setMethod('GET');
        
        // Send the request and get the response
        res = http.send(req);
        
        return res;
}
```

### 具体的な仕様を伝えて作成してもらう
```getOpptiesという名前のメソッドを作成してください。引数は、取引先IDがaccountIdで渡されます。この取引先の進行中の商談リストを、完了予定日順に並べ替えて返してください。LWCから呼び出す想定です。```

```apex
public class myOppties{
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getOppties(Id accountId) {
      Date today = Date.today();
      return [SELECT Id, Name, StageName, CloseDate FROM Opportunity WHERE AccountId = :accountId AND CloseDate >= :today ORDER BY CloseDate];
    }
}
```
***
```getContentsという名前のメソッドを作成してください。レコードIdを引数に、そのレコードに紐づくContentVersionレコードのうち、JPEG画像のみをリストで返してください。```

```apex
private static List<ContentVersion> getContents(Id recordId) {
    List<ContentVersion> results = [SELECT Id, Title, FileType, VersionData FROM ContentVersion WHERE FirstPublishLocationId = :recordId AND FileType = 'JPG'];
    return results;
}
```
***
`レコードIDとメッセージを引数で受け取り、当該レコードに対し、受け取ったメッセージでChatter投稿するメソッドを作成してください。`

```apex
public static void postMessage(Id recordId, String message) {
        ConnectApi.FeedItemInput feedItemInput = new ConnectApi.FeedItemInput();
        ConnectApi.MentionSegmentInput mentionSegmentInput = new ConnectApi.MentionSegmentInput();
        ConnectApi.MessageBodyInput messageBodyInput = new ConnectApi.MessageBodyInput();
        ConnectApi.TextSegmentInput textSegmentInput = new ConnectApi.TextSegmentInput();

        messageBodyInput.messageSegments = new List<ConnectApi.MessageSegmentInput>();

        mentionSegmentInput.id = recordId;
        messageBodyInput.messageSegments.add(mentionSegmentInput);

        textSegmentInput.text = message;
        messageBodyInput.messageSegments.add(textSegmentInput);

        feedItemInput.body = messageBodyInput;
        feedItemInput.feedElementType = ConnectApi.FeedElementType.FeedItem;
        feedItemInput.subjectId = recordId;

        ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(null, feedItemInput);
}
```

## フィードバック

HISTORY & FEEDBACK: EINSTEIN というタブで、過去実行したプロンプト＆生成コードの履歴が確認できます。また、右側のFeedback列からフィードバックを送信できます。コメントとしてフィードバックを送信することで、Einstein for Developersはフィードバックを継続学習し、ますます精度がよくなることが期待できます。たくさんお試し＆フィードバックください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/5711163e-644c-fcbc-e32b-ac67707aa10e.png)


## さいごに
いくつか試してみましたが、すでに簡単なテンプレートやサンプルコードをクイックに確認したいといった用途では十分に使えそうな印象を持ちました。

もし思っていたコードが返ってこない場合、プロンプトの聞き方を変えて何度かトライすることで、所望のコードが得られる可能性があります。ここらへんはLLMの特性を理解し、いかに目的のコードを得られるためのプロンプトを書けるか？プロンプトエンジニアスキルが今後コード開発の場面でも重要になってくるのかもしれません。

最近Salesforce開発に携わり始めたエンジニアの方から、ベテランSalesforceエンジニアさんまで幅広く役立ててもらえるのではないでしょうか。

少しでも使ってみたい！と思ってもらえましたら、公式ドキュメントで詳細を確認の上、是非お試しください。

https://developer.salesforce.com/tools/vscode/en/einstein/einstein-overview