T-CREATOR

NotebookLM 活用事例:営業提案書の下書きと顧客要件の整理を自動化

NotebookLM 活用事例:営業提案書の下書きと顧客要件の整理を自動化

営業活動において、提案書の作成や顧客要件の整理は非常に重要なプロセスです。しかし、これらの業務には多くの時間と労力がかかり、特に新人営業担当者にとっては品質のばらつきも課題となっています。

Google Labs が提供する NotebookLM は、RAG(Retrieval-Augmented Generation)技術を活用した革新的なツールで、営業提案業務の効率化に大きく貢献します。本記事では、NotebookLM を使って営業提案書の下書き作成と顧客要件の整理を自動化する具体的な方法を、技術的な観点から詳しく解説します。

背景

営業提案業務の現状と課題

営業担当者は日々、顧客との商談や提案書の作成に追われています。特に提案書作成では、顧客のニーズを正確に把握し、自社の製品やサービスがどのように課題を解決できるかを論理的に説明する必要があります。

しかし、この過程では多くの情報を扱わなければなりません。顧客へのヒアリング内容、会議の議事録、過去の成功事例、製品仕様書など、複数の資料を横断的に参照しながら提案書を組み立てる作業は、経験豊富な営業担当者でも数時間から数日を要することがあります。

RAG 技術と NotebookLM の位置づけ

RAG(Retrieval-Augmented Generation)は、大規模言語モデル(LLM)と情報検索技術を組み合わせた手法です。従来の LLM は学習データに基づいて回答を生成しますが、RAG では特定の資料を参照元として指定し、その内容に基づいた回答を生成できます。

NotebookLM はこの RAG 技術をベースに構築されており、ユーザーがアップロードした資料のみを「唯一の正解」として扱います。そのため、Web 上の不確実な情報に影響されることなく、業務に特化した高精度な情報整理や文書生成が可能となっています。

2025 年 11 月時点で、NotebookLM は無料版でも最大 100 個のノートブック、1 ノートブックあたり最大 50 個のソース、1 ソースあたり最大 500,000 語まで対応しており、営業活動で扱う資料の大部分をカバーできます。

以下の図は、従来の営業提案プロセスがどのように進行するかを示しています。

mermaidflowchart TB
    meeting["顧客との商談<br/>ヒアリング実施"] --> note["議事録作成<br/>手書きメモ整理"]
    note --> search["過去資料の検索<br/>類似案件の調査"]
    search --> analyze["要件の分析<br/>課題の整理"]
    analyze --> draft["提案書の下書き<br/>パワポ作成"]
    draft --> review["上司レビュー<br/>修正作業"]
    review --> final["最終版完成"]

    style meeting fill:#e1f5ff
    style note fill:#fff4e1
    style search fill:#fff4e1
    style analyze fill:#fff4e1
    style draft fill:#fff4e1
    style review fill:#ffe1e1
    style final fill:#e1ffe1

この図からわかるように、営業担当者は商談後に複数の手作業工程を経て提案書を完成させます。特に「過去資料の検索」「要件の分析」「下書き作成」のステップに多くの時間を費やしており、これらを効率化することが業務改善の鍵となります。

NotebookLM の基本機能

NotebookLM には営業提案業務に活用できる以下の主要機能があります。

#機能名説明
1ソース管理PDF、Google ドキュメント、Web ページなど複数形式の資料を一元管理
2自動要約アップロードした資料の要点を自動抽出
3マインドマップ情報の関係性を可視化し構造化
4カスタムプロンプト独自の指示を設定して特定の出力形式を指定
5テンプレート機能ブリーフィング、タイムラインなど定型フォーマットで出力

これらの機能を組み合わせることで、営業提案書の作成プロセスを大幅に効率化できるのです。

課題

手作業による提案書作成の問題点

従来の提案書作成プロセスには、いくつかの深刻な問題があります。

まず、情報の散逸です。顧客とのヒアリング内容は営業担当者の手書きメモやバラバラなデジタルファイルに記録され、会議の議事録は Google ドキュメントやメール、製品情報は社内の共有ドライブなど、複数の場所に分散して保存されています。提案書を作成する際、営業担当者はこれらの資料を一つひとつ開いて内容を確認し、必要な情報を探し出さなければなりません。

次に、属人化の問題があります。ベテラン営業担当者は長年の経験から、どの資料にどのような情報があるかを把握しており、効率的に提案書を作成できます。しかし、新人営業担当者は資料の在り処すらわからず、提案書の品質にも大きなばらつきが生じてしまうのです。

