📚

Gemini APIを利用したChrome拡張機能でSalesforceの商談入力を自動化してみた

に公開

はじめに

こんにちは、Psyです。
普段はIT企業でSalesforceのアドミニをしています。

Salesforceを運用していると、「入力に時間がかかる」という声は日常的に聞こえてきます。
組織ごとにカスタム要望が異なるため項目数はどんどん増えますし、1日の顧客接触件数が多い営業担当者にとっては、入力作業そのものがなかなかの負担です。

根本的にはライセンスの見直しやAIツールの連携で解決できそうではあるものの、システムの規模感や決裁プロセスを考えると、すぐに動かせるものでもありません。
そんな中ふと、「Chrome拡張機能で入力を楽にできないだろうか?」と思い立ちました。

セキュリティや保守性の問題など、乗り越えないといけない壁は多そうですが、まずは個人のハンズオン環境で試してみようということで、Gemini APIと組み合わせたChrome拡張機能を作ってみました。
本記事では、その実装内容と、Chrome拡張開発未経験の状態からClaude Codeを使ってどこまでできたのかを紹介します。

課題・背景

改めて、今回の取り組みの背景を簡単に整理します。

  • 項目数の多さ:
    営業組織からのカスタム要望に対応するうちに、商談レコードの入力項目が膨大になっている
  • 1日の接触件数:
    営業担当者は多い日で数十件の顧客接触があり、そのたびにSalesforceへの入力が発生する
  • 入力フォームの複雑さ:
    テキスト、数値、日付、選択リストなど項目タイプが多岐にわたり、手入力の工数が大きい

これらの課題を手軽に解消するためのアプローチとして、Chrome拡張機能で商談の文字起こしデータからフォームへの自動入力ができないか、個人のハンズオン環境で検証してみることにしました。

作ったもの

まずは実際に作成したものを紹介します。

入力画面の起動

今回は、ハンズオン環境上で事前に取引先レコードと編集に使用する商談レコードを作成しておきました。

この商談レコードの編集を開き、Chrome拡張機能を起動します。

入力テキスト(サンプル)

商談メモを入力します。
今回は以下のようなサンプルを入力しました。

【商談記録】ABC株式会社様 - SFAシステム導入検討
日時:2026年2月3日 14:00-15:30
参加者:
- 先方:山田部長(情報システム部)、佐藤課長(営業企画部)
- 当社:田中(営業担当)、鈴木(SE)

---
田中:本日はお忙しい中お時間いただきありがとうございます。先日のウェビナーにご参加いただいた件で、SFAシステムの詳細をご説明させていただければと思います。

山田部長:こちらこそよろしくお願いします。実は当社、今使っている営業管理がExcelベースで限界を感じていまして。営業チームが50名を超えてきたので、そろそろ本格的なシステムを入れたいと考えています。

佐藤課長:具体的には、案件の進捗管理と売上予測の精度を上げたいんです。今は月末にならないと数字が見えなくて。

田中:なるほど。リアルタイムでの可視化ですね。弊社のソリューションですと、ダッシュボードで即座に案件状況が確認できます。鈴木の方からデモをお見せしましょうか。

(デモ実施 - 約30分)

山田部長:これはいいですね。特にモバイル対応しているのがありがたい。外回りの営業が多いので。

佐藤課長:導入コストはどれくらいになりますか?

田中:御社の規模ですと、初期導入費用が350万円、月額ライセンスが1ユーザーあたり5,000円となります。50名でご利用の場合、月額25万円です。初年度は合計で650万円程度とお考えください。

山田部長:競合他社と比較検討中なのですが、○○社のサービスとの違いは何でしょうか?

鈴木:○○社さんはグローバル展開が強みですが、弊社は国内企業様向けのカスタマイズ性と、日本語サポートの手厚さが特徴です。また、導入後3ヶ月間の伴走支援プログラムが含まれています。

佐藤課長:いつ頃から利用開始できますか?

田中:ご契約から約1ヶ月半で本稼働可能です。3月末決算に間に合わせるのであれば、2月中旬までにご発注いただければ対応できます。

山田部長:わかりました。社内で検討して、来週中には回答したいと思います。一度見積書を正式にいただけますか。

