この記事はバージョン Spring ’26 において執筆しています。
現在の動作と異なる場合がありますので、ご認識おきください。
これまでのAIチャットボットは、テキストで答えを返すことが主な役割でした。しかし、Agentforce のエージェントは違います。彼らは自ら計画し、ツールを選び、行動(Act)します 。この「行動」において、私たちLWC開発者が果たすべき役割は極めて重要です。なぜなら、複雑な業務パラメータをAIに渡すための「入力フォーム」や、AIが検索した大量のデータを可視化する「結果リスト」は、テキストチャットだけでは表現しきれないからです 。
本記事では、AgentforceがApexを実行する際にLWCをUIとして利用する「Function Calling」の実装パターンを、最新の LightningTypeBundle 仕様に基づいて解説します 。
今回の記事で作成したコンポーネントの全ソースコードをGitHubで公開しています。 ぜひ Clone して、あなたの組織で動かしてみてください。
イントロダクション:生成AIから「Agentic AI」へ
Agentforceの核心は、推論エンジン「Atlas」にあります。Atlasはユーザーの曖昧な指示(例:「部品を注文したい」)を解釈し、それを実行するために必要な「ツール(ApexやFlow)」を自律的に選択します 。
しかし、ツールを実行するには正確な引数(パラメータ)が必要です。 「どの部品?」 「いつまでに必要?」 こうした情報をユーザーから収集(Slot Filling)する際、従来はテキストでのラリーが必要でしたが、AgentforceではInput LWCを提示して一気に入力を完了させることができます 。同様に、実行結果もテキストの羅列ではなく、画像付きのカードリストなどのOutput LWCとして返すことが可能です。
つまり、LWCは単なる画面コンポーネントから、AIと現実世界(データ)を繋ぐ「手足」としてのインターフェースへと進化したのです 。

Experience Cloud サイトで LightningTypeBundleを使用するには、拡張チャットをv2にする必要があります。v2のメリットやv1との違いなど詳細はこちらをご覧ください。
アーキテクチャ:LWCとAIをつなぐ「LightningTypeBundle」
この仕組みを実現するための「接着剤」となるのが、メタデータタイプ LightningTypeBundle です 。force-app/main/default/lightningTypes/ ディレクトリ配下に以下の3つのJSONファイルを配置して定義します 。
- schema.json (必須): Apex等のデータ構造を定義し、バリデーションルールを設定します 。
- editor.json (任意): エージェントがデータを「収集」する際に使用するLWCを指定します(Input Override) 。
- renderer.json (任意): エージェントがデータを「表示」する際に使用するLWCを指定します(Output Override) 。
このバンドルにより、同一のデータ型(Apexクラス)に対して、入力用と出力用で異なるUIを割り当てることが可能になります 。

