LWRで構築する英国紅茶ブランドサイト 「The Royal Brew」 制作日誌、最終回(第5弾)です。
これまでの連載で、商品詳細ページのUI、カート連携、ページ遷移と、ECサイトの骨格を作り上げてきました。 そして今回、このサイトに命を吹き込む最後のピース、「AIエージェント」 を実装しました。
まずは、完成した姿をご覧ください。
画面右下に控えるのは、黄金の精神を宿した執事 「セバスチャン」。 クリックすると優雅なチャットウィンドウが開き、お客様の好みに合わせた紅茶の提案や、淹れ方の指南を行います。
なぜ「標準機能」を使わないのか?
通常、Experience CloudサイトにAgentforceを導入する場合、Salesforce標準の「組み込みメッセージング」コンポーネントを使用するのが一般的です。
もし、あなたが「まずは手早く標準機能でAIを導入したい」とお考えなら、以下の記事で完全な手順を解説していますので、そちらをご覧ください。
▲ 基本編:AgentforceをExperience Cloudに標準実装する完全ガイド
しかし、「The Royal Brew」においては、画面の右下に「よくあるチャットボタン」が表示されることなど許されません。 私たちに必要なのは、画一的なチャットボットではなく、ブランドの世界観を体現する 「執事」 なのです。
そこで今回は、標準UIもフロー(Flow)も使わず、LWCとApexだけでAgentforceを直接呼び出す という、開発者魂全開のフルスクラッチ実装を行いました。
今回の記事で作成した「執事セバスチャン」の全ソースコード(Apex, LWC, CSS, Meta XML)は、GitHubで公開しています。 ぜひ Clone して、あなたの組織で動かしてみてください。
収録内容:
classes/RoyalButlerController.cls: Agentforce呼び出しロジックlwc/royalButler: チャットUIコンポーネント一式staticresources/SebastianAvatar: アイコン画像
アーキテクチャ:ApexからAgentforceを直叩きする
組み込みサービスの「カスタムUI設定」でも部分的なデザイン変更は可能ですが、ウィンドウの形状やアニメーションまで完全に制御するには限界があります。 今回の実装のキモは、Agentforceを純粋な 「AI API」 として扱い、UIを100%自作することです。
1. 組み込みサービスの「カスタムUIコンポーネント」とは?
これは、「Salesforceが提供するチャットウィジェットの枠組み(フレーム)の中で、特定の部分(ヘッダーや最小化時のボタンなど)だけを自作LWCに差し替える」機能です。
- メリット: チャットの接続確立、履歴の保持、添付ファイル送信などの「面倒な機能」はSalesforce任せにできる。
- デメリット:
- 「枠」の制約: 基本的に「四角いウィンドウ」という形状や、画面上の配置ルールからは逃れられません。
- 世界観の限界: 「黄金の像をクリックしたら羊皮紙が開く」といった、根本的なUI構造の変更は困難です。
- 挙動: あくまで「チャットボット」として振る舞うため、今回のような「執事」としての独自演出(思考アニメーションや独自のセッション管理)を組み込むにはハックが必要です。
2. 今回の実装(API + LWCフルスクラッチ)
これは、Agentforceを単なる「頭脳(バックエンドAPI)」として扱い、見た目(フロントエンド)を100%自作する方法です。
- メリット:
- 完全な自由: 形も、動きも、配置も、すべてCSSとJSで思いのまま。
- 「セバスチャン」になれる: アイコンもウィンドウも、標準チャットの気配を完全に消せます。
- デメリット:
- 全部自作: 履歴の表示、スクロール制御、エラー処理などを自分で書く必要がある(今回はそれを乗り越えました!)。
結論: 「The Royal Brew」のような没入型ブランド体験を作るなら、今回の手法が唯一の正解です。
採用した手法は、Apexの Invocable.Action クラスを使って、Agentforceの標準アクション generateAiAgentResponse を直接実行する というものです。
- LWC: 独自のチャットUI描画、セッションID管理、思考アニメーション。
- Apex: 標準アクションを動的に生成し、Agentforceにプロンプトを投げる。
- Agentforce: 回答を生成して返す。
この構成なら、フローを作成する手間すら不要で、デザインの自由度は無限大です。
Step 1: 執事「セバスチャン」の定義
まずは主役の準備です。 「黄金の像」のアイコンを用意し、Agentforceの設定(Agent Builder)で以下のようなロールを与えました。
- 名前: Sebastian
- 役割: 英国紅茶専門店「The Royal Brew」の専属執事
- 指示 (Instructions):あなたは忠実な執事セバスチャンです。常に「〜でございます」といった最高敬語を使い、冷静沈着に振る舞ってください。お客様の気分に合わせて最適な紅茶を提案し、その優雅なティータイムをサポートするのがあなたの使命です。
Step 2: Apexコントローラー (The Bridge)
ここが技術的なハイライトです。 Invocable.Action.createCustomAction の第一引数に 'generateAiAgentResponse' を指定することで、Agentforceのエンドポイントを直接叩くことができます。
public with sharing class RoyalButlerController {
// 【重要】Agent Builderで設定したエージェントのAPI参照名
private static final String AGENT_API_NAME = 'Sebastian';
/**
* AgentforceをInvocable Action経由で直接呼び出すメソッド
* @param userMessage ユーザーの入力メッセージ
* @param sessionId 会話のセッションID(文脈維持用)
* @return Agentからの回答テキスト
*/
@AuraEnabled
public static Map<String, Object> askSebastian(String userMessage, String sessionId) {
try {
// 1. Invocable Actionの作成
Invocable.Action action = Invocable.Action.createCustomAction('generateAiAgentResponse', AGENT_API_NAME);
// 2. パラメータの設定
// 標準アクションの仕様に基づき、'userMessage' を渡します
action.setInvocationParameter('userMessage', userMessage);
// セッションIDを渡すことで、Agentは「さっきの話」を記憶できます
if (String.isNotBlank(sessionId)) {
action.setInvocationParameter('sessionId', sessionId);
}
// 3. 実行
List<Invocable.Action.Result> results = action.invoke();
// 4. 結果の取得と解析
if (results.size() > 0 && results[0].isSuccess()) {
Map<String, Object> output = results[0].getOutputParameters();
// 全ての出力を返す (response, sessionId 等)
return output;
} else {
// エラーログを出力
String errorDetails = 'Agent Error: ' + results[0].getErrors();
System.debug(errorDetails);
throw new CalloutException(errorDetails);
}
} catch (Exception e) {
String debugMsg = 'Exception: ' + e.getMessage() + ' Stack: ' + e.getStackTraceString();
System.debug(debugMsg);
// ユーザー向けの一般的なエラーメッセージ
String userMsg = '申し訳ございません。執事セバスチャンとの通信中にエラーが発生いたしました。';
AuraHandledException ahe = new AuraHandledException(userMsg);
ahe.setMessage(userMsg);
throw ahe;
}
}
}
Step 3: LWCによる独自UI (The Face)
最後に、フロントエンドです。 「The Royal Brew」のブランドカラー(ネイビー&ゴールド)を基調としたデザインを組み上げました。
royalButler.html
アイコン画像には、静的リソースにアップロードした「黄金のセバスチャン」を使用しています。
<img src={butlerIconUrl} class="header-avatar"/>

