はじめに
つい数か月前に引っ越してから自炊を始めたのですが、料理中は手が汚れるためレシピ本は言わずもがな、スマートフォンでレシピサイトを見るのにもなかなかに不自由します。
今回は、さくらのAIエンジンが月3,000リクエストまで無料なうえ、公式から使い切りキャンペーンなるものが開催されているため、AIを組み込んだ無料のお料理アシスタントを作りたいと思います。
https://qiita.com/official-events/bd14d28b53326d318fec
なお、本記事で紹介するアプリの完成品は以下となります。
https://github.com/pesipesi/cooking-assistant
要件
大体以下の機能をめざしつつ、その時のバイブでGeminiがコーディングしています。
- ハンズフリーでレシピを音声操作可能
- いろんな環境で気軽に動かしたかったためWebアプリとして作成
- レシピについて不明点があればAIに質問できるようにする
- タイマー機能を付ける
- レシピ登録機能では、きちんとフォーマット化されていない文章や、レシピ本の写真からAIが自動でレシピを登録してくれる
- レシピからカロリー計算
さくらのAIエンジンについて
使えるモデルとしては以下のものがあります。
https://ai.sakura.ad.jp/sakura-ai/ai-engine/#model
画像認識モデルや音声読み上げモデルもそろっているため非常にありがたいです。
AIチャット相談機能は gpt-oss、レシピ本の読み取りは画像認識可能なマルチモーダルモデルを使うといったようなことが可能です。
ブラウザ標準で読み上げ機能が付いているのであまり意味はないですが、せっかく音声読み上げモデルも使えるのでずんだもんがしゃべってくれる機能も追加します。
AIの呼び出し方法は、基本的には以下の形式でPOSTをすることで呼び出しが可能です。
https://manual.sakura.ad.jp/api/cloud/portal/?api=ai-engine-inference-api
curl -sS -X POST "https://api.ai.sakura.ad.jp/v1/chat/completions" \
-H "Authorization: Bearer {さくらのAIエンジンAPIトークン}" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-oss-120b",
"messages": [
{
"role": "user",
"content": "inference apiとは何ですか?"
}
]
}' | jq -r '.choices[0].message.content'
## Inference API とは?
**Inference API(推論 API)**は、機械学習(ML)や深層学習(DL)で学習済みモデルを**「推論(予測)だけ」**実行できるように公開した Web API のことです。
| 項目 | 内容 |
|------|------|
| **目的** | モデルの **予測(入力 → 出力)** をアプリケーションやサービスから手軽に呼び出す |
| **対象** | 画像認識、自然言語処理、音声認識、レコメンデーションなど、あらゆるタスクの学習済みモデル |
| **提供形態** | - **REST / HTTP**(JSON, multipart など)<br>- **gRPC**<br>- **GraphQL**(一部) |
| **実装場所** | - クラウド(AWS SageMaker, GCP Vertex AI, Azure ML, Hugging Face Inference Endpoints など)<br>- オンプレミス / エッジデバイス(Docker, FastAPI, TorchServe, TensorFlow Serving) |
| **主な要素** | 1. **エンドポイント URL**(リクエスト先)<br>2. **認証方式**(API Key, OAuth, JWT など)<br>3. **リクエストフォーマット**(画像は base64、テキストは文字列、バイナリは multipart など)<br>4. **レスポンスフォーマット**(予測ラベル、確率、ベクトル、画像など) |
| **メリット** | - **スケーラビリティ**:リクエスト数に応じて自動でスケール<br>- **運用負荷低減**:インフラ管理やデプロイが抽象化される<br>- **速度**:最適化されたハードウェア(GPU/TPU/FPGA)やキャッシュが利用できる<br>- **セキュリティ**:認証・レートリミットで安全に提供 |
| **デメリット** | - **レイテンシ**:ネットワーク往復が必要<br>- **コスト**:リクエスト単位や計算リソースに課金されることが多い<br>- **カスタマイズ制限**:提供側が許容する前処理・後処理しか使えない場合がある |
---
## 1. 仕組みの流れ(典型的なシナリオ)
1. **クライアント側**が HTTP POST リクエストでデータ(例:画像ファイル、テキスト)を送信
2. **API ゲートウェイ**が認証・レートリミットをチェックし、リクエストを **Inference サーバ** に転送
3. **Inference サーバ**は
- 受け取ったデータを **前処理**(リサイズ、正規化、トークナイズ)
- 学習済みモデルに **推論**(forward)を実行
- **後処理**(スコアのソフトマックス、トップ‑K 選択)
- 予測結果を JSON 形式などで返す
4. **クライアント**はレスポンスを受け取り、アプリのロジックに組み込む
この一連の流れがすべて「API 呼び出し」だけで完結するため、開発者は **モデルの実装・サーバ設定** に手を煩わせずに済みます。
画像をAIに渡す場合は、画像のURLを渡す方法と、Base64でエンコードした文字列を渡す方法があるみたいです。
試しに、AIに適当に生成させた架空のレシピサイトのイメージをBase64で読み込ませてみます