処理の流れ
- Agentforceのアクション が実行され、Apexクラス の
@InvocableMethodが呼ばれる。 - Apexクラス が結果(カスタムクラスのインスタンスなど)を返す。
- エージェントは、そのデータ型に関連付けられた lightningTypes を参照する。
- lightningTypes の設定に従い、指定された LWC がチャット画面にレンダリングされる。
実装Step 1: データ契約(Apex DTO)の定義
まずは、AIとデータをやり取りするためのApexクラスを作成します。 ここでのポイントは、「InvocableMethodの仕様(リスト形式)」と「LWCで扱いたいデータ単位」を意識してDTOを設計することです。今回は、検索条件を PartSearchCriteria というクラスにまとめ、それを PartSearchRequest でラップする構成にします。
以下は、部品検索(Search Parts)を行うコントローラの例です。
force-app/main/default/classes/PartOrderController.cls
public with sharing class PartOrderController {
// 1. 入力DTOコンテナ: FlowやAgentからの呼び出し口
public class PartSearchRequest {
@InvocableVariable(label='Search Criteria' description='Container for search criteria.' required=true)
public PartSearchCriteria criteria;
}
// 2. 実体DTO: LWCで入力させる検索条件
// このクラスを Custom Lightning Type の対象とします
public class PartSearchCriteria {
@AuraEnabled
@InvocableVariable(label='Category' description='Part category (e.g., Engine, Body)')
public String category;
@AuraEnabled
@InvocableVariable(label='Keyword' description='Search keyword for part name')
public String keyword;
}
// 3. 出力DTO: 検索結果
public class PartInventoryResult {
@InvocableVariable(label='Part ID' description='Unique identifier')
public String partId;
@InvocableVariable(label='Part Name' description='Product name')
public String partName;
@InvocableVariable(label='Quantity' description='Stock count')
public Integer quantityAvailable;
@InvocableVariable(label='Price' description='Unit price')
public Decimal unitPrice;
@InvocableVariable(label='Image URL' description='Product image URL')
public String imageUrl;
}
// 4. アクション本体
@InvocableMethod(label='Search Parts' description='Searches for parts inventory')
public static List<List<PartInventoryResult>> searchParts(List<PartSearchRequest> requests) {
List<List<PartInventoryResult>> responses = new List<List<PartInventoryResult>>();
for (PartSearchRequest req : requests) {
List<PartInventoryResult> results = new List<PartInventoryResult>();
// Criteriaを取り出して検索ロジックを実行
String category = (req.criteria != null) ? req.criteria.category : null;
String keyword = (req.criteria != null) ? req.criteria.keyword : '';
// (検索ロジック: SOQLなどでデータを取得し results に add する)
// ...省略...
responses.add(results);
}
return responses;
}
}
実装Step 2: Input LWC の作成(データ収集)
ユーザーから検索条件を受け取るフォームです。 Agentforceは、value というプロパティを通じてデータオブジェクト全体(CategoryとKeyword)の受け渡しを行います。個別の @api 変数ではなく、value の getter/setter を実装するのがベストプラクティスです。
force-app/main/default/lwc/partSearchForm/partSearchForm.js-meta.xml
<targetConfig> を使用して、このLWCがどのLightning Typeに対応するかを明示するのがコツです。
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Part Search Input</masterLabel>
<targets>
<target>lightning__AgentforceInput</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AgentforceInput">
<targetType name="c__PartSearchInputType"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
force-app/main/default/lwc/partSearchForm/partSearchForm.js
import { LightningElement, api } from 'lwc';
export default class PartSearchForm extends LightningElement {
@api readOnly; // 完了後に読み取り専用にするためのフラグ
_category = '';
_keyword = '';
// Agentforceとのデータバインディング用プロパティ
@api
get value() {
return { category: this._category, keyword: this._keyword };
}
set value(val) {
if (val) {
this._category = val.category || '';
this._keyword = val.keyword || '';
}
}
handleChange(event) {
const { name, value } = event.target;
if (name === 'category') this._category = value;
if (name === 'keyword') this._keyword = value;
// 重要: 変更をAgentforceにリアルタイム通知
this.dispatchEvent(new CustomEvent('valuechange', {
detail: {
value: {
category: this._category,
keyword: this._keyword
}
}
}));
}
// (categoryOptions などのgetterは省略)
}
実装Step 3: Output LWC の作成(結果表示)
検索結果を表示するコンポーネントです。 Agentforceの仕様上、データはリストとして直接渡される場合と、ラッパーオブジェクトとして渡される場合があります。@api value で受け取り、型判定を行うロジックを入れることで、あらゆるケースに対応できます。
force-app/main/default/lwc/partInventoryList/partInventoryList.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Part Inventory Result</masterLabel>
<targets>
<target>lightning__AgentforceOutput</target>
</targets>
</LightningComponentBundle>
force-app/main/default/lwc/partInventoryList/partInventoryList.js
import { LightningElement, api } from 'lwc';
export default class PartInventoryList extends LightningElement {
// データ注入ポイント
@api value;
get items() {
// パターンA: 直接配列が渡される場合 (Collection Rendererの標準挙動)
if (Array.isArray(this.value)) {
return this.value;
}
// パターンB: Wrapperオブジェクト { output: [...] } として渡される場合
return this.value?.output || [];
}
get hasItems() {
return this.items && this.items.length > 0;
}
}
※HTML側 (partInventoryList.html) は、上記の items ゲッターを template for:each で回して表示します。
実装Step 4: メタデータの紐付けとデプロイ
最後に、ApexとLWCをつなぐメタデータを配置します。
ここでの最大の注意点は 「クラス名に c__ プレフィックス(名前空間)を付けること」 です。これを忘れるとAgent Builderで認識されません。
ディレクトリ構造
force-app/main/default/lightningTypes/
├── PartSearchInputType/ <-- 入力用バンドル
│ ├── schema.json
│ └── lightningDesktopGenAi/
│ └── editor.json
└── PartInventoryOutputType/ <-- 出力用バンドル
├── schema.json
└── lightningDesktopGenAi/
└── renderer.json
入力用定義 (PartSearchInputType)
schema.json
@apexClassType を使用してApexクラスを参照します。「$」は内部クラスを示します 。
※ ApexDTOの Criteria クラスを指定している点に注目してください。
{
"title": "Part Search Input Schema",
"description": "Schema for Part Search Criteria",
"lightning:type": "@apexClassType/c__PartOrderController$PartSearchCriteria"
}
editor.json
ルートレベル(”$”)をカスタムLWCでオーバーライドします 。
{
"editor": {
"componentOverrides": {
"$": { "definition": "c/partSearchForm" }
}
}
}
出力用定義 (PartInventoryOutputType)
schema.json
{
"title": "Part Inventory Output Schema",
"description": "Schema for Part Inventory Result",
"lightning:type": "@apexClassType/c__PartOrderController$PartInventoryResult"
}
renderer.json
リスト表示を行うため、collection キーを使用します。
{
"collection": {
"renderer": {
"componentOverrides": {
"$": { "definition": "c/partInventoryList" }
}
}
}
}
Spring ’26からは、「Lightning Types MCP Tool」が提供され、LLMを使用してこれらのメタデータファイルを自動生成できるようになりました。手書きが大変な場合は活用を検討してください 。
エージェントに設定する
最後に、作成したApexをエージェントのアクションとして指定します。

