写真を送ると映っている食材を分析してレシピを返事するAIボットを考えてみた。

投稿者: | 2026-04-11

皆様こんにちは!

「冷蔵庫に何があるかはわかるけど、何を作ればいいかわからない」——そんな日常のあるあるを解決するため、食材の写真を送るだけでレシピを提案してくれるLINE Bot「Recipon(レシぽん)」を考えて作ってみました。

🍳 Recipon とは?

Recipon は LINE に食材の写真を送ると、Claude AI が食材を認識してレシピを提案してくれる Bot です。

  • 冷蔵庫や棚の食材をスマホで撮って LINE に送るだけ
  • Claude Vision API が写真から食材を自動認識
  • 撮影した食材だけで作れる2〜3品のレシピを返信
  • 「今夜何作ろう」が1枚の写真で解決

🔄 システムの仕組み・フロー

LINE Webhook のタイムアウト(30秒)と Claude API の処理時間(最大60秒)の制約があるため、2段階の非同期処理で設計しました。

【ユーザーが写真を送信】
        ↓
┌──────────────────────────────────┐
│  webhook.php(30秒以内に完了)    │
│  1. LINE署名(HMAC-SHA256)検証   │
│  2. 利用回数チェック(1日5回制限)│
│  3. LINE Content APIから画像取得  │
│  4. queue/ ディレクトリに保存    │
│  5. 「受け付けました🍳」即返信   │
│  6. HTTP 200 を即返却            │
└──────────────────────────────────┘
        ↓(cron で1分ごとに起動)
┌──────────────────────────────────┐
│  worker.php(非同期バッチ処理)   │
│  1. キューからジョブ取得          │
│  2. 画像を1000pxにリサイズ(GD) │
│  3. base64エンコード              │
│  4. Claude Vision API に送信     │
│  5. レシピ生成(最大60秒)       │
│  6. LINE Push API でレシピ送信   │
└──────────────────────────────────┘
        ↓
【1〜2分後にユーザーがレシピを受け取る】

💻 技術スタック

区分 技術・サービス
バックエンド PHP 7.4+、Apache(共有サーバー)
画像処理 PHP GD Library(リサイズ・圧縮)
AI Anthropic Claude Vision API(claude-opus-4-6)
メッセージング LINE Messaging API(Reply / Push / Content)
キュー管理 ファイルベース(queue/ ディレクトリ)
レートリミット ファイルベース(日付+UserIDのファイル名)

📂 ファイル構成

recipon/
├── .env                # APIキー・設定(パーミッション600)
├── .htaccess           # 外部アクセス制御
├── config.php          # .env読み込み・定数定義
├── webhook.php         # LINEのWebhook受信
├── worker.php          # cronで動くバッチ処理
├── queue/              # ジョブキュー(画像+JSONペア)
└── ratelimit/          # 利用回数カウントファイル

📸 LINE 写真 OCR の詳細

webhook.php では受け取った画像を即座にキューに保存し、レスポンスを返します:

// webhook.php - 画像メッセージ受信
if ($event['message']['type'] === 'image') {
    $messageId = $event['message']['id'];

    // LINE Content API から画像取得(10秒タイムアウト)
    $imageData = fetchLineImage($messageId, $channelToken);

    // queue/ に保存
    $ts        = time();
    $imgPath   = "queue/img_{$ts}_{$messageId}.jpg";
    $jobPath   = "queue/job_{$ts}_{$messageId}.json";
    file_put_contents($imgPath, $imageData);
    file_put_contents($jobPath, json_encode([
        'line_user_id' => $userId,
        'image_path'   => basename($imgPath),
        'created_at'   => date('Y-m-d H:i:s'),
    ]));

    // 即座に返信して200を返す
    replyMessage($replyToken, "📥 食材を受け付けました!少々お待ちください🍳", $channelToken);
    http_response_code(200);
    exit;
}

worker.php では Claude Vision API で食材認識とレシピ生成を行います:

// worker.php - Claude Vision API でレシピ生成
$imageB64 = base64_encode(file_get_contents($job['image_path']));

$payload = [
    'model'      => 'claude-opus-4-6',
    'max_tokens' => 1500,
    'system'     => implode("\\n", [
        'あなたはレシピ提案AIです。',
        '・Markdownや記号は使わず、絵文字で読みやすくしてください。',
        '・写真に写っている食材と基本調味料だけで作れるレシピを2〜3品提案してください。',
        '・各レシピに材料・手順(5ステップ以内)・調理時間を含めてください。',
        '・出力は日本語で、4900文字以内にしてください。',
    ]),
    'messages' => [[
        'role'    => 'user',
        'content' => [
            ['type' => 'image', 'source' => [
                'type'       => 'base64',
                'media_type' => 'image/jpeg',
                'data'       => $imageB64,
            ]],
            ['type' => 'text', 'text' => 'この食材でレシピを提案してください。'],
        ],
    ]],
];

// Claude API 呼び出し(60秒タイムアウト)
$response = callClaudeApi($payload);

// LINE Push API でレシピ送信
pushMessage($job['line_user_id'], $response, $channelToken);

😅 苦労した点・試行錯誤

問題①:LINE Webhook のタイムアウト

最初は webhook.php の中でそのまま Claude API を呼ぼうとしました。しかし LINE Webhook の応答タイムアウトは30秒、一方 Claude API の画像処理には最大60秒かかることがあります。タイムアウトするとLINE側で再送が発生し、同じリクエストが2回処理される問題も。

解決策:webhook は「受け付けました」とすぐ返信してキューに保存するだけにして、重い処理はcronで動く worker.php に分離しました。

問題②:データベースなしでレートリミット

共有サーバーでデータベースを使わずに「1日5回まで」の制限を実装する必要がありました。

解決策:ファイル名を {YYYY-MM-DD}_{userId}.txt にすることで、日付が変わるだけで自動リセット。ファイルの中身はカウント数のみのシンプルな設計です。

// レートリミットのチェック
$rateFile = "ratelimit/" . date('Y-m-d') . "_{$userId}.txt";
$count    = file_exists($rateFile) ? (int)file_get_contents($rateFile) : 0;

if ($count >= DAILY_LIMIT) {
    replyMessage($replyToken, "本日の利用上限(" . DAILY_LIMIT . "回)に達しました。また明日どうぞ😊", $channelToken);
    exit;
}
file_put_contents($rateFile, $count + 1);

問題③:画像サイズとAPIコスト

元のスマホ写真をそのまま送ると画像トークン数が増えてAPIコストが跳ね上がります。GD Library で最大1000px・JPEG 85% に圧縮することで、1リクエストあたり約1,600トークンに抑えられました。

問題④:worker.php の多重起動

cronが1分ごとに起動するため、前の処理が終わっていない場合に多重実行されるリスクがあります。ロックファイル(90秒タイムアウト)で排他制御しています。

🤖 ClaudeCodeで作った感想

Recipon を作る際も ClaudeCode と一緒に設計しました。特に良かったのは:

  • 設計の矛盾を指摘してくれる:「LINE のタイムアウト制約があるのでキュー設計が必要です」と自分から提案してくれた
  • セキュリティを自動的に組み込む:.htaccess によるディレクトリ保護、HMAC-SHA256 検証、パーミッション設定など
  • コスト試算まで考えてくれる:APIの利用コストをトークン数から概算して、レートリミットの設計根拠を示してくれた

✅ まとめ

「冷蔵庫の写真を送るだけでレシピが来る」というシンプルなアイデアですが、LINE の制約・AIのコスト・共有サーバーの制限など、実装には意外と考慮点が多くありました。それでも ClaudeCode のサポートで設計から実装まで一気に進めることができました。

日常のちょっとした「困った」をAIで解決するのは、これからも続けていきたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です