base64 shougayaki.jpeg | tr -d '\n' | jq -R '{
"model": "preview/Kimi-K2.6",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "レシピ画像を分析して材料を列挙してください"
},
{
"type": "image_url",
"image_url": {
"url": ("data:image/jpeg;base64," + .)
}
}
]
}
]
}' | curl -sS -X POST "https://api.ai.sakura.ad.jp/v1/chat/completions" \
-H "Authorization: Bearer {さくらのAIエンジンAPIトークン}" \
-H "Content-Type: application/json" \
-d @- | jq -r '.choices[0].message.content'
画像のレシピ「極み・豚の生姜焼き」の材料は以下の通りです。
**【メイン材料】(2人前)**
* 豚ロース薄切り肉(生姜焼き用)250g
* 玉ねぎ 1/2個
* 片栗粉 大さじ1
* サラダ油 大さじ1
* 千切りキャベツ 山盛り
**【黄金比の生姜ダレ】**
* 醤油 大さじ2
* 酒 大さじ2
* みりん 大さじ1
* 砂糖 小さじ1
* おろし生姜(チューブ大さじ1〜たっぷりと) ぶりと
**【添え物・トッピング】**
* マヨネーズ(お好みで)
おろし生姜のところが少し変ですが、実用的な精度でレシピの判断はできているみたいです。
材料欄には載っていないマヨネーズもレシピの中から拾ってくれています。
なお、当然ですがモデルによって精度はだいぶ変わってきます。
比較的安いモデル(preview/Qwen3-VL-30B-A3B-Instruct)だと、画像の改行などに対応できていませんでした。
はい、画像のレシピを分析して材料を列挙します。
画像に記載されている材料は以下の通りです。
### 主な材料 (INGREDIENTS)
- 豚ロース薄切り肉 (生姜焼 250g き用)
- 玉ねぎ (1/2個)
- 片栗粉 (大さじ1)
- サラダ油 (大さじ1)
- 千切りキャベツ (山盛り)
### 【黄金比の生姜ダレ】の材料
- 醤油 (大さじ2)
- 酒 (大さじ2)
- みりん (大さじ1)
- 砂糖 (小さじ1)
- おろし生姜 (チュウ 大さじ1〜たっぷり)
実装
実装は当初ContinueかClineとさくらのAIエンジンで行おうと思ったのですが、コンテキスト長の問題かあまり捗々しくいかなかったため普通にGemini(Antigravity)で行っています。
いろいろと追加の指示を出しているので完成物とは仕様が結構違いますが、一応初回のプロンプトは以下の通りです。
Antigravityの /grill-me というスキルで添削してもらいました。
初回プロンプト
# 役割と目的
あなたはJavaScript、Next.js (App Router)、SQLite、およびWeb Speech APIに精通したシニアフルスタックエンジニアです。
さくらのAIEngine(パブリックプレビューのVision/TTSモデルを含む)を活用した、スマホ対応のWeb版「料理アシスタントAIエージェント」を開発してください。
以下の詳細仕様と設計書を完全に理解し、仕様漏れや「//後で実装する」といったプレースホルダーのない、そのまま動作する完結した完全な【JavaScript】のコードを出力してください。
---
# 1. 技術スタック & 外部API仕様
- **フレームワーク:** Next.js 14+ (App Router, JavaScript)
- **スタイリング:** Tailwind CSS (スマホファースト、レスポンシブ)
- **データベース:** SQLite (`sqlite3` または `sqlite`ライブラリを使用)。VPS等の永続化環境にデプロイする想定のため、ローカルのファイル保存(`database.db`)で問題ありません。
- **さくらのAI Engine 仕様 (標準 fetch のみで実装、外部SDK不使用):**
- Base URL: 環境変数 `SAKURA_AI_BASE_URL` から取得
- テキスト/画像解析モデル: `preview/Qwen3-VL-30B-A3B-Instruct`
- 音声合成 (TTS) エンドポイント: `/v1/audio/speech`
- 音声合成モデル: `voicevox:zundamon`
---
# 2. 認証仕様(簡易パスワード方式)
- ユーザー認証は、`.env.local` に定義した単一の固定パスワード `APP_PASSWORD` を使用する簡易方式とします。
- **ログイン画面 (`/login`):**
パスワード入力フォームを配置。一致した場合、セッション管理用のクッキー(`session_auth=true`)を発行し、トップページへリダイレクト。
- **ガード:** Next.jsの `middleware.js` を実装し、未認証ユーザーが `/login` 以外にアクセスした場合は強制的に `/login`へリダイレクトさせてください。すべてのAPIルート(`/api/*`)も未認証時は401エラーを返すようにしてください。
---
# 3. データベース仕様 (SQLite)
以下のテーブル構造を持つSQLiteデータベース(`database.db`)を初期化・接続するモジュール(`src/lib/db.js`)を作成してください。
### `recipes` テーブル
- `id`: INTEGER PRIMARY KEY AUTOINCREMENT
- `title`: TEXT
- `ingredients`: TEXT (JSON文字列として保存)
- `steps`: TEXT (JSON文字列の配列として保存)
- `created_at`: DATETIME DEFAULT CURRENT_TIMESTAMP
---
# 4. フロントエンド・UI/UX仕様(スマホ最適化)
画面は以下の4つのコンポーネント/ビューで構成し、Tailwind CSSでモダンなスマホUIにしてください。
### A. 音声操作有効化コントロール(最上部に配置)
- ブラウザ(特にiOS Safari)の音声認識・再生制限を解除するため、画面上部に**「音声操作を有効化(マイク許可)」ボタン**を配置。
- 初回タップ時にダミー音声を再生してオーディオ再生ロックを解除し、同時に `SpeechRecognition`
を開始させてください。状態に応じてボタンの見た目(有効化前、認識中、停止中など)を変化させてください。
### B. レシピ作成・編集ビュー
- **新規作成:** テキスト入力とファイルアップロードボタンを配置。
- **【重要】画像圧縮処理:** アップロードされた画像はペイロード制限を回避するため、送信前にフロントエンド(Canvas
API等)で長辺1000px程度にリサイズし、JPEG圧縮してからAPI (`/api/recipes/parse`) に送信する処理を必ず実装してください。
- **既存編集機能:**
登録済みのレシピ詳細画面に「編集」ボタンを設置。タイトル、材料、手順をテキストとして修正でき、`/api/recipes/update`
でSQLiteを更新する機能。
### C. 調理・閲覧ビュー(最重要)
- スマホ画面いっぱいに「現在のステップ」を1件だけ大きく表示する「調理モード」。
-
現在選択されている手順(インデックス)を保持し、画面のタップ(進む/戻るボタン)および後述の音声コマンドで操作可能にしてください
。
### D. アシスタント・チャットUI
- 画面下部から引き出し式、またはフローティングで開閉するチャット窓。
-
質問を送信する際、現在表示中のレシピのタイトル・材料・全手順を「コンテキスト」としてバックエンドに自動で付与して送信してください
。
---
# 5. 音声操作仕様(ハイブリッド型制御ロジックとSafari対策)
フロントエンドにおいて、`Web Speech API` を `continuous = true`
で起動し、認識されたテキストに対してハイブリッド型判定を実装してください(デバウンス処理必須)。
- **【Safari対策】:** 無音で自動停止した場合に備え、`onend`
イベント内で再起動(`start()`)を試みるロジックを実装してください。もし完全に停止した場合は、上記の「音声操作コントロール(A)」
でユーザーに停止を知らせ、タップで手動再開できるフォールバックUIを構成してください。
### 【ルール1:普段の固定命令(ウェイクワード不要)】
ウェイクワードの有無に関わらず、即座にフロントエンド側でアクションを実行してください。
1. **「次」 / 「すすんで」** -> レシピ手順を1つ進める。
2. **「前」 / 「もどって」** -> レシピ手順を1つ戻す。
3. **「最初から」** -> 手順をインデックス0に戻す。
4. **「読んで」 / 「おしえて」** -> 現在表示している手順のテキストをTTSで読み上げる。
5. **「ストップ」 / 「とめて」** -> 再生中のTTS音声を即座に一時停止(`audio.pause()`)する。
### 【ルール2:AI使用時の質問(ウェイクワード必須)】
上記にヒットせず、かつ文頭や文中に **「さくら」** が含まれる場合のみ、`/api/parse-intent`
を叩きます。(「さくら」が含まれない雑音は無視)。
---
# 6. バックエンドAPIエンドポイント仕様
1. **`POST /api/recipes/parse` (レシピ構造化)**
- 引数: テキスト、または画像データ(Base64)
- 処理: Qwen3-VLモデルに投げ、指定のJSONフォーマット `{ "title": "...", "ingredients": [], "steps": [] }` で返却させる。
2. **`POST /api/recipes/update` (更新)**
- 引数: `id`, `title`, `ingredients`, `steps`
- 処理: 対象レコードのUPDATE。
3. **`POST /api/parse-intent` (インテント解析)**
- 処理: 現在の全手順リストと発言をAIに送り、`{ "action": "MOVE" | "CHAT", "target_step": number | null, "reply_text":
string | null }` のJSONで返答させる。`MOVE` なら画面をスクロール、`CHAT` なら返答テキストをTTSで読み上げる。
4. **`POST /api/chat` (文脈付きチャット)**
- 処理: 表示中レシピの全コンテキストを含めたシステムプロンプトを与えて返答を生成。
5. **`POST /api/tts` (音声合成)**
- 処理: さくらTTS APIの音声をフロントに返すプロキシ。
---
# 7. セキュリティ & APIキーのモジュール化 (シークレットマネージャ対応)
さくらのAI Engineへの通信処理やキー取得は、`src/lib/sakuraClient.js` にカプセル化してください。外部SDKは使用せず、標準の
`fetch` を用いてください。
**【キー取得ロジック】:**
環境変数 `SECRET_MANAGER_ENABLED=true`
の場合は、以下のさくらのクラウドAPI仕様に従って動的にAPIキーを取得してください。それ以外の場合は `.env.local` の
`SAKURA_AI_API_KEY` を直接使用してください。
- **取得API**: `POST https://secure.sakura.ad.jp/cloud/zone/{SAKURA_CLOUD_ZONE}/api/cloud/1.
1/secretmanager/vaults/{SECRET_MANAGER_VAULT_ID}/secrets/unveil`
- **認証ヘッダー**: `Authorization: Basic {Base64(SAKURA_CLOUD_ACCESS_TOKEN:SAKURA_CLOUD_ACCESS_TOKEN_SECRET)}`
- **リクエストボディ**: `{ "Secret": { "Name": "{SECRET_MANAGER_SECRET_NAME}" } }`
- **レスポンス仕様**: `{ "Secret": { "Value": "ここにAPIキーが入る" } }`
自動AIレビューボット
本筋とは外れますが、せっかく無料でAIのAPIが使えるのでアプリのコードレビューを自動化させます。
プルリクを出すとCIでAIボットがコード差分をレビューしてくれるものです。
今回はGiteaというgithubライクなOSSで導入していますが、おそらくgithubでもワークフローを設定すれば同じように動かせると思います。