今回は「Agentforce (Default)」に問いかけると社内エージェントが在庫状況を回答するように設定しました。
トピックを作成
以下のとおりトピックを作成しています。ご参考にどうぞ。
分類の説明:
あなたは熟練した在庫管理アシスタントです。 ユーザーが特定の自動車部品を探している場合、在庫があるか確認したい場合、あるいは部品の価格や詳細を知りたい場合に使用します。「部品が欲しい」「在庫はあるか」といった意図が含まれるクエリに対して選択されます。
範囲:
自動車部品の在庫検索、製品情報の確認、価格の照会、および部品注文プロセスの開始。
指示:
- あなたは熟練した自動車部品在庫管理アシスタントです。あなたの主な役割は、ユーザーが探している部品を正確に見つけることです。
- ユーザーから部品に関する問い合わせ(在庫確認、価格、注文など)があった場合は、会話で詳細を聞き出そうとせず、直ちに「Search Parts」アクションを呼び出してください。
- 【重要】 カテゴリやキーワードが不明な場合でも、チャットでユーザーに質問しないでください。Input LWC(入力フォーム)を表示させることで、ユーザー自身に入力してもらうほうが効率的だからです。
- アクションの結果はOutput LWC(リスト画面)としてユーザーに表示されます。結果の内容(商品名や価格など)をテキストで復唱したり要約したりしないでください。
- 代わりに、「検索結果は画面の通りです。他にお手伝いすることはありますか?」や「ご希望の部品は見つかりましたか?」と簡潔に声をかけてください。
トピックアクションを作成
続いて、トピックにアクションを追加します。
- 作成したトピックを選択します。
- 「このトピックのアクション」タブを選択し、[新規] > [新規アクションを作成] をクリック。
- アクション種別として「Apex」を選択し、「呼び出し可能なメソッド」を選択します。
- 参照アクションとしてあらかじめ作成したApexメソッドを選択します。
- [次へ] を押します。

- 入力
- 「ユーザーからデータを収集」にチェックを入れます。
- 「入力 表示中」の選択で
PartSearchInputTypeを選択します。
- 出力
- 「指示」に以下を入力します。
Display the search results using the provided list component. Do not iterate through the list or summarize the item details (name, price, stock) in the text response. Simply respond with a brief confirmation like “Here are the parts I found.” or ask if they would like to proceed with an order based on the results shown. - 「会話に表示」にチェックを入れます。
- 「出力 表示中」の選択で
PartInventoryOutputTypeを選択します。
- 「指示」に以下を入力します。

結果を確認(プレビュー)
これまでのソースコードと設定が組み合わさり、エージェントが入力と出力でLWCコンポーネントを表示するようになりました!