さらに、時間的なコストも無視できません。ある調査によれば、営業担当者は平均して提案書の作成に 3〜5 時間を費やしており、複雑な案件では 1 週間以上かかることもあります。この時間を顧客との対話や関係構築に使えれば、より高い成果を上げられるでしょう。

顧客要件の構造化不足

顧客から聞き出した要件をそのまま箇条書きにしただけでは、提案書の作成には不十分です。要件には「必須要件」と「希望要件」があり、優先順位や予算、スケジュールとの関係性を整理する必要があります。

しかし、実際の商談では顧客の発言が時系列順に記録されるだけで、体系的な整理が後回しになりがちです。その結果、提案書作成の段階で「あの話はどこに書いてあったか」「この要件の優先度は高かったか」と資料を見返す時間が発生してしまいます。

また、複数の顧客と並行して商談を進めている場合、各顧客の要件が混同されるリスクもあります。A 社に提案すべき内容を B 社の提案書に誤って記載してしまうようなミスは、信頼を大きく損なう重大な問題です。

過去事例の活用不足

多くの企業には、過去に作成した提案書や成功事例が蓄積されています。これらは新しい提案書を作成する際の貴重な参考資料となりますが、実際には十分に活用されていません。

理由は単純で、過去の提案書を探すのに時間がかかりすぎるからです。ファイル名や作成日から検索しても、内容が類似しているかどうかは実際に開いて読んでみないとわかりません。結局、ゼロから提案書を作成した方が早いという判断になってしまうのです。

以下の図は、これらの課題がどのように関連し合っているかを示しています。

mermaidflowchart TD
    scattered["情報の散逸<br/>複数ツールに分散"] --> time_cost["作業時間の増加<br/>3〜5時間/件"]
    unstructured["要件の未構造化<br/>時系列メモのみ"] --> time_cost

    scattered --> error["ミスのリスク<br/>顧客情報の混同"]
    unstructured --> error

    skill_dependent["属人化<br/>経験による差"] --> quality_gap["品質のばらつき<br/>新人は苦戦"]
    unused_past["過去事例の未活用<br/>検索コスト高"] --> quality_gap

    time_cost --> opportunity_loss["機会損失<br/>顧客対応時間の減少"]
    error --> opportunity_loss
    quality_gap --> opportunity_loss

    style scattered fill:#ffe1e1
    style unstructured fill:#ffe1e1
    style skill_dependent fill:#ffe1e1
    style unused_past fill:#ffe1e1
    style time_cost fill:#ffcccc
    style error fill:#ffcccc
    style quality_gap fill:#ffcccc
    style opportunity_loss fill:#ff9999

この図が示すように、個々の課題は独立しているのではなく、相互に影響し合って最終的に大きな機会損失を生み出しています。これらの課題を根本的に解決するには、情報の一元化と自動化が不可欠なのです。

解決策

NotebookLM による情報の一元管理

NotebookLM の最大の強みは、複数の形式の資料を 1 つのノートブックで一元管理できることです。営業活動で扱う主要な資料形式はすべて対応しています。

対応しているファイル形式は以下の通りです。

#形式用途例
1PDF製品カタログ、過去の提案書、契約書
2Google ドキュメント議事録、ヒアリングメモ
3Google スライド製品紹介資料、過去のプレゼン資料
4テキスト顧客からのメール、簡易メモ
5Web ページ(URL)競合他社の情報、業界ニュース
6YouTube 動画製品デモ、セミナー録画
7音声ファイル商談の録音データ

これらの資料を 1 つのノートブックにまとめてアップロードすることで、NotebookLM が横断的に情報を検索し、関連する内容を自動的に抽出してくれます。

たとえば、「A 社の予算制約に対応できる製品プランは何か?」という質問をすると、NotebookLM はヒアリングメモから予算情報を、製品カタログから該当プランを、過去の提案書から類似事例を見つけ出し、それらを統合した回答を生成します。

RAG 技術の仕組みと利点

ここで、NotebookLM の中核技術である RAG(Retrieval-Augmented Generation)について詳しく説明します。

従来の大規模言語モデル(LLM)は、学習済みの知識のみを使って回答を生成します。そのため、企業固有の製品情報や顧客の具体的な要件については回答できません。一方、RAG では以下のステップで回答を生成します。

まず、ユーザーからの質問を受け取ります。次に、アップロードされた資料の中から質問に関連する部分を検索します。そして、検索された関連部分をコンテキストとして LLM に渡し、それに基づいた回答を生成するのです。最後に、生成された回答と共に、参照元の資料へのリンクを提供します。

