Salesforce Einstein CopilotとApexによるカスタムAIソリューションのアーキテクチャ設計

執筆者:Salesforce アーキテクト


背景と応用シナリオ

近年、生成AI、特に Large Language Models (LLM、大規模言語モデル) の進化は、ビジネスアプリケーションのあり方を根本から変えようとしています。Salesforce はこの変革の波をリードするべく、信頼性を中核に据えた対話型AIアシスタント「Einstein Copilot」をプラットフォームに深く統合しました。

私たち Salesforce アーキテクトにとって、Einstein Copilot は単なる新機能ではありません。これは、ビジネスプロセスを再定義し、ユーザー体験を革新し、そして全く新しい価値を創出するための強力な「設計要素」です。標準機能で提供されるアクション(例えば「取引先の要約」や「ケースのドラフト作成」など)だけでも十分に強力ですが、その真価はプラットフォームの拡張性、すなわち Apex を用いたカスタムアクションの構築にあります。

アーキテクトの視点から見た応用シナリオは多岐にわたります:

  • 複雑なビジネスロジックの自動化:「この顧客の過去の購入履歴とサポートケースを分析し、最も適切なアップセル商品を3つ提案して」といった自然言語の指示に基づき、複数のオブジェクトを横断する複雑な Apex ロジックを実行し、結果を返す。
  • 外部システム連携の簡素化:「顧客番号12345の最新の請求書データをERPシステムから取得して」という指示で、Apex Callout を実行し、外部システムのデータを取得・整形して Copilot に提示する。これにより、ユーザーは複数のシステムを意識することなく、Salesforce 上でシームレスな操作が可能になります。
  • 業界特化のインサイト生成:金融業界であれば「この顧客のポートフォリオのリスク評価を実行し、コンプライアンス上の注意点をリストアップして」といった業界固有のロジックを Apex で実装し、専門的な分析をオンデマンドで提供する。

このように、Einstein Copilot のカスタムアクションは、Salesforce を単なるデータリポジトリから、企業のインテリジェンスとプロセスが有機的に結合した「対話型ビジネスOS」へと昇華させるための鍵となります。本稿では、アーキテクトの視点から、Einstein Copilot のカスタムアクションを Apex で構築する際のアーキテクチャ設計、原理、そしてベストプラクティスについて詳述します。


原理説明

Einstein Copilot のアーキテクチャを理解する上で重要なのは、その中心に「Einstein Trust Layer」が存在することです。Trust Layer は、LLM との対話におけるデータのマスキング(動的グラウンディング)、有害なコンテンツのフィルタリング、そして監査証跡の記録といった、エンタープライズレベルでAIを利用するために不可欠なセキュリティとガバナンスの機能を提供します。

アーキテクトとして私たちが設計するカスタムアクションは、この Trust Layer を介して安全に実行されます。その仕組みは以下の通りです。

1. ユーザープロンプトの解析

ユーザーが Copilot に「取引先『ABC商事』に関連する進行中の商談をすべてクローズして、その理由を Chatter に投稿して」といったプロンプトを入力します。

2. インテント(意図)の特定とアクションのマッチング

Copilot の中核であるオーケストレーションエンジンがプロンプトを解析し、ユーザーの意図を理解します。そして、その意図に最も合致するアクションを、標準アクションおよび利用可能なカスタムアクションのライブラリから検索します。この時、Apex クラスやメソッドに付与された @InvocableMethod アノテーションの `label` や `description` 属性が、アクションの発見可能性(Discoverability)において極めて重要な役割を果たします。

3. Apex アクションの実行

適切なカスタムアクション(この例では「商談の一括クローズと理由の投稿」という Apex アクション)が見つかると、Copilot はプロンプトから抽出したパラメータ(取引先名、クローズ理由など)を Apex メソッドの引数として渡し、これを実行します。重要なのは、この Apex コードは常に実行ユーザーのコンテキストで動作するという点です。これにより、Salesforce の共有ルールや項目レベルセキュリティが厳格に適用され、ユーザーがアクセス権を持たないデータに AI がアクセスすることを防ぎます。

4. 結果の生成と返却

Apex アクションは処理結果(成功メッセージやエラー情報など)を Copilot に返却します。Copilot はその結果を解釈し、LLM を用いて自然で分かりやすい言葉に変換して、最終的な応答をユーザーに提示します。「ABC商事に関連する3件の進行中商談をクローズし、理由を Chatter に投稿しました。」といった形です。