田中:承知しました。本日中にお送りいたします。次回は決裁者の方にもご同席いただければ、最終提案をさせていただければと思います。

山田部長:では次のステップとして、弊社の取締役会議にかけて承認を取りたいので、2月10日に役員向けのプレゼンをお願いできますか?

田中:もちろんです。2月10日、承知しました。

構造化の実行と確認画面での編集

「構造化を実行」ボタンを押すと、入力テキストがGemini APIに渡され、構造化を行います。
確認画面では、この結果が編集可能なフォームとして表示されます。
確認が必要な項目には「要確認」バッジが付くので、ユーザーはそこを重点的にチェックできます。

フォームへの入力結果

「フォームに入力」を押すと、Content ScriptがSalesforceの商談編集画面のフォームに値を自動入力します。入力後も保存はされていない状態なので、ユーザーが内容を最終確認してから保存ボタンを押します。

中身について

全体の流れ

改めて、今回作成したのは、商談の録音文字起こしテキストを貼り付けると、Salesforceの商談オブジェクトに自動入力してくれるChrome拡張機能です。

処理の流れは以下のとおりです。

1. ユーザーがテキストを入力/アップロード

2. Popup → Background: テキスト送信

3. Background → Gemini API: 構造化リクエスト

4. Gemini API → Background: 構造化結果(JSON)

5. Background → Popup: 結果表示

6. ユーザーが確認・編集

7. Popup → Content Script: 入力指示

8. Content Script: Salesforceフォームに値を入力

ポイントは、LLMの構造化結果をそのままフォームに入力するのではなく、ユーザーが確認・編集できるステップを挟んでいることです。
あくまで入力の「下書き」を作る位置づけなので、保存ボタンも自動ではクリックしません。最終判断はユーザーに委ねる設計にしています。

アーキテクチャ

拡張機能は3つのコンポーネントで構成されています。

┌─────────────────────────────────────────────────────────────┐
│                    Chrome Extension                          │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐                    ┌──────────────┐      │
│  │   Popup UI   │                    │   Content    │      │
│  │  (入力/確認) │                    │   Script     │      │
│  └──────┬───────┘                    └──────┬───────┘      │
│         │                                     │              │
│         ▼                                     ▼              │
│  ┌──────────────────────────────────────────────────┐      │
│  │              Background Service Worker            │      │
│  │  - Gemini API連携                                 │      │
│  │  - メッセージルーティング                          │      │
│  └──────────────────────────────────────────────────┘      │
└─────────────────────────────────────────────────────────────┘


                  ┌─────────────────┐
                  │   Gemini API    │
                  └─────────────────┘
コンポーネント 役割
Popup UI テキスト入力と構造化結果の確認・編集を行うUI
Background Service Worker Gemini APIとの通信やコンポーネント間のメッセージルーティング
Content Script Salesforceページ上でのDOM操作とフォーム入力

加えて、項目定義(CSSセレクタや型情報)と選択リスト値をJSON設定ファイルとして外部管理することにしました。
項目の追加・変更が頻繁に発生した場合に、設定ファイルの変更だけで対応できるようにするためです。

対応している項目

今回のハンズオン環境で作成したデモでは、以下の商談項目に対応しました。

項目名 データ型 備考
金額 / 完了予定日 / 確度(%) 数値・日付 そのまま入力
次のステップ / 説明 テキスト 説明は文字起こしの要約を自動生成
種別 / フェーズ / リードソース / Delivery Status 選択リスト 事前定義された選択肢から判定
Order Number / Tracking Number 数値 カスタム項目
Main Competitor(s) / Current Generator(s) テキスト カスタム項目
主キャンペーンソース 参照 候補提示のみ(自動入力は非対応)

参照関係の「主キャンペーンソース」だけは自動入力が難しいため、LLMが候補名を提示し、ユーザーがSalesforce上で検索・紐づけする運用としています。

開発の進め方

Chrome拡張は初めてだったので、Claude Codeに頼った

Chrome拡張機能の開発は今回が初めてだったので、要件定義から実装までClaude Codeを活用して進めることにしました。

参考にしたのは、マイナビエンジニアブログのChrome拡張機能作成記事と、そこで紹介されているChrome-Extension-starter-v3テンプレートです。