アプリ紹介
以下、最終的に完成したアプリのデモとなります。
レシピ登録
テキスト入力
雑然としたテキストをAIがいい感じに解釈して、いい感じにレシピ化してくれます。
プリンのレシピ(AI製)
まずは小鍋を用意して、砂糖(大さじ3くらい)と水(大さじ1くらい)を放り込んで中火にかけます。
かき混ぜずに見守り、茶色く焦げて香ばしい匂いがしてきたら火を止めます。
すかさずお湯(大さじ1くらい)をジュワッと入れて(跳ねるので注意!)、熱いうちにプリンカップ3〜4個の底に等分に流し込んでおきます。
次にボウルを出して卵(2〜3個)を割り入れます。固めが好きなら3個にしてください。
そこに砂糖(大さじ3〜4くらい)をバサッと加え、白身を切るように、泡立てないようにシャリシャリ感がなくなるまで混ぜます。
ここで牛乳(300mlくらい/コップ約1杯半)を電子レンジなどで人肌に温めます。
温まった牛乳を、先ほどの卵のボウルに少しずつ注ぎながら混ぜ合わせます。
香りが欲しければ、ここでバニラエッセンス(数滴)を振り入れます。これでプリン液は完成です。
茶こしやザルを用意して、プリン液を濾しながら先ほどのカラメル入りカップに注ぎます(面倒ならそのままドバッと注いでも味は同じです)。
深めのフライパン(または鍋)にカップを並べ、カップの1/3〜半分の高さになるようにフライパンにお湯を張ります。
フライパンに蓋をして火にかけ、お湯が沸騰したらすかさず「ごく弱火」にして10〜15分ほど蒸します。
表面がフルフルと固まっていたら火を止め、そのまま蓋を開けずに10分放置して余熱で中まで火を通します。
粗熱が取れたら冷蔵庫に突っ込んで、しっかり冷やせば完成です。
画像登録
レシピ本やレシピサイトの画像をアップロードすると、AIがそれを解析してレシピに起こしてくれます。
今回は架空のレシピサイト風イメージをAIに生成させてデモを行っています。
ただ、さくらのAIエンジンは入力データを学習には利用しないとのことですので、個人利用の範疇であればレシピ本などからどしどし登録しても問題ないはずです。
レシピ閲覧
登録されたレシピはタグや料理名、材料名からあいまい検索できるようにしています。
個別のレシピを開くと、編集と削除することが可能です。
基本は画面上部に材料、画面下部に手順が表示されているだけですが、こまごまとした追加機能が付いています。
音声入力
今回一番実装したかった機能です。
画面上部から音声操作を有効化にすると、音声コマンドを実行できるようになります。
様々なコマンドを用意しており、ハンズフリーでページめくりをおこなったり、文章を読み上げさせることができます。
AI機能
次の三つの機能が備わっています。
-
AI読み上げ
ずんだもんが手順を読み上げます。
ブラウザ標準の音声読み上げと比較して、漢字の読み仮名はわりと正確に読んでくれているような気がします。
さくらのAIエンジンだと、音声読み上げモデルは月間50リクエストのみなので、ここぞという場面だけずんだもんを呼び出しましょう
なお、音声読み上げモデルは個別に規約への同意をしないと呼び出せないため要注意です。

