SalesforceからREST APIを使って生成AIを利用する方法
Salesforceにも生成AI用のREST APIがありますね。
生成型 AI とエージェント型 AI の環境は広大で急速に変化しているため、Salesforce のすべての顧客が Salesforce のみのアプローチを採用することは考えにくいです。しかし、それが戦略になった場合、エージェントを展開する作業を完了し、それを他のシステムに公開したい場合は、API 経由でアクセスする必要があります。
Agentforce API がライブで利用可能になりました。この API は、サーバー送信イベント (SSE) HTTP 標準を使用します。SSE は、長い応答の進行ビットをチャンクするために使用される、長時間実行される一方向 HTTP 接続です。これは、実行中のエージェント要求の進行状況に関するフィードバックをユーザーに提供するために、一度に少しずつ推論を生成する長時間実行されるエージェントに最適です。ただし、API の使用がエージェント間である場合は、標準の単一の HTTP 要求を使用する同期モードもあり、実装が簡単になります。
SSE は HTTP の長年の機能であり、JavaScript、Python、Java などの多くの言語で十分にサポートされています。
試せる環境もできたようですね。
拡張された Developer Edition の技術リソースは、 Agentforce Developer CenterおよびData Cloud Developer Centerで入手できます。これには、ブログ投稿、ビデオ、ポッドキャスト、ドキュメント、Trailhead 学習が含まれます。
フローからプロンプト テンプレートを呼び出すことで、要約はできそうな感じですね。
Connect REST API のおかげで、Salesforce プラットフォーム内でプログラム的にプロンプト テンプレートを呼び出すことができるだけでなく、サードパーティ システムからも呼び出すことができます。
プロンプトテンプレートは、生成 AI 機能をアプリケーションやワークフローに統合するために使用されます。これらは、AI 開発ツールの Einstein 1 Studio スイートの一部であるPrompt Builderで作成できます。プロンプトテンプレートを呼び出すと、最初に解決され、プロンプトテンプレート内のすべてのデータ参照 (マージ項目、関連リスト、フロー、Apex など) が表示され、Einstein Trust Layerを介して送信されてから、大規模言語モデル (LLM) から応答が生成されます。すぐに使用できるエントリポイントの中には、メールコンポーザや動的フォームを含む Lightning レコードページなど、Salesforce からプロンプトテンプレートを呼び出すことができるものもあります。
このブログ記事では、フローと Apex を使用して Salesforce アプリケーション内から、また REST API を使用して Salesforce 外部の独自のアプリケーションからプロンプト テンプレートを呼び出すためのカスタム エントリ ポイントを作成する方法について説明します。
プロンプトテンプレートを実行するためのエントリポイント
プロンプト テンプレートを呼び出すと、いくつかの変換を経て最終的なプロンプトが生成され、それが LLM に送信されます。LLM の応答、つまり「完了」も処理され、AI 対応のビジネス ワークフローまたはアプリで使用する最終的な生成が生成されます。すぐに使用できるエントリ ポイントに加えて、Flow または Apex で実装されたカスタム ビジネス ロジックから、または REST API を介して外部システムからこのプロセスを開始できます。
以下の表 (前回のブログ投稿にも含まれています) は、プロンプト テンプレートの種類に応じて、プロンプト テンプレートに含まれる可能性のある入力とエントリ ポイントをまとめたものです。
プロンプトテンプレートの種類 | 説明 | 入力 | エントリーポイント |
---|---|---|---|
営業メール Email Composer を使用してパーソナライズされた電子メールを作成します。 | 連絡先またはリード、およびオプションで選択した別のオブジェクト | メール作成,フロー,Apex,REST API,Copilotアクション | |
フィールド生成 | レコード ページ内のカスタム フィールドに保存されるテキストを生成します。 | お好みのオブジェクト | レコードページ,フロー,Apex,REST API,Copilotアクション |
フレックス | どこでも使用できるテキストを生成します。 | 選択したオブジェクトは最大5つまで | フロー,Apex,REST API,Copilotアクション |
記録の概要 | Einstein Copilot で使用されるレコードの概要を生成します。 | お好みのオブジェクト | Copilotアクション |
Flow からプロンプト テンプレートを呼び出す
作成して保存およびアクティブ化されたすべてのプロンプト テンプレートは、通常のアクション要素を使用してフローから呼び出すことができる呼び出し可能なアクションとして自動的に公開されます。要素を作成するときにプロンプト テンプレート カテゴリを選択することで、それらをフィルター処理できます。
例を見てみましょう。フローから以下のようなプロンプト テンプレートを呼び出すとします。
特定の入力を持つすべてのテンプレートは、フローから呼び出されたときに、その入力を関連エンティティとして渡す必要があります。以前のブログ投稿で、各プロンプト テンプレート タイプの入力オプションについて説明しました。 この例では、これは連絡先オブジェクトに関連付けられたフィールド生成プロンプト テンプレートであるため、連絡先を関連エンティティとして渡す必要があります。
ご覧のとおり、Flow からプロンプト テンプレートを呼び出すのは非常に簡単なので、AI によって生成された出力を通常のビジネス ワークフローに組み込むのが非常に便利になります。
Apex からプロンプト テンプレートを呼び出す
Apex でプロンプト テンプレートを呼び出すには、Apex の Connect APIのクラスとメソッドを使用します。
今回は、少し複雑な flex プロンプト テンプレートを例として使用します。
このテンプレートは、不動産業者が販売している物件を宣伝できるソーシャル投稿を生成するために使用されます。プロンプトテンプレートの本文は、ここに示されているものよりも長いことに注意してください。Apex から呼び出す方法を見てみましょう。
まず、Flow と同様に、各プロンプト テンプレートでは異なる入力が渡されることが想定されるため、Apex で入力を作成し、それをテンプレートに渡す必要があります。サンプルの Flex プロンプト テンプレートは Property__c カスタム オブジェクトに関連付けられているため、Apex から呼び出すときにプロパティ レコードを渡す必要があります。
Map<String, String> property = new Map<String, String>();
property.put('id', propertyId); // Don't need to pass other field values even if referenced in the template, just the Id
ConnectApi.WrappedValue propertyValue = new ConnectApi.WrappedValue();
propertyValue.value = property;
Map<String, ConnectApi.WrappedValue> inputParams = new Map<String, ConnectApi.WrappedValue>();
inputParams.put('Input:Property', propertyValue); // Property is the API Name we gave to the input
ConnectApi.EinsteinPromptTemplateGenerationsInput executeTemplateInput = new ConnectApi.EinsteinPromptTemplateGenerationsInput();
executeTemplateInput.additionalConfig = new ConnectApi.EinsteinLlmAdditionalConfigInput();
executeTemplateInput.additionalConfig.applicationName = 'PromptBuilderPreview';
executeTemplateInput.isPreview = false;
executeTemplateInput.inputParams = inputParams;
インスタンスでは、呼び出しのいくつかのパラメータEinsteinPromptTemplateGenerationsInputを制御できることに注意してください。興味深いものは次のとおりです。
- このisPreviewパラメータは、プロンプトを解決して解決結果を返すか、解決してから LLM に送信して完了を返すかを制御します。
- このnumGenerationsパラメータは、LLM から取得する応答の数を制御します。デフォルトは 1 ですが、複数のオプションから目的の応答を選択する場合は、1 回の呼び出しで複数の補完を返すことができます。
- このtemperatureパラメータは、モデルが取るリスクの数を制御します。デフォルト値および最小値は 0 (リスクなし) で、革新性は低く、決定論的であるものの、より正確な応答が生成されます。最大値は 1 です。
- このfrequencyPenaltyパラメータは、トークンがこの世代または以前の世代に何回出現したかを考慮して、生成されたトークンの繰り返しを制御するために使用されます。
generateMessagesForPromptTemplate最後に、メソッドを呼び出して、テンプレート API 名とEinsteinPromptTemplateGenerationsInput作成したインスタンスを渡して呼び出しを実行します。
ConnectApi.EinsteinPromptTemplateGenerationsRepresentation generationsOutput = ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate(
'Generate_Social_Media_Posts',
executeTemplateInput
);
応答はEinsteinPromptTemplateGenerationsRepresentation、解決されたプロンプト、世代、およびその他のパラメータを含む になります。
世代が 1 つだけの場合は、次のようにして応答(タイプEinsteinLLMGenerationItemOutput) テキストを抽出できます。
ConnectApi.EinsteinLLMGenerationItemOutput response = generationsOutput.generations[0];
String response = response.text;
// Do something with the text, which will be valid JSON in our example
応答には、 という興味深いパラメータも含まれていることに注意してください。safetyScoreRepresentationこれは、毒性検出モデルに渡すことで Salesforce が計算する一連の測定値であり、応答の安全性を評価します。安全性スコアに含まれる情報を確認してください 。
Flex プロンプト テンプレートの例に戻ると、テンプレートに有効な JSON を返すように指示していることに注意してください。これは、Apex からプロンプト テンプレートを呼び出すときに非常に便利な方法です。応答が Lightning Web コンポーネントに送信されている場合、Apex または JavaScript のいずれかで簡単に解析できるためです。
ご覧のとおり、Apex からプロンプト テンプレートを呼び出すことは、AI によって生成された出力をより複雑なバックエンドのビジネス ロジックに組み込むときに非常に便利です。そしてもちろん、LWC から Apex を呼び出して、フロントエンドでそれらの応答を表示できることも忘れないでください。
REST API からプロンプト テンプレートを呼び出す
最後に、外部システムからプロンプト テンプレートを呼び出すには、 Connect REST APIで公開されているリソースを使用します。
/einstein/prompt-templates/promptTemplateDevName/generations
Flow や Apex の場合と同じように、プロンプト テンプレートが期待する入力をリクエスト ボディに渡す必要があります。
{
"isPreview": "false",
"inputParams": {
"valueMap": {
"Input:Property": {
"value": {
"id": "a011Q00001EQYXOQA5"
}
}
}
},
"additionalConfig": {
"numGenerations": 1,
"temperature": 0,
"frequencyPenalty": 0.0,
"presencePenalty": 0.0,
"additionalParameters": {},
"applicationName": "PromptBuilderPreview"
}
}
生成のレスポンス本体は次のようになります。
{
"generations": [
{
"parameters": "{finish_reason=stop, index=0, logprobs=null}",
"responseId": "893db990-7acb-4845-ad65-54d82606ca65",
"text": "{\n "twitter": "🌟 Exciting new listing! Explore our luxurious property, [Provide:{PROPERTY NAME}], featuring [Provide:{BEDS}] beds, [Provide:{BATHS}] baths, and an asking price of [Provide:{ASKING PRICE}]. Click the link for a sneak peek! [Provide:{PICTURE}]",\n "linkedin": "🌟🌟 Don't miss out on this incredible opportunity! Discover the stunning property, [Provide:{PROPERTY NAME}], offering [Provide:{BEDS}] beds, [Provide:{BATHS}] baths, and an asking price of [Provide:{ASKING PRICE}]. Click the link below to see more! [Provide:{PICTURE}]",\n "blockkit": {\n "blocks": [\n {\n "type": "section",\n "text": {\n "type": "mrkdwn",\n "text": "*Luxury Property Alert!* \n\n🌟🌟🌟\n\nIntroducing [Provide:{PROPERTY NAME}], a magnificent home with [Provide:{BEDS}] beds and [Provide:{BATHS}] baths. Priced at [Provide:{ASKING PRICE}]."\n },\n "accessory": {\n "type": "image",\n "image_url": "[Provide:{PICTURE}]",\n "alt_text": "Luxury Property"\n }\n },\n {\n "type": "divider"\n },\n {\n "type": "actions",\n "elements": [\n {\n "type": "button",\n "text": {\n "type": "plain_text",\n "text": "View Property",\n "emoji": true\n },\n "url": "https://d1q000001ewauuaq-dev-ed.develop.lightning.force.com/[Provide:{RECORD ID}]"\n }\n ]\n }\n ]\n }\n}"
}
],
"parameters": null,
"prompt": "You're the community manager for Dreamhouse, a real estate agency that sells luxury properties. \nCreate social media posts for twitter, linkedin and slack (block kit format) describing the property.\n\nInstructions:\n"""\nUse clear, concise, and straightforward language using the active voice and strictly avoiding the use of filler words and phrases and redundant language.\nMake sure the response is valid JSON.\nWhen you generate the posts, include the name of the property, [Provide:{PROPERTY NAME}], and explain the characteristics of the house, such as [Provide:{BATHS}], [Provide:{BEDS}]and[Provide:{ASKING PRICE}]. Also include a link to the picture of the property, [Provide:{PICTURE}]. \n\nThe twitter post should include emojis.\nThe linkedin post should include several emojis and bullets, and also have a special output formatting: Text render environment only supports Unicode and emoji. Use symbols from Unicode’s Mathematical Alphanumeric Symbols block liberally to produce facsimiles of bold, italics, line separation, and other formatting. Examples for a sample sentence:\n\nitalics sans: 𝘛𝘩𝘦 𝘘𝘶𝘪𝘤𝘬 𝘉𝘳𝘰𝘸𝘯 𝘍𝘰𝘹 𝘑𝘶𝘮𝘱𝘦𝘥 𝘖𝘷𝘦𝘳 𝘵𝘩𝘦 𝘭𝘢𝘻𝘺 𝘥𝘰𝘨.\nbold sans: 𝗧𝗵𝗲 𝗤𝘂𝗶𝗰𝗸 𝗕𝗿𝗼𝘄𝗻 𝗙𝗼𝘅 𝗝𝘂𝗺𝗽𝗲𝗱 𝗢𝘃𝗲𝗿 𝘁𝗵𝗲 𝗹𝗮𝘇𝘆 𝗱𝗼𝗴.\nbold italic sans: 𝙏𝙝𝙚 𝙌𝙪𝙞𝙘𝙠 𝘽𝙧𝙤𝙬𝙣 𝙁𝙤𝙭 𝙅𝙪𝙢𝙥𝙚𝙙 𝙊𝙫𝙚𝙧 𝙩𝙝𝙚 𝙡𝙖𝙯𝙮 𝙙𝙤𝙜.\nitalics serif: 𝑇ℎ𝑒 𝑄𝑢𝑖𝑐𝑘 𝐵𝑟𝑜𝑤𝑛 𝐹𝑜𝑥 𝐽𝑢𝑚𝑝𝑒𝑑 𝑂𝑣𝑒𝑟 𝑡ℎ𝑒 𝑙𝑎𝑧𝑦 𝑑𝑜𝑔.\n\nThe block kit code should be valid block kit code.\nWhen you generate the block kit code: \n- Add several sections. \n- Include the name of the property, [Provide:{PROPERTY NAME}], and explain the characteristics of the house, such as [Provide:{BATHS}], [Provide:{BEDS}] and [Provide:{ASKING PRICE}]. \n- Include a the picture of the property, which image_url is [Provide:{PICTURE}]. \n- Include a button which url points to https://d1q000001ewauuaq-dev-ed.develop.lightning.force.com/[Provide:{RECORD ID}]\n- Include emoticons so the post is more visual.\n\nExample of block kit code:\n\n{\n "blocks": [\n {\n "type": "section",\n "text": {\n "type": "mrkdwn",\n "text": "Hello, Assistant to the Regional Manager Dwight! *Michael Scott* wants to know where you'd like to take the Paper Company investors to dinner tonight.\n\n *Please select a restaurant:*"\n }\n },\n {\n "type": "divider"\n },\n {\n "type": "section",\n "text": {\n "type": "mrkdwn",\n "text": "*Farmhouse Thai Cuisine*\n:star::star::star::star: 1528 reviews\n They do have some vegan options, like the roti and curry, plus they have a ton of salad stuff and noodles can be ordered without meat!! They have something for everyone here"\n },\n "accessory": {\n "type": "image",\n "image_url": "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg",\n "alt_text": "alt text for image"\n }\n },\n {\n "type": "section",\n "text": {\n "type": "mrkdwn",\n "text": "*Kin Khao*\n:star::star::star::star: 1638 reviews\n The sticky rice also goes wonderfully with the caramelized pork belly, which is absolutely melt-in-your-mouth and so soft."\n },\n "accessory": {\n "type": "image",\n "image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/korel-1YjNtFtJlMTaC26A/o.jpg",\n "alt_text": "alt text for image"\n }\n },\n {\n "type": "section",\n "text": {\n "type": "mrkdwn",\n "text": "*Ler Ros*\n:star::star::star::star: 2082 reviews\n I would really recommend the Yum Koh Moo Yang - Spicy lime dressing and roasted quick marinated pork shoulder, basil leaves, chili & rice powder."\n },\n "accessory": {\n "type": "image",\n "image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/DawwNigKJ2ckPeDeDM7jAg/o.jpg",\n "alt_text": "alt text for image"\n }\n },\n {\n "type": "divider"\n },\n {\n "type": "actions",\n "elements": [\n {\n "type": "button",\n "text": {\n "type": "plain_text",\n "text": "Farmhouse",\n "emoji": true\n },\n "value": "click_me_123"\n },\n {\n "type": "button",\n "text": {\n "type": "plain_text",\n "text": "Kin Khao",\n "emoji": true\n },\n "value": "click_me_123",\n "url": "https://google.com"\n },\n {\n "type": "button",\n "text": {\n "type": "plain_text",\n "text": "Ler Ros",\n "emoji": true\n },\n "value": "click_me_123",\n "url": "https://google.com"\n }\n ]\n }\n ]\n}\n\nThe response should have the next format:\n{\n "twitter": [here goes the twitter post, that should have less than 280 characters],\n "linkedin": [here goes the linkedin post, that should have between 1500 and 2000 characters], \n "blockkit": [here goes the block kit post], \n}\n\n"""\n\nNow generate the posts.\n\n\n",
"promptTemplateDevName": "Generate_Social_Media_Posts",
"requestId": "chatcmpl-97KqPl1TEiOgMdnKKPQ9aVs7rPJGr"
}
Connect REST API のおかげで、Salesforce プラットフォーム内でプログラム的にプロンプト テンプレートを呼び出すことができるだけでなく、サードパーティ システムからも呼び出すことができます。想像力を働かせて、この機能を使用して実装できるすばらしいユース ケースをすべて考えてみましょう。
実際に組んでみました。
ちょうどそのまま利用できるトレイルヘッドがあります。
説明通りにQuick Summaryというプロンプト テンプレートを作成します。
このプロンプトテンプレートをフローのアクションで呼び出します。
アクションの注意点は2つありました。
プロンプトでは優先度、件名などを使っているので1ではトリガーしたケースを指定します。項目を指定しなくていいみたいです。
2つ目はプロンプトだけでカスタム項目を更新できると思っていましたが、うまくいかなかったです。アクションの出力を変数に格納して、別途この変数でレコードを更新しました。
結果は以下のようになりました。日本語では試してません...