レシピ提案AIを作りたかった
何の変哲もないこの願望が、今回のことの始まりである。
というのも、筆者は大の料理好きで毎日自炊している。そして最近のお気に入りツールであるObsidianにレシピをメモしている。せっかくこうして書き溜めたレシピがあるのならば、「今日作れるレシピ」を自動でレコメンドして献立を提示してくれるツールを作ってみようと思い立った。(ちなみにソースコードはこちら)
構成はいたって単純で、準備したのは以下。
- 食材の情報を含むノートとレシピを記載したノート:Markdown形式で作成し、メタデータを付加
- Claude Code:スクリプトを作成、自然言語でレシピを提案
- macOSとPython:ここは動けば何でもいいと思う
食材ノート
食材ノートに含む情報としては「在庫があるか」「何個あるか」「いつまでに使い切るべきか」を選択。例えば「しめじ」というタイトルのノートであれば、フロントマターは以下のように記載した。
---
tags:
- In-Stock # 在庫なしだとOut-of-Stock
counts: 2 # 使用できる回数
exp_date: 2026-08-15 # 消費期限
---
counts に使用可能な回数を入れておくことで、使い切ったタイミングで献立提案を打ち切れるようにしている。
レシピノート
レシピ側はもう少し複雑にした。ただ材料を並べるだけだと「これがないと成立しない食材」と「あってもなくても大差ない食材」が区別できず、 「調理可能なレシピの候補」が極端に少なくなってしまう と考えたからだ。
例えばかき玉スープを作る際、具は白菜でもキャベツでも何でもいいわけだが、卵がなければそれはもう別の料理になってしまう。これを踏まえて、システムには「もし卵の在庫があるなら、手持ちの食材が白菜・キャベツどちらの場合でも、かき玉スープをレコメンド」してほしい。
そんなわけで、材料を役割ごとに分けて書いた。例えばかき玉スープのレシピは以下のように書ける。(あくまで例なので簡素化している。また、この食材管理方法についてはこちらのサイトを参考にさせていただいた)
---
tags:
- Recipe
- あっさり
main: # これがないと味が変わるレベルの必須食材
- "[[卵]]"
recommend: # レシピ内で使われているが、代替もできる食材
- "[[白菜]]"
seasonings: # 調味料
- "[[鶏がらスープ]]"
- "[[ごま油]]"
- "[[片栗粉]]"
sub: # 代替として使用できる食材の候補
- "[[キャベツ]]"
---
さらに、系統や状況に応じたタグも付けた。例えば、
-
冷・温:冷たい系・温かい系 -
スタミナ:ネギや豚肉を使った料理 -
Tried・Never:作ったことがあるかないか
といった具合だ。これは、後述するスコアリングで作りたいレシピの系統も指定するためだ。
スコアリングロジック
在庫食材とレシピを突き合わせて、優先順位付きのタプルをソートキーにしてランキングする。
sort_key = (in_stock_ratio, expiry_urgency, common_score, protein_score, tried_never_score, summer_score)
考慮したのは以下の要素。優先したい順に示すと、
-
in_stock_ratio: mainとseasoningsのうち在庫のある食材でカバーできる割合。高いほど高スコア -
expiry_urgency: 使う食材の中に消費期限が近いものがあるほど高スコア -
common_score: 不足食材が少ない、追加購入の必要がないほど高スコア -
tried_never_score: 作ったことがないレシピを優先 -
summer_score: 夏季限定で、冷やスタミナタグのついたレシピを優先
食材が在庫切れでも、代替食材が登録されていればそれで賄えたことにする仕組みも入れた。
SUBSTITUTABLE_MATERIALS: dict[str, list[str]] = {
"きのこ": ["えのき", "しめじ"],
}
結構網羅的にルールを組んだつもりだ。自分の考えは記述できた、これで「今日何作ろう」問題はみごとに解決…するはずだった。
欲しいメニューが出てこない
早速スクリプトを走らせてみた。
…なんかしっくりこない。どうにも、レコメンドされるレシピを食べたいと思えない。
一応自分が信頼できる(と思っている)料理研究家の方のレシピを厳選した上でメモを取っているので、レシピが悪いということはないはず。
最初はデータの粒度が粗いのかと思い、main/recommend/seasoningsの切り分けをやり直したり、#あっさり系や#がっつり系といったレシピのタグを追加してみたりした。それでもズレは埋まらなかった。
そこで、ロジックを疑う前に「自分が今日欲しいメニューは何か」を素直に書き出してみることにした。
そうして2つのことに気づく。
気づき① 欲しいものは、そもそも在庫だけで完結していなかった
書き出したメニューを見返すと、たしかに在庫食材を使ってはいるのだが、足りない材料も混じっていた。つまり、自分が食べたい、システムにレコメンドしてほしいと思っていたのは「厳密には今の在庫だけでは作れないメニュー」だったということになる。
一方レコメンドシステムだと、不足食材が多いレシピはcommon_scoreで確実に順位を落とす。だから私が「食べたい」と思っていたメニューは高得点を取れず、レコメンドされなかった。
システムが私の食べたいメニューを提案し、献立を組んでくれないのは、システム上の不具合ではなく、自分の欲求がそもそも「在庫の範囲内」という制約に従っていなかったからだった。
気づき② 「冷や汁を食べたい」場面でアクアパッツァが出てきた理由
もう一つ具体的な例として「冷や汁を食べたいのにアクアパッツァばかり提案してきた」ことを挙げたい。筆者はサバを使った冷たい料理である冷や汁が食べたいと思い立ち、冷や汁のレシピノートを以下のように作成していた。なお、サバ缶は購入済みで、食材ノートにもそれを反映している。
---
tags:
- Recipe
- 冷
- Never
main:
- "[[サバ]]"
recommend:
- "[[きゅうり]]"
seasonings:
- "[[味噌]]"
- "[[醤油]]"
- "[[みりん]]"
- "[[白だし]]"
- "[[ごま油]]"
sub:
---
実を言うと、この時点できゅうりは購入していない(在庫がない)。でもたった1つの食材の在庫がなかったくらいでそこまで点数が落ちるとは思えない。それに、冷やNeverなど加点要素も含んでいる。これならシステムは冷や汁をレコメンドしてくれるだろう…そう思っていた。
しかしいざスクリプトを実行してみると、システムが提案したのは冷や汁ではなく「和風アクアパッツァ」だった。そのレシピは以下の通り。(ちなみに参考にした冷や汁・アクアパッツァのリンクも追加しときます)
---
tags:
- Recipe
- Never
- 温
main:
- "[[サバ]]"
recommend:
- "[[きのこ]]"
seasonings:
- "[[オリーブオイル]]"
- "[[にんにく]]"
- "[[白だし]]"
- "[[酒]]"
sub:
- "[[めんつゆ]]"
---
そういえばきのこの在庫がある。 その食材ノートを見ると、exp_date: 2026-08-15。これはスコアリング時点からわずか数日後の期限だった。
食材の在庫があり、その消費期限も近い。購入済みで、消費期限の迫っている食材を含むレシピの消費を、冷などのタグより優先する方針をとっていることを考えると、このシステムがアクアパッツァを優先するのは必然だった。
つまりアクアパッツァは「メイン食材(サバ)を消費しつつ、期限が近い在庫(きのこ)も同時に片付けられる」という、在庫管理の観点では文句なしに正しい選択だった。私は冷や汁を食べるためにサバ缶を買ったのであって、在庫の効率的に消化したかったわけではなかったのに…。
効率的だが無感情な人生 vs 非効率的だが楽しい人生
レコメンドシステムは、私が与えた目的関数(在庫消化・期限管理など)に忠実に、効率的な答えを返してくれた。在庫のある食材を、消費期限までに食べ切る。ロジックとしては何も間違っていない。
ただ、その提案に沿ったご飯を食べたいかと聞かれると、正直微妙だった。だってそんなに急いできのこ消費しなくても良くない?悪くなりそうなら冷凍しとけばいいじゃん。(ちなみに先ほどのシステムできのこの消費期限を1ヶ月延ばしても結果は変わらなかった)
ここで気づいたのは、数理的な知識や情報技術により組み立てられた効率的な生活が、必ずしも豊かで楽しい生活を意味するわけではないということだ。
在庫の消化率や期限管理を目的関数に据えた瞬間、システムはその目的関数を最適化する解決策を導く。特に私のように「選択の余地は残しているけど、食べたいものの候補はある程度決まっている」人間にとっては、その選択肢以外をレコメンドされてもあまりいい気持ちにはならない。
もちろんこんなふうに提案された献立にケチをつけようものなら「ご飯なんでもいいって言ったくせに文句を言うな!」ということになる(私は絶対やりません)。それに、基本的に食材はやたらと購入せず、廃棄する前に使い切ったほうが断然良い。それでも、いくらなんでもそんなに最速で在庫消化しなくても…とは思う。
効率を優先して作られた解決策が、必ずしも理想の結果を生み出すわけではない。それでも効率的なら正しいんだと信じて、元々持っていた感情や欲求を完全に無視してシステムに従うか、それとも在庫と消費期限を多少無視しても好きなものを食べて楽しく暮らすか。少なくとも今の私には、感情を完璧に排斥するには少し時間がかかりそうだな…と思った。