要件定義書の作成

まずは簡単な要件定義書を作成しました。
Salesforceの商談オブジェクトにどんな項目があるか、選択リスト値は何か、画面遷移はどうするか、といった情報をdocs/PRD.mdに整理しています。

これが結果的にうまくいきました。選択リスト値やフィールドマッピングのような定義的な情報を事前に明示しておくと、その後の実装フェーズでClaude Codeが一貫した方針でコードを生成してくれます。要件定義書がそのままプロンプトのコンテキストとして機能するイメージです。

コード生成と試行錯誤

Chrome-Extension-starter-v3をベースに、Claude Codeでコンポーネントごとにコードを生成・調整していきました。
Popup UIやGemini API連携の部分は比較的スムーズに進みましたが、Salesforce Lightning ExperienceのDOM操作はかなり試行錯誤が必要でした。

実装のポイント

ここでは、技術的に面白かった部分や工夫が必要だった部分に絞って紹介します。ディレクトリ構成は以下のとおりです。

salesforce_chrome_extension/
├── manifest.json           # 拡張機能マニフェスト(Manifest V3)
├── service-worker.js       # バックグラウンドスクリプト(Gemini API連携)
├── foreground.js           # コンテンツスクリプト(フォーム入力)
├── popup/
│   ├── index.html          # ポップアップUI
│   ├── style.css           # スタイル
│   └── popup.js            # ポップアップロジック
├── config/
│   ├── fields.json         # 項目定義・セレクタ設定
│   └── picklists.json      # 選択リスト値
├── logo/
│   └── icon.svg            # アイコン
└── docs/
    └── PRD.md              # 要件定義書

Gemini APIでのテキスト構造化

プロンプト設計の工夫

service-worker.jsで商談の文字起こしテキストをGemini APIに投げて構造化しています。ここでのポイントは2つあります。

1つ目は、選択リスト値をプロンプトで明示することです。
「種別は${PICKLIST_VALUES.types.join(', ')} から選択してね」とLLMに明示することで、Salesforceに存在しない値が返ってくるリスクを減らしています。

function generateSystemPrompt() {
  return `あなたは営業商談の文字起こしデータを分析し、
Salesforceの商談レコードに入力する情報を抽出するアシスタントです。

以下の項目を抽出してJSON形式で返してください:
- amount: 金額(数値、不明な場合はnull)
- closeDate: 完了予定日(YYYY-MM-DD形式、不明な場合はnull)
- type: 種別(選択肢: ${PICKLIST_VALUES.types.join(', ')} から選択)
- stage: フェーズ(選択肢: ${PICKLIST_VALUES.stages.join(', ')} から選択)
...(以下、他の項目も同様)

重要な注意事項:
1. JSONのみを返し、説明文やマークダウン記法は不要です
2. 選択リスト項目は必ず上記の選択肢の中から選んでください
3. 明確に判断できない項目はnullにしてください
4. uncertainFieldsには、推測で埋めた項目名を含めてください`;
}

2つ目は、uncertainFieldsという項目を設けたことです。
LLMが「これは自信がないけど推測で埋めた」という項目を自己申告させることで、確認画面で「要確認」バッジを表示できるようにしています。
実用に当たっては専門用語を適切に認識できなかったり、現場の暗黙知を拾いきれないケースが想定されるので、要確認フラグというバッファを持たせておくことにしました。

プロンプト + コードの二重チェック

とはいえ、プロンプトで「この選択肢から選んで」と指示しても、LLMが必ずしも従うとは限りません。そこでコード側でも、返ってきた値が事前定義された選択肢に含まれているかをチェックし、数値項目には型チェックと範囲制限を適用するようにしています。念のための二重チェックです。

Salesforceフォームへの自動入力

foreground.jsのContent Scriptが担う部分で、ここが一番苦労しました。

ハイブリッド項目検出

Salesforceのフォーム上で「金額」の入力欄はどこにあるのか?を特定するために、2段階の検出方式を採用しています。

// 1. 設定ファイルのCSSセレクタを優先使用
if (fieldsConfig?.fields?.[fieldKey]) {
  element = findFieldByConfig(fieldsConfig.fields[fieldKey]);
}