この仕組みにより、NotebookLM は以下の利点を提供します。

正確性の向上が第一の利点です。アップロードされた資料のみを参照するため、Web 上の不確実な情報に影響されません。

トレーサビリティの確保も重要です。すべての回答に資料内の該当箇所へのリンクが付与され、根拠を確認できます。

業務特化型の回答も可能になります。企業固有の用語や製品情報を正確に理解し、それに基づいた提案が可能です。

そして、セキュリティの維持ができます。資料は外部の Web 検索に使用されず、ノートブック内で完結します。

以下の図は、NotebookLM を活用した営業提案システムのアーキテクチャを示しています。

mermaidflowchart TB
    subgraph sources["ソース資料"]
        pdf["製品カタログ<br/>PDF"]
        doc["議事録<br/>Googleドキュメント"]
        audio["商談録音<br/>音声ファイル"]
        web["競合情報<br/>Webページ"]
    end

    subgraph notebook["NotebookLM"]
        upload["資料アップロード"] --> indexing["インデックス作成<br/>埋め込みベクトル化"]
        indexing --> storage["ベクトルDB<br/>資料保存"]

        query["質問入力"] --> retrieval["関連情報検索<br/>RAG処理"]
        storage --> retrieval
        retrieval --> generation["回答生成<br/>Gemini API"]
        generation --> response["回答+参照元<br/>リンク付き"]
    end

    subgraph output["出力"]
        summary["要約"]
        structured["構造化データ"]
        proposal["提案書下書き"]
    end

    pdf --> upload
    doc --> upload
    audio --> upload
    web --> upload

    response --> summary
    response --> structured
    response --> proposal

    style sources fill:#e1f5ff
    style notebook fill:#fff4e1
    style output fill:#e1ffe1
    style generation fill:#ffe1f5

この図から、NotebookLM が資料をベクトル化してインデックスを作成し、質問に応じて関連情報を検索して回答を生成する流れがわかります。すべての処理は NotebookLM 内で完結し、外部に情報が流出することはありません。

プロンプトエンジニアリングの重要性

NotebookLM から期待通りの出力を得るには、適切なプロンプト設計が欠かせません。特に、カスタムプロンプト機能を活用することで、提案書の下書きや要件整理を自動化できます。

効果的なプロンプトの構成要素は以下の通りです。

まず、役割の明確化です。「あなたは経験豊富な営業コンサルタントです」のように、AI の役割を指定します。

次に、対象資料の指定です。「アップロードされた議事録と製品カタログを参照して」のように、どの資料を使うかを明示します。

出力形式の指定も重要です。「以下の形式で出力してください」として、具体的なテンプレートを示します。

そして、制約条件の設定です。「文字数は 2000 字以内、箇条書きで」のように、出力の条件を指定します。

最後に、禁止事項の明記です。「アップロードされた資料以外の情報は使用しないでください」のように、避けるべき行動を先に記載します。

実践的なプロンプトの例を見てみましょう。

以下は、顧客要件を構造化するためのプロンプトです。

text【プロンプトの目的】
顧客ヒアリングの議事録から、要件を構造化して整理する

【役割】
あなたは営業支援の専門家です

【対象資料】
アップロードされた議事録のみを参照してください

【出力形式】
以下のJSON形式で出力してください:

{
  "顧客名": "",
  "商談日": "",
  "必須要件": [],
  "希望要件": [],
  "予算": "",
  "導入時期": "",
  "決裁者": "",
  "課題": [],
  "期待効果": []
}

このプロンプトを NotebookLM の「カスタム」会話スタイルに設定することで、議事録をアップロードするだけで自動的に構造化された JSON 形式の要件リストが生成されます。

テンプレート機能の活用

NotebookLM には、あらかじめ用意されたテンプレート機能があり、定型的な出力を簡単に生成できます。営業提案業務では特に以下のテンプレートが有用です。

ブリーフィング・ドキュメントは、複数の資料を統合して 1 つの要約文書を作成します。顧客情報、製品情報、過去事例をまとめて提案書の骨子を生成できます。

タイムラインは、プロジェクトのスケジュールや商談の経緯を時系列で整理します。

よくある質問は、顧客から想定される質問とその回答を Q&A 形式で生成します。

これらのテンプレートは、そのまま使用することもできますが、より効果的な方法があります。それは、出力フォーマットを定義したテンプレート文書を自分で作成し、それをソースとしてアップロードする方法です。

