AI・機械学習 2026.05.13

Laravel×AIチャットボットで問い合わせ対応コストを月30万円削減した実装事例

約6分で読めます

問い合わせ対応に追われるWeb担当者必見。LaravelとOpenAI APIを組み合わせたチャットボット実装で、月30万円のコスト削減を実現した実案件を解説します。

こんな悩み、抱えていませんか?

「問い合わせメールへの返信に毎日2〜3時間取られている」「同じような質問が繰り返し来て、担当者が疲弊している」「営業時間外の問い合わせを翌朝まで放置せざるを得ない」——。

これはとある神奈川県の中堅製造業クライアントから寄せられた実際の声です。月間の問い合わせ件数は約800件。その約7割が「営業時間」「製品スペック」「見積もり方法」といった定型的な質問でした。担当者2名がほぼ専任で対応していたため、人件費換算で月30万円以上が問い合わせ対応だけに消えていたのです。

この記事では、そのクライアントに実際に導入したLaravel×OpenAI APIを組み合わせたAIチャットボットの実装手順を、コード例を交えて詳しく解説します。


なぜ「定型問い合わせ」がここまで負担になるのか

問い合わせ対応が肥大化する背景には、いくつかの構造的な問題があります。

1. FAQページが機能していない 多くの企業でFAQページは存在するものの、検索性が低く、ユーザーが欲しい情報にたどり着けていません。結果として「メールで聞いた方が早い」という行動を促してしまっています。

2. 問い合わせフォームへの動線が太すぎる サイト設計上、問い合わせフォームへのリンクが目立ちすぎると、些細な疑問でも「とりあえず送信」する行動が増えます。本来であれば自己解決できた問い合わせが積み上がる原因です。

3. 対応品質のムラが発生する 担当者が変わると回答の内容や温度感が変わり、クレームに発展するケースも少なくありません。チャットボットによる一次対応の統一はここにも効きます。

これらの課題を一気に解決するのが、LaravelバックエンドとOpenAI APIを組み合わせたコンテキスト対応型のチャットボットです。


無料AI相談

AIで気軽にWeb相談してみませんか?

詳しく見る

実装の全体像:アーキテクチャ設計の考え方

まず、今回採用したシステム構成を整理しておきます。

flowchart TD
    A[ユーザー問い合わせ] --> B[Laravelフロントエンド]
    B --> C[ChatController]
    C --> D{FAQ検索}
    D -->|マッチあり| E[定型回答を返却]
    D -->|マッチなし| F[OpenAI API呼び出し]
    F --> G[コンテキスト付き回答生成]
    G --> H[回答をDBに保存]
    H --> I[ユーザーへ返答]
    E --> I
    I --> J{解決しない場合}
    J -->|エスカレーション| K[担当者へメール通知]

ポイントは二段階の回答戦略です。まず自社の既存FAQ・製品データベースからベクトル検索で類似質問を探し、マッチした場合は固定回答を返します。マッチしない場合のみOpenAI APIにリクエストを送ることで、APIコストを約60%削減できました。


実装手順:コードで見る具体的な構築方法

Step 1:LaravelプロジェクトにOpenAI SDKを導入

composer require openai-php/laravel
php artisan vendor:publish --provider="OpenAI\Laravel\ServiceProvider"

.envファイルにAPIキーを追記します。

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_ORGANIZATION=org-xxxxxxxxxxxxxxxx

Step 2:会話履歴を保存するマイグレーション

コンテキストを保持するために、会話セッションと発言履歴を分けて管理します。

// database/migrations/xxxx_create_chat_sessions_table.php
Schema::create('chat_sessions', function (Blueprint $table) {
    $table->id();
    $table->string('session_id')->unique();
    $table->string('visitor_ip')->nullable();
    $table->boolean('escalated')->default(false);
    $table->timestamps();
});

Schema::create('chat_messages', function (Blueprint $table) {
    $table->id();
    $table->foreignId('chat_session_id')->constrained()->cascadeOnDelete();
    $table->enum('role', ['user', 'assistant', 'system']);
    $table->text('content');
    $table->timestamps();
});