// 2. 見つからなければ、ラベルテキストから動的に検出
if (!element) {
  element = findFieldByLabel(fieldKey);
}

まずはconfig/fields.jsonに定義したCSSセレクタで探し、それで見つからなければ画面上のラベルテキスト(「金額」「Close Date」など)をたどって入力欄を探す仕組みです。Salesforceはライセンス等によってDOM構造が微妙に異なることがあるので、この二段構えが効いてきます。

Lightning Experience対応が最大のハマりポイント

Salesforce Lightning ExperienceはLWC(Lightning Web Components)ベースで構築されています。これが何を意味するかというと、単純にelement.value = '新しい値'と書いても、フレームワーク側が変更を認識してくれません。

この問題を解決するために、ネイティブなプロパティセッターを使って値を設定し、その上でinput/change/blurイベントを順番に発火させる必要がありました。

async function fillTextField(element, value) {
  element.focus();
  element.value = '';

  // React/LWCのプロパティ管理をバイパスして値を設定
  const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
    window.HTMLInputElement.prototype,
    'value'
  )?.set;

  if (nativeInputValueSetter) {
    nativeInputValueSetter.call(element, String(value));
  }

  // input, change, blurイベントを順番に発火
  dispatchEvents(element);
  return true;
}

Object.getOwnPropertyDescriptorでHTMLInputElementの本来のvalueセッターを取り出し、それを直接呼ぶことでLWCのラッパーを迂回しています。これはReactアプリケーションに対するテスト自動化などでも使われるテクニックですが、Salesforceの文脈ではあまりドキュメントがなく、Claude Codeの知識に助けられた部分です。

選択リスト(Combobox)も一筋縄ではいかない

Lightning Experienceの選択リストは、通常の<select>タグではなくlightning-comboboxというカスタムコンポーネントで実装されています。そのため、「ドロップダウンボタンをクリック → 300ms待機 → 表示されたオプション一覧から該当する値を探してクリック」という手順をプログラム的に再現する必要がありました。

ポップアップUIの画面遷移

ポップアップUIは4つの画面状態を持ち、以下のように遷移します。

[入力画面] → [処理中] → [確認・編集画面] → [入力完了]
     ↑                        |
     └────────────────────────┘
              (キャンセル/再入力)

入力画面ではテキストの直接貼り付けに加え、.txt.mdファイルのアップロードにも対応しています。確認・編集画面では、LLMが構造化した結果を一覧表示し、選択リスト項目はドロップダウン、テキスト項目はテキストフィールドとして表示されます。LLMが推測で埋めた項目には「要確認」バッジが付くので、ユーザーはそこを重点的にチェックすれば済むようになっています。

課題と今後の展望

今回はあくまで個人のハンズオン環境での検証なので、実運用に向けてはいくつかの課題があります。

  • データセキュリティとガバナンス:
    • 今回はGemini APIを利用したが、実運用では送信したデータがモデルの学習に利用されない工夫が必要
    • APIキーの管理がハードコードに近い状態なので、よりセキュアな管理への移行が必要
  • メンテナンス性:
    • 現在のDOMを直接操作して値を注入する方式は、SalesforceのUIアップデートによって突然動作しなくなるリスクがある
    • nativeInputValueSetter でフレームワークをバイパスする手法は、標準機能の挙動を妨げる可能性があり、長期的な保守性の観点では課題となる
  • カスタムオブジェクト等への対応:
    現状は商談オブジェクト専用だが、リードやケース、またカスタムオブジェクトへの横展開には、さらに柔軟なフィールドマッピングの設定が必要

今回のChrome拡張機能は「既存環境に手を入れずにブラウザ側で解決する」という点では非常に強力です。
一方で、実運用に向けてはよりプラットフォームの標準に則った堅牢な仕組みへの昇華が必要であると感じました。

まとめ

本記事では、Chrome拡張機能とGemini APIを組み合わせて、Salesforceの商談フォームへの入力を自動化する仕組みを紹介しました。

実際に利用するには様々な障壁がありますが、「こんなことできないかな?」をClaude Codeで要件定義書として整理してからコード生成に進むことで、サクッと一貫性のある実装ができたことは良い学びになりました。

最後までお読みいただき、ありがとうございました!

参考資料

Discussion