たとえば、提案書のフォーマットを定義した Markdown ファイルを作成します。

markdown# 提案書テンプレート

# エグゼクティブサマリー

(ここに概要を記載)

# 顧客の課題

(ここに顧客が抱える課題を記載)

# 提案内容

(ここに解決策を記載)

# 導入効果

(ここに期待される効果を記載)

# スケジュール

(ここに導入スケジュールを記載)

# 見積もり

(ここに価格情報を記載)

このテンプレートファイルを NotebookLM にアップロードし、「このテンプレートに沿って、他の資料の内容を使って提案書の下書きを作成してください」と指示すると、一貫性のある提案書が自動生成されるのです。

マインドマップによる可視化

顧客要件や提案内容を視覚的に整理するには、NotebookLM のマインドマップ機能が非常に有効です。

アップロードした資料の内容を自動的に「中心トピック」と「関連トピック」に分類し、それらの関係性を図として表示します。これにより、複雑な顧客要件の全体像を一目で把握できるようになります。

たとえば、顧客ヒアリングの議事録からマインドマップを生成すると、以下のような構造が可視化されます。中心に「顧客の主要課題」が配置され、その周囲に「予算」「スケジュール」「技術要件」「組織体制」などの関連トピックが配置されます。各トピックをクリックすると、該当する資料の箇所にジャンプできるため、詳細を確認するのも簡単です。

この可視化機能は、営業担当者だけでなく、技術チームや経営層と情報を共有する際にも役立ちます。

具体例

ここからは、NotebookLM を使って顧客要件を構造化し、提案書の下書きを作成する具体的な実装例を紹介します。

顧客ヒアリングから要件抽出までのワークフロー

実際の営業現場で NotebookLM を活用する場合、以下のワークフローが効果的です。

まず、商談中に音声録音を行います。顧客との会話をスマートフォンや IC レコーダーで録音します。

次に、録音ファイルを NotebookLM にアップロードします。mp3 形式などの音声ファイルを新しいノートブックに追加します。

そして、製品資料や過去事例も同じノートブックにアップロードします。製品カタログ、価格表、類似業界の成功事例などをまとめて登録します。

カスタムプロンプトを設定します。前述の要件構造化プロンプトを「カスタム」会話スタイルに設定します。

NotebookLM に質問します。「この商談から顧客要件を抽出して構造化してください」と指示すると、自動的に JSON 形式の要件リストが生成されます。

生成された要件を確認・修正します。必要に応じて内容を調整し、社内の管理システムに登録します。

最後に、提案書の下書きを依頼します。「この要件に対応する提案書の下書きを、テンプレートに沿って作成してください」と指示すると、提案書の骨子が完成します。

以下の図は、このワークフローの流れを示しています。

mermaidsequenceDiagram
    participant sales as 営業担当者
    participant meeting as 顧客商談
    participant notebook as NotebookLM
    participant output as 構造化データ
    participant proposal as 提案書下書き

    sales->>meeting: 商談実施<br/>音声録音
    meeting->>notebook: 録音ファイル<br/>アップロード
    sales->>notebook: 製品資料<br/>過去事例<br/>アップロード
    sales->>notebook: カスタムプロンプト<br/>設定
    sales->>notebook: 要件抽出依頼

    notebook->>notebook: RAG処理<br/>関連情報検索
    notebook->>notebook: Gemini APIで<br/>要件構造化
    notebook->>output: JSON形式で<br/>要件出力

    sales->>output: 内容確認・修正
    sales->>notebook: 提案書下書き依頼
    notebook->>proposal: テンプレートに沿って<br/>下書き生成
    sales->>proposal: 最終調整・完成

このワークフローにより、従来 3〜5 時間かかっていた作業が、30 分〜1 時間程度に短縮できます。

プロンプト設計のベストプラクティス

実務で NotebookLM を活用する際、プロンプトの設計が成果を大きく左右します。ここでは、実際に効果があったプロンプト設計のコツを紹介します。

シンプルな言い回しを使う

複雑な表現よりも、明確で簡潔な指示の方が正確な結果を得られます。

text❌ 悪い例:
顧客が述べた様々な要望や期待、そして現状の課題について、
包括的かつ体系的に整理し、優先順位を加味しながら...

✅ 良い例:
顧客の要件を以下の3つに分類してください:
1. 必須要件
2. 希望要件
3. 現状の課題

複数の質問を同時にしない

1 つのプロンプトには 1 つの目的に絞ります。

