ClassLab Engineering の Dev チームメンバーが執筆しました。
*この記事は、業務システムのUX改善・Salesforce LWC開発・データ駆動型設計に興味があるフロントエンド/フルスタックエンジニア向けです。*
1. 背景
ClassLab のコールセンターでは、電気・ガス・水道・引越し・不要品回収など、複数のサービスを電話1本で案内しています。顧客の状況(引越し先の地域、現在の契約状況、希望サービスの組み合わせ)によって案内内容が大きく変わるため、オペレーターには幅広い知識と判断力が求められます。
問題は、新人オペレーターとベテランの品質差でした。ベテランは経験から最適な案内パスを瞬時に判断できますが、新人は「次に何を聞くべきか」「どの条件でどのサービスを案内すべきか」の判断に時間がかかり、案内漏れや誤案内が発生していました。
この課題を解決するため、通話中にリアルタイムで最適な案内パスを提示するトークスクリプト動的生成システムを構築しました。
2. 課題
| 課題 | 影響 | 定量データ |
|---|---|---|
| 案内パスの複雑さ | 顧客の回答によって分岐が指数的に増加 | サービス組み合わせ数十パターン |
| 新人の案内品質 | 案内漏れ・誤案内による顧客不満 | 新人のNPS平均がベテランより低い |
| マニュアル更新の遅延 | サービス追加・条件変更のたびに手動更新 | 更新に数日〜1週間 |
| 二重入力の手間 | 通話中のメモを後からSalesforceに転記 | 後処理(ACW)時間の増加 |
特に深刻だったのはマニュアル更新の遅延です。新サービスが追加されるたびに紙のフローチャートとExcelを更新する作業が発生し、更新漏れが案内ミスに直結していました。
3. 設計
システム構成
graph TB
subgraph "Salesforce"
LWC[LWC トークスクリプト画面<br/>オペレーター操作UI]
APEX[Apex コントローラー<br/>スクリプトデータ取得]
OBJ[カスタムオブジェクト<br/>スクリプト定義・顧客データ]
end
subgraph "スクリプトエンジン"
ENGINE[条件分岐エンジン<br/>JavaScript]
EVAL[条件評価器<br/>顧客回答×ルール]
RENDER[動的レンダラー<br/>次のステップを生成]
end
LWC --> APEX
APEX --> OBJ
LWC --> ENGINE
ENGINE --> EVAL
EVAL --> RENDER
RENDER --> LWC
設計上の最大の判断: サーバーサイド vs クライアントサイド
条件分岐の評価をどこで行うかが最大の設計判断でした。
| 方式 | メリット | デメリット |
|---|---|---|
| サーバーサイド(Apex) | ロジック一元管理、セキュリティ高 | 通話中のレイテンシ、API制限 |
| クライアントサイド(LWC内JS) | 即座にレスポンス、オフライン対応 | ロジック分散、更新の反映 |
クライアントサイド(LWC内JavaScript)を選択しました。通話中のオペレーターにとって「ボタンを押してから次のステップが表示されるまでの時間」は0.5秒でも長いと感じます。サーバーサイドではApexのコールアウト遅延が避けられないため、条件分岐ルールをセッション開始時に一括取得し、以後の分岐評価はクライアントで完結させています。
スクリプト定義のデータモデル
スクリプトの条件分岐をコードに埋め込まず、Salesforceのカスタムオブジェクトでデータ駆動型にしています。
// スクリプトステップの定義(Salesforceオブジェクトから取得)
const step = {
id: "step_electricity_area",
type: "question", // question / info / action
text: "お引越し先のエリアはどちらですか?",
options: [
{
label: "Aエリア(関東)",
value: "area_a",
nextStepId: "step_area_a_plan",
conditions: [], // 無条件で表示
},
{
label: "Bエリア(関西)",
value: "area_b",
nextStepId: "step_area_b_plan",
conditions: [],
},
{
label: "Cエリア(都市ガス)(電気+ガスセット)",
value: "area_c_set",
nextStepId: "step_area_c_set",
conditions: [
{ field: "gasRequired", operator: "eq", value: true }
],
},
],
};
SVやマネージャーがSalesforce上でスクリプトの条件分岐を編集できるため、エンジニアの手を介さずにサービス追加・条件変更が可能です。
4. 実装
条件評価エンジン
顧客の回答履歴と条件ルールを照合し、表示すべき選択肢をフィルタリングします。
// LWCテンプレートの自動エスケープにより、スクリプトインジェクションは防止
const ALLOWED_FIELDS = new Set(["area", "gasRequired", "planType", "ampere"]);
function evaluateConditions(conditions, customerData) {
return conditions.every(condition => {
if (!ALLOWED_FIELDS.has(condition.field)) return false;
const actual = customerData[condition.field];
switch (condition.operator) {
case "eq": return actual === condition.value;
case "neq": return actual !== condition.value;
case "in": return condition.value.includes(actual);
case "gt": return actual > condition.value;
default: return false;
}
});
}
function getVisibleOptions(step, customerData) {
return step.options.filter(option =>
option.conditions.length === 0 ||
evaluateConditions(option.conditions, customerData)
);
}
通話中のリアルタイム入力同期
オペレーターがスクリプトの選択肢を選ぶと、その回答が即座にSalesforceのレコードに反映されます。通話終了後の転記作業が不要になります。
async function onOptionSelected(option, stepId) {
// ローカル状態を即座に更新(UIの即時レスポンス)
customerData[stepId] = option.value;
// 次のステップを計算
const nextStep = scriptMap.get(option.nextStepId);
const visibleOptions = getVisibleOptions(
nextStep, customerData
);
renderStep(nextStep, visibleOptions);
// Salesforceへの非同期保存(UIをブロックしない)
// retryQueueはlocalStorageに永続化し、LWCアンロード時もデータ喪失しない
saveToSalesforce(stepId, option.value).catch(err => {
console.error("SF save failed, will retry:", err);
retryQueue.push({ stepId, value: option.value });
localStorage.setItem("retryQueue", JSON.stringify(retryQueue));
});
}
ポイント: Salesforceへの保存は非同期で行い、UIをブロックしません。保存失敗時はリトライキューに積み、通話終了後にバックグラウンドで再試行します。
マニュアル更新ゼロの実現
スクリプト定義がSalesforceのカスタムオブジェクトに格納されているため、新サービスの追加は以下のフローで完結します。
1. SVがSalesforceで新しいスクリプトステップを作成
2. 条件分岐ルールを設定
3. 次回セッション開始時に自動反映
エンジニアによるコード変更もデプロイも不要です。
5. 結果(数値)
| 指標 | Before | After | 改善率 |
|---|---|---|---|
| 新人の案内品質スコア(※) | ベテラン比 -30% | ベテラン比 -5%以内 | 大幅改善 |
| 案内漏れ率 | 数%(新人) | 0.5%未満 | -90%超 |
| マニュアル更新リードタイム | 数日〜1週間 | 即時(SV操作) | -99% |
| 通話後処理(ACW) | 転記作業あり | 転記不要 | 大幅短縮 |
| 新サービス追加のコード変更 | 必須 | 不要 | ゼロ |
※ 品質スコア: 案内完了率・案内漏れ件数・顧客フィードバックの3指標を10点満点で内部評価。
最大の効果は新人とベテランの品質差の縮小です。システムが最適な案内パスを提示するため、新人でもベテランと同等の案内品質を実現できるようになりました。
6. 展望
次に取り組む課題
- AIによる案内順序の最適化: 蓄積された通話データから「どのサービスから案内すると成約率が高いか」をAIで予測。ルールベースからML駆動への転換を、次のエンジニアと一緒に設計したい領域です
- AI研修システムとの連携: AI音声ロールプレイ研修システムと本番トークスクリプトを統合し、研修と実務のギャップをゼロに近づける計画です
- LWCで動的トークスクリプトを実現した設計 — 本システムの技術設計の詳細
- AI音声ロールプレイ研修を内製した話 — 研修側との連携
- Findyでカジュアル面談する
- 採用情報を見る
関連記事
—
採用情報
ClassLab では、業務システムのUXを本気で改善したいフロントエンド/フルスタックエンジニアを募集しています。Salesforce LWC開発やデータ駆動型UIの経験を活かせるポジションです。
—
ClassLab Engineering チームメンバーが執筆しました。
>
ClassLab.では、一緒にプロダクトを作るエンジニアを募集しています。
カジュアル面談も大歓迎です!
>