Step 3:ChatControllerのコア実装

<?php

namespace App\Http\Controllers;

use App\Models\ChatSession;
use App\Models\ChatMessage;
use App\Services\FaqMatcherService;
use Illuminate\Http\Request;
use OpenAI\Laravel\Facades\OpenAI;

class ChatController extends Controller
{
    public function __construct(
        private FaqMatcherService $faqMatcher
    ) {}

    public function send(Request $request): \Illuminate\Http\JsonResponse
    {
        $request->validate([
            'message'    => 'required|string|max:500',
            'session_id' => 'required|string',
        ]);

        $session = ChatSession::firstOrCreate(
            ['session_id' => $request->session_id],
            ['visitor_ip' => $request->ip()]
        );

        // ユーザーメッセージを保存
        $session->messages()->create([
            'role'    => 'user',
            'content' => $request->message,
        ]);

        // まずFAQデータベースで検索
        $faqAnswer = $this->faqMatcher->find($request->message);
        if ($faqAnswer) {
            $session->messages()->create([
                'role'    => 'assistant',
                'content' => $faqAnswer,
            ]);
            return response()->json(['reply' => $faqAnswer, 'source' => 'faq']);
        }

        // FAQにない場合はOpenAIへ
        $history = $session->messages()
            ->orderBy('created_at')
            ->get()
            ->map(fn($m) => ['role' => $m->role, 'content' => $m->content])
            ->toArray();

        $systemPrompt = [
            'role'    => 'system',
            'content' => "あなたは株式会社〇〇の公式サポートAIです。"
                       . "製品・サービスに関する質問に丁寧に答えてください。"
                       . "回答が不明な場合は無理に答えず、担当者への連絡を案内してください。",
        ];

        $response = OpenAI::chat()->create([
            'model'       => 'gpt-4o-mini',
            'messages'    => array_merge([$systemPrompt], $history),
            'max_tokens'  => 400,
            'temperature' => 0.3,
        ]);

        $reply = $response->choices[0]->message->content;

        $session->messages()->create([
            'role'    => 'assistant',
            'content' => $reply,
        ]);

        // エスカレーション検出
        if ($this->needsEscalation($reply)) {
            $this->escalate($session);
        }

        return response()->json(['reply' => $reply, 'source' => 'ai']);
    }

    private function needsEscalation(string $reply): bool
    {
        $triggers = ['担当者', 'お問い合わせください', '対応いたしかね'];
        foreach ($triggers as $trigger) {
            if (str_contains($reply, $trigger)) return true;
        }
        return false;
    }

    private function escalate(ChatSession $session): void
    {
        if ($session->escalated) return;
        $session->update(['escalated' => true]);
        // 担当者へのメール通知処理(Mailableで実装)
    }
}

Step 4:フロントエンドのチャットUI(Alpine.js)

<div x-data="chatBot()" class="fixed bottom-4 right-4 z-50">
    <div x-show="open" class="w-80 bg-white rounded-xl shadow-2xl flex flex-col" style="height:420px">
        <div class="bg-indigo-600 text-white px-4 py-3 rounded-t-xl font-semibold">
            💬 サポートチャット
        </div>
        <div class="flex-1 overflow-y-auto p-3 space-y-2" x-ref="messages">
            <template x-for="msg in messages" :key="msg.id">
                <div :class="msg.role === 'user' ? 'text-right' : 'text-left'">
                    <span class="inline-block px-3 py-2 rounded-lg text-sm"
                          :class="msg.role === 'user' ? 'bg-indigo-100' : 'bg-gray-100'"
                          x-text="msg.content"></span>
                </div>
            </template>
        </div>
        <div class="p-3 border-t flex gap-2">
            <input x-model="input" @keyup.enter="send" type="text"
                   class="flex-1 border rounded-lg px-3 py-2 text-sm" placeholder="質問を入力..." />
            <button @click="send" class="bg-indigo-600 text-white px-4 rounded-lg text-sm">送信</button>
        </div>
    </div>
    <button @click="open = !open" class="bg-indigo-600 text-white w-14 h-14 rounded-full shadow-lg text-2xl">
        💬
    </button>