text❌ 悪い例:
顧客要件をまとめて、提案書も作成して、
競合との比較表も出力してください

✅ 良い例:
まず顧客要件をJSON形式でまとめてください

要件整理が完了したら、次のステップとして提案書作成を依頼します。

出力形式を具体的に指定する

「まとめてください」だけでは不十分です。具体的なフォーマットを示します。

以下は、出力形式を明確に指定したプロンプトの例です。

text以下の形式で出力してください:

# 顧客情報
- 企業名:
- 担当者:
- 部署:

# 課題
1.
2.
3.

# 要件
## 必須
-
-

## 希望
-
-

# 予算・スケジュール
- 予算:
- 導入希望時期:

禁止事項を先に明記する

AI が避けるべき行動を最初に伝えます。

text【重要な制約】
- アップロードされた資料以外の情報は使用しないでください
- 推測や憶測で情報を補完しないでください
- 資料に記載がない項目は「情報なし」と明記してください

【タスク】
上記の制約を守りながら、顧客要件を整理してください

文書ベースのプロンプト手法

高度なテクニックとして、プロンプト自体を文書化してソースとしてアップロードする方法があります。

プロンプト文書ファイル(prompt_requirements.md)を作成します。

markdown# 顧客要件整理プロンプト

# あなたの役割

あなたは営業支援の専門家です

# 対象資料

- 商談の録音ファイル
- 顧客から提供された資料
- 製品カタログ

# タスク

議事録から顧客要件を抽出し、構造化してください

# 出力形式

JSON 形式で以下の項目を出力:

- 顧客名
- 商談日
- 必須要件(配列)
- 希望要件(配列)
- 予算
- 導入時期
- 課題(配列)

# 制約

- 資料に記載のない情報は推測しない
- 曖昧な表現は避け、具体的に記述する

このファイルを NotebookLM にアップロードし、「このプロンプトに従って処理してください」と指示するだけで、一貫性のある処理を実行できます。

Gemini API を使った RAG 実装の技術例

NotebookLM は非常に便利ですが、2025 年 11 月時点で一般向けの Public API は提供されていません。そのため、より高度な自動化やシステム連携を実現したい場合は、NotebookLM の基盤技術である Gemini API を使って独自の RAG システムを構築する必要があります。

ここでは、TypeScript と Next.js を使って、営業提案支援システムを構築する実装例を紹介します。

環境のセットアップ

まず、必要なパッケージをインストールします。

bashyarn add @google/generative-ai
yarn add -D @types/node

次に、環境変数ファイル(.env.local)に API キーを設定します。

bashGOOGLE_API_KEY=your_api_key_here

型定義の作成

TypeScript で型安全な実装をするため、まず顧客要件の型を定義します。

typescript// types/requirements.ts

/**
 * 顧客要件の構造化データ型
 */
export interface CustomerRequirements {
  customerName: string;
  meetingDate: string;
  mustHaveRequirements: string[];
  niceToHaveRequirements: string[];
  budget: string;
  timeline: string;
  decisionMaker: string;
  challenges: string[];
  expectedBenefits: string[];
}

次に、Gemini API のレスポンス型を定義します。

typescript// types/gemini.ts

/**
 * Gemini APIのレスポンス型
 */
export interface GeminiResponse {
  text: string;
  error?: string;
}

/**
 * RAG処理の設定型
 */
export interface RAGConfig {
  model: string;
  temperature: number;
  maxOutputTokens: number;
}

Gemini API クライアントの実装

Gemini API を利用するクライアントクラスを作成します。

まず、クライアントの初期化部分です。

typescript// lib/geminiClient.ts
import { GoogleGenerativeAI } from '@google/generative-ai';
import type { RAGConfig } from '@/types/gemini';

/**
 * Gemini APIクライアントクラス
 * 営業提案書作成のためのRAG処理を実行
 */
export class GeminiClient {
  private ai: GoogleGenerativeAI;
  private config: RAGConfig;

  constructor(apiKey: string, config?: Partial<RAGConfig>) {
    // APIキーでクライアントを初期化
    this.ai = new GoogleGenerativeAI(apiKey);

    // デフォルト設定をマージ
    this.config = {
      model: 'gemini-1.5-pro',
      temperature: 0.2, // 創造性を抑えて正確性を重視
      maxOutputTokens: 2048,
      ...config,
    };
  }
}

次に、ファイルをアップロードする機能を実装します。

typescript/**
 * ファイルをGemini APIにアップロード
 * @param filePath ファイルのパス
 * @param mimeType ファイルのMIMEタイプ
 * @returns アップロードされたファイルのメタデータ
 */