royalButler.js
次回の会話のために、サーバーで払い出されたセッションIDを保持します。
...
// Apex経由でAgentforceを呼び出し
const result = await askSebastian({
userMessage: text,
sessionId: this.sessionId // 初回はnull/undefined
});
...
// 次回の会話のためにセッションIDを保持
if (result.sessionId) {
this.sessionId = result.sessionId;
}
...
royalButler.css
入力の利便性にも気を遣いました。Shift + Enter で改行を可能にしたり、Enterキーを押した時の挙動を制御したり、工夫が入っています。
完成した世界観
こうして実装されたセバスチャンは、単なる検索ボットではありません。 「レモン風味の紅茶はある?」と聞けば、
「かしこまりました。レモン風味をご所望でございましたら、「Lemon」をお薦めいたします。厳選された茶葉にシチリア産レモンの香気を合わせた伝統的なブレンドでございます。……」
と、完璧な執事言葉で返してくれます。
まとめ:LWC開発者なら「自作」しよう
Agentforceは標準機能だけでも強力ですが、LWC × Apex (Invocable Action) を組み合わせることで、ブランド体験を損なわない、自由自在なUI/UXを実現できることが分かりました。
「標準でサクッと導入」か、「LWCでとことんこだわる」か。 プロジェクトの要件に合わせて選べるのが、Salesforceプラットフォームの強みです。
全5回にわたる「The Royal Brew」構築日誌、いかがでしたでしょうか? LWRサイト開発、Custom LWC、そして最新のAgentforceまで。Salesforceの開発は、アイデア次第でここまで自由に、そして美しく作れるのです。
ぜひ皆さんも、独自の「世界観」を持ったサイト構築に挑戦してみてください!
参考URL
Invocable.Action クラス
カスタムエージェント呼び出し可能アクション: Apex クラスの例
ApexでAgentforceを呼び出す!呼び出し可能アクションを使ったカスタムウィジェット構築ガイド




読者の声