-
AIレシピ
「AIレシピ」と話しかけるか、画面右下のチャットアイコンから呼び出せます。
今開いているレシピについての質問を投げかけることでAIが回答してくれます。
「ブロッコリーってどう切るの?」「小さじ1杯って何グラム?」「適量っていったいなんすか?」という料理への素朴な疑問を投げかけていきましょう。

-
AIタイマー
「AIタイマー」と話しかけた後に、「パスタを7分茹でる」といった形で時間を伝えるとAIが内容を判断してタイマーをセットしてくれます。
時間が0になると音声付きで通知もしてくれます。
複数タイマーを設定可能かつ、各タイマーにタイトルが付くため、どれがどのタイマーか見分けがつくようになっています。
モデルにもよると思いますが、試しに「ゆで卵を半熟で」という指示だと大体6~7分、「固ゆで卵で」という指示だと9分を自動でセットしてくれました。

所感
- レシピ登録、AIチャット、タイマーセットの場面でAIリクエストを行いますが、さくらのAIエンジンは月間3,000リクエスト、1日当たり100リクエスト程度まで無料とのことなので、もっとAIを使い倒せるような機能を考えたいです。
- 複数環境に対応するためWebアプリとして実装しましたが、どうもスマートフォンはブラウザの音声認識周りがかなり不安定のようです。アンドロイド向けネイティブアプリとして開発したほうがよかったかもしれません。
- 画像認識でのレシピ登録は思った以上の精度で文字起こしをしてくれました。とはいえところどころ漢字の読み間違いなども含まれていたため、今後のモデルの精度向上に期待です。
- 正直、なんとなくで追加したGitのAIレビューボットがかなり便利なので、これだけで月間3,000リクエスト消化できるポテンシャルがある気はしています。