async uploadFile(filePath: string, mimeType: string) {
  const fileManager = this.ai.fileManager;

  try {
    // ファイルをアップロード
    const uploadResult = await fileManager.uploadFile(filePath, {
      mimeType,
      displayName: filePath.split('/').pop(),
    });

    console.log(`ファイルアップロード成功: ${uploadResult.file.uri}`);
    return uploadResult.file;
  } catch (error) {
    console.error('ファイルアップロードエラー:', error);
    throw new Error(`ファイルのアップロードに失敗しました: ${filePath}`);
  }
}

そして、RAG 処理のメインロジックを実装します。

typescript/**
 * RAG処理を実行して顧客要件を抽出
 * @param fileUri アップロード済みファイルのURI
 * @param prompt 実行するプロンプト
 * @returns 生成されたテキスト
 */
async generateContent(fileUri: string, prompt: string): Promise<string> {
  const model = this.ai.getGenerativeModel({
    model: this.config.model,
  });

  try {
    // ファイルとプロンプトを組み合わせて生成
    const result = await model.generateContent([
      {
        fileData: {
          mimeType: 'application/pdf',
          fileUri: fileUri,
        },
      },
      { text: prompt },
    ]);

    const response = result.response;
    return response.text();
  } catch (error) {
    console.error('コンテンツ生成エラー:', error);
    throw error;
  }
}

エラーハンドリングの実装

実運用では、適切なエラーハンドリングが不可欠です。Gemini API では主に以下のエラーが発生します。

主要なエラーコードとその対処法を以下の表にまとめます。

#エラーコード意味リトライ対処法
1400 Bad Requestリクエスト形式不正不要パラメータを確認
2401 UnauthorizedAPI キー無効不要API キーを再確認
3429 Too Many Requestsレート制限超過必要指数バックオフで待機
4500 Internal Server Errorサーバーエラー有効短時間待機後リトライ
5503 Service Unavailableサービス停止中有効長めの待機後リトライ

エラーハンドリングを実装したラッパー関数を作成します。

typescript// lib/errorHandler.ts

/**
 * APIエラーの型定義
 */
interface APIError {
  code: number;
  message: string;
  retryable: boolean;
}

/**
 * エラーコードからエラー情報を取得
 */
function parseError(error: any): APIError {
  const code = error?.status || error?.code || 500;
  const message =
    error?.message || '不明なエラーが発生しました';

  // リトライ可能なエラーかどうかを判定
  const retryable = [429, 500, 503].includes(code);

  return { code, message, retryable };
}

次に、指数バックオフを使ったリトライロジックを実装します。

typescript/**
 * 指数バックオフを使ったリトライ処理
 * @param fn 実行する関数
 * @param maxRetries 最大リトライ回数
 * @param baseDelay 基本待機時間(ミリ秒)
 */
export async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  baseDelay: number = 1000
): Promise<T> {
  let lastError: any;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      const apiError = parseError(error);

      // リトライ不可能なエラーはすぐに投げる
      if (!apiError.retryable) {
        throw error;
      }

      // 最後の試行だったら諦める
      if (attempt === maxRetries) {
        break;
      }

      // 指数バックオフで待機
      const delay = baseDelay * Math.pow(2, attempt);
      console.log(
        `リトライ ${
          attempt + 1
        }/${maxRetries}: ${delay}ms 待機`
      );
      await new Promise((resolve) =>
        setTimeout(resolve, delay)
      );
    }
  }

  throw lastError;
}

顧客要件抽出の実装

これまでの部品を組み合わせて、顧客要件を抽出する関数を実装します。

typescript// lib/extractRequirements.ts
import { GeminiClient } from './geminiClient';
import { retryWithBackoff } from './errorHandler';
import type { CustomerRequirements } from '@/types/requirements';

/**
 * 顧客要件抽出用のプロンプト
 */
const REQUIREMENTS_PROMPT = `
あなたは営業支援の専門家です。

このファイルには顧客との商談内容が記録されています。
以下のJSON形式で顧客要件を抽出してください:

{
  "customerName": "顧客企業名",
  "meetingDate": "YYYY-MM-DD",
  "mustHaveRequirements": ["必須要件1", "必須要件2"],
  "niceToHaveRequirements": ["希望要件1", "希望要件2"],
  "budget": "予算情報",
  "timeline": "導入希望時期",
  "decisionMaker": "決裁者名",
  "challenges": ["課題1", "課題2"],
  "expectedBenefits": ["期待効果1", "期待効果2"]
}

【重要な制約】
- ファイルに記載のない情報は推測しないでください
- 情報が不明な項目は空文字または空配列にしてください
- 必ずJSON形式で出力してください
`.trim();