高度なトピックとベストプラクティス
実装にあたり、以下の制約とベストプラクティスに注意してください。
ペイロード制限とパフォーマンス
Agentforce Service Agentのアクション実行において、LWCに渡されるデータサイズには約4MBの制限があります 。
- 画像データはBase64ではなくURLとして渡す。
- 大量のレコードを返す場合はページネーションを検討する。
2GPパッケージ開発の注意点
管理パッケージ(2GP)開発において、schema.json で内部クラス(例: ns__OuterClass$InnerClass)を参照すると、インストール時に検証エラーが発生するリスクがあります 。
推奨: DTOクラスは、内部クラスではなく独立したトップレベルのApexクラスとして定義することが最も安全です(例: @apexClassType/ns__MyDtoClass) 。
チャット特有の「ステート管理」に注意する
チャットインターフェース特有の課題として、「コンポーネントの再利用と破棄」があります。ユーザーがチャット履歴をスクロールして画面外に移動した際、LWCのインスタンスはパフォーマンス最適化のために破棄され、再び表示される際に再生成される場合があります。
- 課題: 入力フォームに値を入力している途中でスクロールアウトすると、戻ったときに入力内容が消えているリスクがある。
- 対策: 内部変数(
this.value)だけで状態を持たず、valuechangeイベントを通じてこまめにAgentforce側(親)へ値を預ける設計にしてください。- 親コンポーネントが状態を保持していれば、LWCが再生成された際に
@apiプロパティを通じて値が再注入され、状態が復元されます。
- 親コンポーネントが状態を保持していれば、LWCが再生成された際に
クライアントサイドのセキュリティ (Einstein Trust Layer)
Agentforceの推論自体は「Einstein Trust Layer」によって守られていますが、LWCはユーザーのブラウザ(クライアントサイド)で実行されます。以下の点に留意してください。
- 機密情報の隠蔽: ブラウザのDevToolsを使えばJavaScriptコードは誰でも閲覧可能です。APIキーや機密ロジックをJS内にハードコードせず、必ずApexを経由してください。
- DOMアクセスの制限:
renderer.jsonを通じて注入されるデータは信頼できるApexからのものですが、XSS(クロスサイトスクリプティング)対策として、innerHTMLへの直接代入は避け、lightning-formatted-textなどの標準コンポーネントを使用してください。
トラブルシューティングガイド
開発プロセスで遭遇する可能性が高いエラーとその対処法を付記します。
| エラーメッセージ / 現象 | 推定原因 | 対処法 |
|---|---|---|
| “Unsupported Data Type” in Builder | Agent Action設定画面でCustom Lightning Typeを選択した際に表示される警告。 | @apexClassTypeを使用している場合、BuilderのUIがプレビューを生成できずにこの警告を出すことがあるが、機能的には問題ないため無視して保存が可能。 |
| LWCが表示されず標準のJSONダンプが表示される | targetsの設定漏れ、またはrenderer.jsonのマッピングミス。 | js-meta.xmlにlightning__AgentforceOutputが含まれているか確認する。また、renderer.jsonのdefinitionパスが正しいか(c/プレフィックスなど)を確認する。 |
| Deployment Error: “Invalid type reference” | schema.json内のApexクラス参照エラー。 | Apexクラス名、名前空間、$による内部クラス参照の構文を再確認する。特に2GP環境ではトップレベルクラスへの移行を検討する。 |
| Input LWCの値がエージェントに渡らない | イベント発火の実装ミス。 | valuechangeイベントが正しく発火されているか、detailオブジェクトの構造がschema.jsonの定義と一致しているか、ブラウザのコンソールログで確認する。 |
まとめ
LWCはAgentforceにおいて、単なる情報表示画面ではなく、AIの思考と現実の業務をつなぐ重要なインターフェースとなりました 。 今回解説した LightningTypeBundle を使いこなすことで、テキストチャットの枠を超えた「GUIを持つAIエージェント」を構築し、ユーザー体験を劇的に向上させることができます。
ぜひ、Spring ’26環境で実際に手を動かして試してみてください。
参考URL
Use Lightning Type Overrides in Enhanced Chat v2
Enhance the Agent UI with Custom LWCs and Lightning Types
Custom Lightning Types – Agentforce Developer Guide
Example: Customizing User Interface Using Custom Lightning Types with Editor and Renderer Overrides – Agentforce Developer Guide
Renderer.json for Custom Lightning Types – Agentforce Developer Guide
The Salesforce Developer’s Guide to the Spring ’26 Release – Salesforce Developers Blog
Custom Lightning Types Overview – Salesforce Help
9 Features for Salesforce Developers in the Spring ’26 Release – SF BEN
How to use Custom Lightning Types in Agentforce Service Agents? – Salesforce Diaries







読者の声