最適化計算にLLMを使うときのベストプラクティス
はじめに
在庫の発注量や配送の順番、シフトの割り当てといった最適化に、LLMを使いたいという相談が増えています。現場の要望が自然言語で来る以上、どこかでLLMを挟むこと自体は自然な発想です。
うまくいくかどうかは、LLMをどの工程に置くかでほぼ決まります。要望を式に翻訳させるのと、解そのものを考えさせるのとでは、同じ「LLMを使う」でも結果がまるで違います。前者は翻訳の誤りを人が読んで直せますが、後者は正しく見える誤りが通り続けます。
置き方は大きく3つあります。それぞれの向き不向きと、どこに置いても共通して要る作法を整理します。
対象読者:
- 在庫・配分・スケジュールの最適化に、LLMやコーディングエージェントを組み込もうとしている方
- 組み込んでみたが、出てきた答えを採用してよいか判断できずにいる方
- 既存のソルバーがある業務に、自然言語の入口を足したい方
記事のポイント:
- LLMを置ける3つの工程を並べ、どれを選ぶかの基準を示します
- 定式化を任せるときの作法、コードを毎回書かせるかの判断、検算のやり方を順に整理します
LLMを置ける工程は3つある
同じ要望から同じ解に至るまでに、LLMを差し込める場所は3か所です。

上の構成には、探索の工程がありません。LLMが推論で解を1つ組み立てて、それがそのまま答えになります。中と下は探索をソルバーに任せていて、違いはコードが毎回変わるかどうかです。
実務で最初に検討すべきは下の構成です。中の構成は問題の形が案件ごとに変わるときに効きます。上の構成は、制約が数本を超える問題には向きません。探索していないので、条件を全部満たしているかどうかが運任せになります。
要望1本と制約1本を対応づける
下の構成を選んだとして、LLMの持ち場は要望を目的関数と制約に翻訳するところです。ここでの作法がひとつあります。
翻訳の中身自体は込み入っていません。「金曜は熟練者を2人以上入れたい」なら、金曜の枠に入る人のうち熟練者フラグが立っている人数の和が2以上、という不等式が1本で済みます。「同じ人が3日連続で夜勤に入らないようにしたい」なら、連続する3日ぶんの夜勤変数の和が2以下という不等式を、人数×日数だけ並べます。
ここで保ちたいのは、要望1本が不等式1本か、同じ形の不等式の集まりに対応している状態です。この対応が崩れていなければ、LLMが何を書いたかを人が読んで確かめられます。崩れると、生成された定式化を読む作業が、要望を最初から式にする作業と同じ手間になります。
制約を1本書き落としても、ソルバーは残りの制約を満たす最適解を返します。答えは正しく見えます。だから制約1本ずつに、それがどの要望から来たかを紐づけて残します。
実行不能を握りつぶさない
要望どうしが矛盾していると、ソルバーは実行不能を返します。ここを例外として握りつぶし、制約を勝手に緩めて解を出す実装をときどき見かけますが、これはいちばんもったいない使い方です。
人が手で組んでいたころは、誰かが黙って優先順位をつけていた部分です。実行不能はその衝突が表に出た瞬間なので、どの要望とどの要望が両立しないかを、名前で返せるようにしておきます。矛盾する制約の最小の組み合わせを取り出す機能は多くのソルバーが持っていて、それを要望の名前に翻訳し直せば、現場に返せる形になります。
毎回コードを書かせるか、書き切ったものを渡すか
コーディングエージェントが使えるようになって、中の構成が現実的になりました。要望を受け取ったエージェントが最適化スクリプトを自分で書き、実行して解を返します。探索はソルバーがやるので、推論で組み立てるのとは質が違います。定式化を人が書く手間も省けます。
ただし毎回同じ最適化を回すなら、そのモジュールは一度作り切ってレビューし、固定したものを呼ばせるほうがよいと考えています。生成のたびにコードが変わる余地を残す理由がありません。同じ入力に同じ答えが返らなくなりますし、毎回書かせるということは毎回レビューが要るということでもあります。
問題の形が案件ごとに変わるなら、中間の置き方があります。既存のソースコードを渡して、変わった部分だけ直させたうえで実行させる形です。差分を読めばよいので、ゼロから書かせるより確認する量が減ります。目的関数と制約の骨格は人が持ったまま、係数やデータの取り回しだけを任せる、という切り分けになります。
判断の目安は実行の頻度です。月に数回で毎回条件が違うなら毎回書かせてよく、日次で同じ計算を回すなら固定します。
出た解は必ず検算する
どの構成を選んでも、最後に検算を挟みます。制約を満たしているかは答えを見れば確かめられますし、最適からどれだけ離れているかもソルバーが返す双対ギャップで分かります。
検算を作るときに気づいてほしいことがあります。制約を満たしているかを検査する仕組みを作れるなら、その検査器は制約を式で持っています。持っているなら、同じ式をソルバーに渡して解かせることもできます。検算のために書いた条件を、そのまま定式化に使い回すのが素直な流れです。検査だけ作って解くのは推論に任せる、という構成になっていたら、労力の配分が逆になっています。
線形なら線形計画、整数が混じるなら混合整数計画、組み合わせが爆発するならメタヒューリスティクスと、式が書けたあとは既製の道具を選ぶだけです。名前のついた問題になっていることも多く、どの車がどの順に回るかは配送計画問題や巡回セールスマン問題、誰をどの枠に入れるかは割当問題、需要が不確実ななかで仕入れ数を決めるのは新聞売り子問題です。
LLMを挟まないほうがよい場合
置き場所の前に、そもそも挟むかどうかを確かめたほうがよい場合があります。相談を受けたときに訊くのは次のあたりです。
- 間違えたとき、取り消せるか
- 同じ入力に同じ答えが返る必要があるか
- なぜその答えになったかを、相手に説明する義務があるか
この3つは、式が書けるかどうかとは別の軸です。取り消せるかどうかは誤りの結果の話で、検算できるかとは独立しています。制約充足を完全に検算できても、送金や発注そのものを取り消せるわけではありません。
同じ入力に同じ答えが返るかは、監査や規制のある業務で効いてきます。去年の判断を、いまもう一度再現できるか。ソルバーは同じ入力に同じ答えを返しますが、その手前にLLMを挟むと、翻訳の結果が揺れる余地が残ります。定式化を生成のたびに作らず、確定したものを保存して使い回すのは、ここへの対処でもあります。
説明義務は式で書けても消えません。むしろ説明できる形にするために、精度で劣る単純なモデルをあえて選ぶことがあります。信用リスクの領域では珍しくない判断です。
まとめ
最適化にLLMを使うかどうかより、どの工程に置くかのほうが結果を左右します。解そのものを推論で組み立てさせると、条件を全部満たしているかどうかが確かめられないまま答えが出てきます。探索をソルバーに渡せば、制約を満たすことは解の定義に入ります。
置き場所を決めたあとの作法は3つあります。要望1本と制約1本の対応を保ち、生成された定式化を人が読める状態にしておきます。毎回同じ計算なら、モジュールを作り切ってレビューし、固定したものを呼ばせます。そして出た解を、制約充足と最適性ギャップで検算します。
いま手元の問題に取りかかるなら、まず要望を1つ選んで、それが不等式1本になるかを紙の上で確かめてみてください。なるなら、その調子で並べていけば定式化が揃います。ならない要望が出てきたら、そこは言葉のまま残しておいて、翻訳をLLMに任せる部分の候補にします。境界をどこに置くかを決めるのが、この設計でいちばん時間をかける価値のあるところです。