次に、要件抽出のメイン関数を実装します。

typescript/**
 * 商談録音ファイルから顧客要件を抽出
 * @param filePath 商談録音ファイルのパス
 * @returns 構造化された顧客要件
 */
export async function extractRequirements(
  filePath: string
): Promise<CustomerRequirements> {
  const apiKey = process.env.GOOGLE_API_KEY;
  if (!apiKey) {
    throw new Error('GOOGLE_API_KEY が設定されていません');
  }

  const client = new GeminiClient(apiKey);

  // ファイルアップロードをリトライ付きで実行
  const file = await retryWithBackoff(() =>
    client.uploadFile(filePath, 'audio/mp3')
  );

  // RAG処理をリトライ付きで実行
  const responseText = await retryWithBackoff(() =>
    client.generateContent(file.uri, REQUIREMENTS_PROMPT)
  );

  // JSON部分を抽出してパース
  const jsonMatch = responseText.match(/\{[\s\S]*\}/);
  if (!jsonMatch) {
    throw new Error('有効なJSONが生成されませんでした');
  }

  return JSON.parse(jsonMatch[0]);
}

Next.js API ルートの実装

最後に、Next.js の API ルートとして公開します。

typescript// app/api/extract-requirements/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { extractRequirements } from '@/lib/extractRequirements';
import fs from 'fs/promises';
import path from 'path';

/**
 * POST /api/extract-requirements
 * 商談録音ファイルから顧客要件を抽出するAPI
 */
export async function POST(request: NextRequest) {
  try {
    // フォームデータからファイルを取得
    const formData = await request.formData();
    const file = formData.get('audio') as File;

    if (!file) {
      return NextResponse.json(
        { error: 'ファイルがアップロードされていません' },
        { status: 400 }
      );
    }

    // 一時ファイルとして保存
    const bytes = await file.arrayBuffer();
    const buffer = Buffer.from(bytes);
    const tempPath = path.join('/tmp', file.name);
    await fs.writeFile(tempPath, buffer);

    // 顧客要件を抽出
    const requirements = await extractRequirements(
      tempPath
    );

    // 一時ファイルを削除
    await fs.unlink(tempPath);

    return NextResponse.json(requirements);
  } catch (error: any) {
    console.error('要件抽出エラー:', error);
    return NextResponse.json(
      {
        error: error.message || '要件の抽出に失敗しました',
      },
      { status: 500 }
    );
  }
}

フロントエンドからの利用

この API を呼び出すフロントエンド実装の例です。

typescript// components/RequirementsExtractor.tsx
'use client';

import { useState } from 'react';
import type { CustomerRequirements } from '@/types/requirements';

export function RequirementsExtractor() {
  const [loading, setLoading] = useState(false);
  const [requirements, setRequirements] =
    useState<CustomerRequirements | null>(null);
  const [error, setError] = useState<string | null>(null);

  const handleFileUpload = async (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const file = event.target.files?.[0];
    if (!file) return;

    setLoading(true);
    setError(null);

    try {
      // FormDataを作成してファイルを追加
      const formData = new FormData();
      formData.append('audio', file);

      // APIを呼び出し
      const response = await fetch(
        '/api/extract-requirements',
        {
          method: 'POST',
          body: formData,
        }
      );

      if (!response.ok) {
        throw new Error('要件の抽出に失敗しました');
      }

      const data = await response.json();
      setRequirements(data);
    } catch (err: any) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <input
        type='file'
        accept='audio/*'
        onChange={handleFileUpload}
        disabled={loading}
      />
      {loading && <p>処理中...</p>}
      {error && <p style={{ color: 'red' }}>{error}</p>}
      {requirements && (
        <pre>{JSON.stringify(requirements, null, 2)}</pre>
      )}
    </div>
  );
}

この実装により、商談の録音ファイルをアップロードするだけで、自動的に顧客要件が構造化された JSON 形式で取得できます。

実装のポイントと注意事項

上記の実装例では、以下の点に特に注意を払っています。

型安全性の確保です。TypeScript の型システムを最大限活用し、実行時エラーを防いでいます。

適切なエラーハンドリングも重要です。リトライ可能なエラーとそうでないエラーを区別し、無駄なリトライを避けています。