</div>

<script>
function chatBot() {
    return {
        open: false,
        input: '',
        messages: [],
        sessionId: crypto.randomUUID(),
        async send() {
            if (!this.input.trim()) return;
            this.messages.push({ id: Date.now(), role: 'user', content: this.input });
            const msg = this.input;
            this.input = '';
            const res = await fetch('/api/chat', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content },
                body: JSON.stringify({ message: msg, session_id: this.sessionId })
            });
            const data = await res.json();
            this.messages.push({ id: Date.now(), role: 'assistant', content: data.reply });
        }
    }
}
</script>

導入後の変化:数字で見る効果

導入から3ヶ月後のデータを共有します。

  • 自動解決率:3ヶ月で約73%に到達
  • 平均応答時間:48時間 → 即時(24時間対応に)
  • 人件費削減:月約30.5万円相当(担当者2名の工数換算)
  • 顧客満足度:問い合わせ後アンケートのスコアが4.1→4.6に改善
  • OpenAI APIコスト:月額約8,000円(gpt-4o-mini採用+FAQ二段階戦略の効果)

よくある失敗パターンと対処法

実装・運用を通じて見えてきた典型的なつまずきポイントを共有します。

OpenAIへ渡すシステムプロンプトが「丁寧に答えてください」だけだと、競合他社の情報や誤った価格情報を自信満々に回答してしまうケースがありました。対策として「不明な場合は必ず担当者への連絡を促す」「自社製品以外の比較はしない」といったガードレールを明示的に記述することが必須です。`temperature`を0.2〜0.3に下げることも有効です。
会話が長引くとMessageオブジェクトが膨らみ、APIコストが急騰しました。直近5〜8件のメッセージに絞ってAPIへ渡すよう変更し、コストを約40%削減しました。`->latest()->take(8)->get()->reverse()`で最新8件を時系列順に取得する実装がシンプルです。
「とりあえず動かしてからFAQを追加しよう」と考えていたクライアントでは、初月のOpenAI APIコストが想定の3倍になりました。最低でも50〜100件のFAQデータを整備してからローンチするべきです。Notionや既存ExcelのFAQをそのままseederでインポートするだけでも効果は大きく変わります。
チャットボットが「担当者に確認します」と案内したにもかかわらず、通知メールが担当者の迷惑メールフォルダに入り、2日間放置されたケースがありました。Slack通知との併用と、エスカレーション後30分以内に担当者からの折り返しがない場合の再通知ロジックを追加することで解決しました。

無料AI相談

AIで気軽にWeb相談してみませんか?

詳しく見る

このAI技術、御社の業務にも導入できます

AI導入・業務自動化

ChatGPT活用や業務自動化など、最新のAI技術を御社に合わせてご提案します

200件以上の制作実績 顧客満足度97% 初回相談無料

※ 通常1営業日以内にご返信します

まとめと次のステップ

Laravel×OpenAI APIによるチャットボットは、適切に設計すれば初期投資を3〜4ヶ月で回収できる施策です。ただし、技術的な実装よりも「FAQデータの整備」と「システムプロンプトの磨き込み」に時間をかけるべきだというのが今回の最大の教訓です。

「まず何から始めるか」で迷っている方は、以下のチェックリストを参考にしてください。


Fivenine Designでは、今回ご紹介した実装を含め、中小企業向けのLaravel×AI活用支援を承っています。「うちの問い合わせに使えるか試算してほしい」「まず社内向けのデモを見てみたい」という段階からでも、お気軽にご相談ください。実際のデータをもとにコスト削減シミュレーションを無料で作成します。

この記事をシェア

AI技術の導入・活用をサポートします

業務に合わせたAI活用のご提案が可能です。 初回相談は無料です。

※ 1営業日以内にご返信いたします

この技術でお困りなら

無料でプロに相談できます

相談する
AIに無料相談