この一連の流れからわかるように、Apex カスタムアクションは、LLM の能力を Salesforce の厳格なデータセキュリティと強力なビジネスロジック実行能力に「グラウンディング(Grounding、根付かせる)」させるための重要なインターフェースです。アーキテクトは、ビジネス要件を、このアーキテクチャ上で安全かつ効率的に動作する、再利用可能で堅牢な Apex アクションへと変換する責務を負います。


示例代码

ここでは、特定の取引先に関連するオープンケースの中から、優先度が「High」のものを検索し、その件名をリストとして返すというシンプルなカスタムアクションを考えます。このアクションは、サポートマネージャーが「最優先で対応すべきケースは?」と Copilot に尋ねるシナリオで役立ちます。

このコードは Salesforce Developer 公式ドキュメントの設計思想に基づいています。Apex Invocable Method を使用して、Einstein Copilot から呼び出し可能なアクションを定義します。

public with sharing class HighPriorityCaseAction {

    // Einstein Copilotから呼び出されるためのラッパークラス
    // 1つのInvocableMethodは1つの入力パラメータしか受け取れないため、複数の値を渡す場合はこのようにクラスでラップする
    public class CopilotInput {
        @InvocableVariable(label='Account Name' description='The name of the Account to search for high-priority cases.' required=true)
        public String accountName;
    }

    // Copilotへの戻り値を定義するラッパークラス
    // 複雑なデータを構造化して返すことができる
    public class CopilotOutput {
        @InvocableVariable(label='High-Priority Case Subjects' description='A list of subjects from high-priority cases.')
        public List<String> caseSubjects;
    }

    /**
     * @description Einstein Copilotから呼び出されるメソッド。
     * labelとdescriptionは、Copilotがこのアクションの目的を理解し、ユーザーのプロンプトとマッチングするために非常に重要。
     */
    @InvocableMethod(
        label='Get High-Priority Case Subjects'
        description='Finds all open, high-priority cases for a given Account and returns their subjects.'
        category='Case Management'
    )
    public static List<CopilotOutput> findHighPriorityCases(List<CopilotInput> inputs) {
        
        // InvocableMethodは常にリスト形式で入力を受け取るため、最初の要素を取得する
        CopilotInput input = inputs[0];
        String accountName = input.accountName;

        // 結果を格納するためのリストを初期化
        List<CopilotOutput> allOutputs = new List<CopilotOutput>();
        CopilotOutput output = new CopilotOutput();
        List<String> subjects = new List<String>();
        
        // Apexのベストプラクティスに従い、SOQLインジェクションを防ぐために変数をエスケープする
        String escapedAccountName = String.escapeSingleQuotes(accountName);

        try {
            // 指定された取引先名を元に、一致する取引先のIDを検索
            // エラーハンドリング:取引先が見つからない可能性を考慮
            List<Account> accounts = [SELECT Id FROM Account WHERE Name = :escapedAccountName LIMIT 1];

            if (!accounts.isEmpty()) {
                Id accountId = accounts[0].Id;

                // 取引先IDを元に、ステータスがクローズされておらず、優先度が'High'のケースを検索
                // SOQLクエリは効率的であるべき。必要なフィールドのみを取得する。
                for (Case c : [
                    SELECT Subject 
                    FROM Case 
                    WHERE AccountId = :accountId 
                    AND IsClosed = false 
                    AND Priority = 'High'
                    ORDER BY CreatedDate DESC
                    LIMIT 10
                ]) {
                    subjects.add(c.Subject);
                }
            }
            
            output.caseSubjects = subjects;

        } catch (Exception e) {
            // 例外処理はアーキテクチャ上非常に重要。
            // Copilotにエラーを適切に伝えることで、ユーザーは問題が発生したことを認識できる。
            // ここではシンプルにAuraHandledExceptionをスローするが、より詳細なエラーロギング機構を実装することが望ましい。
            throw new AuraHandledException('An error occurred while fetching high-priority cases: ' + e.getMessage());
        }
        
        allOutputs.add(output);
        return allOutputs;
    }
}

注意事項

Einstein Copilot のカスタムアクションを設計・実装する際には、アーキテクトとして以下の点に特に注意を払う必要があります。