セキュリティ対策として、API キーは環境変数で管理し、ソースコードにハードコーディングしません。

ログ出力の最適化も考慮しています。エラーログに機密情報(顧客名、予算など)を含めないよう注意します。

そして、パフォーマンスの最適化です。不要なファイル読み込みを避け、処理完了後は一時ファイルを確実に削除しています。

実際の運用では、以下の点も考慮する必要があります。

ファイルサイズの制限を設けることで、過大なファイルによるシステム負荷を防ぎます。

レート制限の監視も必要です。Gemini API の利用制限に達しないよう、リクエスト数を監視します。

処理結果のキャッシュを活用することで、同じファイルを再処理する無駄を省けます。

そして、監査ログの記録です。誰がいつどのファイルを処理したかを記録し、コンプライアンスを確保します。

まとめ

本記事では、NotebookLM を活用して営業提案書の下書き作成と顧客要件の整理を自動化する方法を、技術的な観点から詳しく解説しました。

NotebookLM 活用の主要なメリット

NotebookLM を営業提案業務に導入することで、以下の効果が期待できます。

作業時間の大幅削減が第一の効果です。従来 3〜5 時間かかっていた提案書作成が 30 分〜1 時間程度に短縮され、営業担当者は顧客との対話により多くの時間を使えるようになります。

情報の一元管理も実現します。議事録、製品資料、過去事例などを 1 つのノートブックで管理し、横断的な検索が可能になります。

品質の標準化にも貢献します。カスタムプロンプトとテンプレート機能により、新人でも一定品質の提案書を作成できるようになります。

トレーサビリティの確保も重要です。すべての出力に参照元へのリンクが付与され、根拠を明確にできます。

そして、過去事例の有効活用が可能になります。蓄積された提案書や成功事例を効率的に検索し、新しい提案に活かせます。

技術的な実装アプローチ

記事で紹介した内容は、大きく 2 つのアプローチに分かれます。

NotebookLM Web 版の活用は、プログラミング不要で今すぐ始められる方法です。カスタムプロンプト、テンプレート機能、マインドマップを組み合わせることで、効率的な要件整理と提案書作成が可能になります。

Gemini API による独自実装は、より高度な自動化やシステム連携を実現する方法です。TypeScript/Next.js を使って RAG システムを構築し、社内システムと統合することで、エンドツーエンドの自動化が可能になります。

どちらのアプローチを選ぶかは、組織の技術リソースや自動化の要件によって決まります。まずは NotebookLM Web 版で効果を実感し、必要に応じて API 実装に移行するのが現実的な導入パスでしょう。

注意点とベストプラクティス

NotebookLM を効果的に活用するには、以下の点に注意が必要です。

セキュリティへの配慮は最優先です。顧客情報や機密情報を扱う場合、誤って外部共有しないよう注意してください。企業版(NotebookLM Plus)では管理機能が強化されていますが、利用ポリシーを明確にすることが重要です。

プロンプトの継続的改善も欠かせません。初回から完璧な出力を得るのは難しいため、実際の使用を通じてプロンプトを改善し続ける必要があります。

人間の最終確認は必須です。AI が生成した提案書や要件リストは、必ず人間が最終確認し、誤りがないかチェックしてください。特に数値情報や顧客名などは慎重に確認します。

段階的な導入を推奨します。いきなり全案件に適用するのではなく、まずは小規模な案件やパイロットプロジェクトで効果を検証し、ノウハウを蓄積してから本格展開することをお勧めします。

チーム内での知識共有も重要です。効果的なプロンプトやテンプレートをチーム内で共有し、組織全体の提案力を底上げしましょう。

今後の展開可能性

NotebookLM と Gemini API は今後も進化を続けます。以下のような展開が期待されます。

マルチモーダル機能の強化により、画像や動画からも要件を抽出できるようになるでしょう。顧客のオフィスの写真や設備の動画から、具体的な提案内容を自動生成できるかもしれません。

音声機能の日本語対応が進めば、日本語での音声要約や音声ベースの提案書レビューが可能になります。

より高度な構造化機能により、顧客要件だけでなく、競合分析や市場動向の分析も自動化できるようになるでしょう。

CRM 連携が進めば、Salesforce や HubSpot などの営業支援システムと直接連携し、商談情報から自動的に提案書を生成できるようになります。

NotebookLM は、営業活動における「考える時間」を増やし、「作業時間」を減らすための強力なツールです。本記事で紹介した技術を活用し、営業提案業務の効率化と品質向上を実現してください。

関連リンク