Laravelと機械学習を組み合わせて受注予測システムを構築する実践ガイド。過去の営業データを活用し、属人的なカン頼りの営業から脱却する具体的な実装手順を解説します。
「あの案件、なんとなく取れそうな気がする」——その感覚、データで裏付けられますか?
こんな悩みはありませんか?
- 営業のベテランが辞めたら、突然受注率が下がった
- 月末になるまで「今月どれくらい売れるか」が全く読めない
- 担当者によって見積もりの精度にバラつきがあり、経営計画が立てにくい
- 過去の商談データはExcelに眠っているが、活用できていない
これらはすべて、営業プロセスが「個人のカン」に依存していることから生まれる問題です。中小企業の営業現場では、ベテラン担当者の直感がそのまま会社の売上予測になってしまっているケースが珍しくありません。
今回は、Laravelと機械学習ライブラリを組み合わせて「受注予測システム」を構築する実践的な手順を解説します。完全なAIシステムを一から作る必要はありません。手元にある商談データを活用して、まず予測精度70〜80%を目指す——それだけで営業マネジメントは劇的に変わります。
なぜ「カン頼り」が組織の成長を阻むのか
属人化が生む3つのリスク
受注予測が個人の経験則に依存している場合、以下のリスクが常に存在します。
1. 知識の属人化 ベテラン営業担当者がどのような判断軸で案件の優先度を決めているのか、組織として可視化されていません。退職や異動があった瞬間、その知識は消えます。
2. パイプライン管理の不正確さ 「感覚値」で報告される案件進捗は、経営者が資金計画・採用計画を立てる際の信頼できる根拠になりません。
3. 学習サイクルの欠如 「なぜこの案件が取れたのか・取れなかったのか」を体系的に振り返る仕組みがないため、組織全体の営業力が上がりにくい。
あるクライアント(神奈川県内の製造業・従業員50名)では、営業担当者5名の月末予測と実際の受注額に平均35%の乖離がありました。この乖離が原因で仕入れの過剰在庫が発生し、年間で数百万円のロスが生じていたのです。
Laravelで受注予測システムを構築する実装手順
システムの全体像
flowchart TD
A[CRM・Excel等の商談データ] --> B[Laravel: データ取込・前処理]
B --> C[Python/RubixML: モデル学習]
C --> D[予測モデルの保存]
D --> E[Laravel API: 予測エンドポイント]
E --> F[営業担当者向けダッシュボード]
F --> G[受注確度スコアの表示]Step 1:商談データの設計と取り込み
まず、予測に使う「特徴量(feature)」を決めます。以下は最低限収集すべきカラムの例です。
// database/migrations/create_deals_table.php
Schema::create('deals', function (Blueprint $table) {
$table->id();
$table->string('deal_name');
$table->decimal('amount', 12, 2); // 商談金額
$table->integer('contact_count'); // 接触回数
$table->integer('days_since_first'); // 初回接触からの日数
$table->string('industry'); // 顧客業種
$table->string('company_size'); // 企業規模
$table->integer('competitor_count'); // 競合社数
$table->boolean('has_demo'); // デモ実施済みか
$table->boolean('has_proposal'); // 提案書送付済みか
$table->string('source'); // リードソース(Web/展示会/紹介など)
$table->tinyInteger('won')->nullable(); // 受注結果(1=受注, 0=失注)
$table->timestamps();
});
ポイントは「結果(won)と、それに影響したと思われる要因」をセットで記録することです。過去データがExcelにある場合、Laravelのコマンドでインポートします。
// app/Console/Commands/ImportDealsFromCsv.php
public function handle()
{
$path = storage_path('app/deals_history.csv');
$rows = array_map('str_getcsv', file($path));
$headers = array_shift($rows);
foreach ($rows as $row) {
$data = array_combine($headers, $row);
Deal::updateOrCreate(
['deal_name' => $data['deal_name']],
[
'amount' => (float) str_replace(',', '', $data['amount']),
'contact_count' => (int) $data['contact_count'],
'days_since_first' => (int) $data['days_since_first'],
'industry' => $data['industry'],
'company_size' => $data['company_size'],
'competitor_count' => (int) $data['competitor_count'],
'has_demo' => $data['has_demo'] === '1',
'has_proposal' => $data['has_proposal'] === '1',
'source' => $data['source'],
'won' => isset($data['won']) ? (int) $data['won'] : null,
]
);
}
$this->info('インポート完了: ' . count($rows) . '件');
}
Step 2:Pythonで予測モデルを学習させる
Laravelは機械学習モデルの「呼び出し役」に徹し、学習処理はPythonに任せるのが現実的です。
# train_model.py
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
import pickle
import json
# データ読み込み(LaravelのDBからエクスポートしたCSV)
df = pd.read_csv('deals_export.csv')
df = df.dropna(subset=['won']) # 結果未確定の商談を除外
# カテゴリ変数をエンコード
le_industry = LabelEncoder()
le_size = LabelEncoder()
le_source = LabelEncoder()
df['industry_enc'] = le_industry.fit_transform(df['industry'])
df['size_enc'] = le_size.fit_transform(df['company_size'])
df['source_enc'] = le_source.fit_transform(df['source'])
features = [
'amount', 'contact_count', 'days_since_first',
'competitor_count', 'has_demo', 'has_proposal',
'industry_enc', 'size_enc', 'source_enc'
]
X = df[features]
y = df['won'].astype(int)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
accuracy = model.score(X_test, y_test)
print(f'予測精度: {accuracy:.2%}')
# モデルとエンコーダをシリアライズ
with open('model.pkl', 'wb') as f:
pickle.dump({
'model': model,
'encoders': {
'industry': le_industry,
'company_size': le_size,
'source': le_source
}
}, f)
print('モデルを保存しました: model.pkl')
Step 3:ダッシュボードで可視化する
APIが完成したら、Laravel BladeとChart.jsを使って営業担当者が日常的に確認できるダッシュボードを構築します。重要なのは「スコアを見て次のアクションがわかる」UIにすること。予測確度と合わせて「次にすべきこと(デモを実施する、競合情報を収集するなど)」を提示できると、ツールとして定着しやすくなります。
よくある失敗パターンと対処法
❌ 失敗①:データ量が少ない状態でスタートする
「とりあえず30件のデータで試してみよう」と軽い気持ちで始めると、モデルの精度が出ずに「AIは使えない」と判断してしまいます。ランダムフォレストで実用的な精度(70%超)を出すには、最低200〜300件の完結した商談データが必要です。
対処法: 過去3〜5年分のデータを洗い出し、まずデータ収集期間を設ける。現在進行中の商談から結果を記録し始めるだけでも、半年後には使えるデータ資産になります。
❌ 失敗②:特徴量を入れすぎる
「担当者の血液型」「商談した曜日」まで特徴量に入れてしまい、モデルが過学習(訓練データには強いが未知データに弱い)する事例があります。
対処法: 最初は5〜8個の特徴量に絞る。重要度(feature_importances_)を確認しながら段階的に追加する。
❌ 失敗③:「予測システム」を作って終わりにしてしまう
システムを構築した直後は盛り上がるものの、モデルの再学習をせずに放置した結果、半年後には市場環境と予測ロジックがズレてしまっていた——というケースは非常に多いです。
対処法: Laravelのスケジューラーで月次の自動再学習を仕込む。
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
// 毎月1日の深夜2時にモデルを再学習
$schedule->command('model:retrain')
->monthlyOn(1, '02:00')
->emailOutputOnFailure(config('mail.alert_address'));
}
このAI技術、御社の業務にも導入できます
AI導入・業務自動化
ChatGPT活用や業務自動化など、最新のAI技術を御社に合わせてご提案します
※ 通常1営業日以内にご返信します
まとめ:まず何から始めるか
受注予測システムの構築は、一度に完璧なものを目指す必要はありません。「データを集める → モデルを作る → 現場で使う → 改善する」というサイクルを回し続けることが本質です。
あるクライアントでは、このシステムを導入してから月末の売上着地予測の精度が48%から79%に改善し、経営者が3ヶ月先の採用・投資計画を自信を持って立てられるようになったとのフィードバックをいただきました。営業担当者からも「どの案件に集中すべきか判断しやすくなった」という声が上がっています。
「データは集めたいが、どこから手をつければいいかわからない」「Pythonの環境構築で詰まってしまった」という場合、Fivenine Designでは初期のデータ設計からシステム構築、ダッシュボード開発まで一貫してサポートしています。まずはお気軽にご相談ください。