権限とセキュリティ (Permissions and Security)

  • 実行コンテキスト: 前述の通り、Apex アクションは必ず `with sharing` で定義し、実行ユーザーの共有設定を遵守させるべきです。これにより、データ漏洩のリスクを最小限に抑えます。
  • カスタム権限: Einstein Copilot の利用自体は `EinsteinCopilot.accessCopilot` というカスタム権限で制御されます。さらに、特定のアクションを特定のプロファイルのユーザーのみに利用させたい場合、Apex 内でユーザーのプロファイルや権限セットをチェックするロジックを組み込むことを検討します。
  • SOQLインジェクション: ユーザーの入力を動的にSOQLクエリに組み込む際は、必ず `String.escapeSingleQuotes()` を使用するか、静的クエリとバインド変数を用いることで、SOQLインジェクション攻撃を防止しなければなりません。

API制限とガバナンス (API Limits and Governance)

  • ガバナ制限: Apex アクションは、通常の Apex トランザクションと同様に、SOQLクエリの発行回数(100回)、DMLステートメントの実行回数(150回)、CPU時間(10秒)といったガバナ制限に従います。アクションは可能な限り軽量かつ効率的に設計し、単一の責任を持つように分割(Single Responsibility Principle)することが重要です。
  • パフォーマンス: Copilot は対話型ツールであるため、ユーザーは迅速な応答を期待します。アクションの実行に時間がかかりすぎると、ユーザー体験が著しく損なわれます。複雑な処理は非同期(@future や Queueable Apex)で行い、まずは即時応答を返すようなアーキテクチャパターンを検討する必要があります。
  • アクションの粒度: 1つのアクションに多くの機能を詰め込みすぎると、LLM がその機能を正しく理解・利用することが難しくなります。アクションは、一つの明確なタスクを実行するように設計するべきです。

エラーハンドリング (Error Handling)

  • 明確な例外処理: `try-catch` ブロックを用いて予期せぬエラーを捕捉し、`AuraHandledException` をスローして Copilot にエラーが発生したことを明確に伝えるべきです。例外メッセージには、エンドユーザーに提示しても問題ない、分かりやすい情報を含めることが推奨されます。
  • 正常系の「失敗」: 「該当するデータが見つかりませんでした」といったケースは技術的なエラーではありませんが、ユーザーにとってはアクションが期待通りに完了しなかった状況です。このような場合は、空のリストや特定のステータスを示す値を返すことで、Copilot が「該当する優先度の高いケースは見つかりませんでした」といった適切な応答を生成できるように設計します。


まとめとベストプラクティス

Einstein Copilot は、Salesforce プラットフォームにおける AI 活用の新しいパラダイムを提示します。アーキテクトとして、私たちはこの強力なツールを最大限に活用し、ビジネスに真の価値をもたらすソリューションを設計する責任があります。Apex を用いたカスタムアクションの構築は、そのための最も直接的で強力な手段です。

最後に、Einstein Copilot カスタムアクションを設計する上でのベストプラクティスをまとめます。

  1. 戦略的整合性 (Strategic Alignment): 何でもかんでもアクション化するのではなく、最もインパクトの大きいビジネス課題を特定し、それを解決するためのアクションを優先的に設計します。
  2. モジュール性と再利用性 (Modularity and Reusability): アクションは小さく、単一の責任を持つように設計します。これにより、テストが容易になり、他のシナリオでの再利用も可能になります。共通のロジックはヘルパークラスに切り出すなど、Apex の設計原則に従います。
  3. 説明可能性 (Explainability): `@InvocableMethod` や `@InvocableVariable` の `label` と `description` は、LLM がアクションを理解するための最も重要なメタデータです。誰が読んでも理解できる、明確で具体的な説明を記述してください。これがプロンプトエンジニアリングの第一歩です。
  4. パフォーマンス第一 (Performance First): 対話型UIの裏側で動くことを常に意識し、パフォーマンスを最優先に考えたコーディングを心がけます。SOQLクエリの最適化、データのバルク処理は基本です。
  5. スケーラビリティの考慮 (Consider Scalability): 現在の利用状況だけでなく、将来的に多くのユーザーが同時にこのアクションを利用する可能性を考慮し、ロック競合やガバナ制限に抵触しないスケーラブルな設計を採用します。
  6. 反復的な改善 (Iterative Improvement): 最初から完璧なアクションを作ることは困難です。まずは基本的な機能をリリースし、ユーザーからのフィードバックや利用ログを分析しながら、継続的にアクションを改善していくアプローチが成功の鍵となります。

Einstein Copilot と Apex の組み合わせは、Salesforce アーキテクトに無限の可能性を提供します。これらの原則に基づき、信頼性と拡張性の高い AI ソリューションを構築することで、企業のデジタルトランスフォーメーションを次のレベルへと導くことができるでしょう。

コメント