Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Taking the Language Model Out of a Build Planner

2026年10月02日に「Dev.to」が公開したITニュース「Taking the Language Model Out of a Build Planner」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

アプリ自動生成ツールで、設計計画にAI(言語モデル)を使う効果を検証した。AIを外してもアプリ生成の精度は変わらず、むしろAIが下位の自動判断を妨げることが示された。上位システムがデフォルト値を設定すると、下位システムの自律的な処理を抑制する影響がある。

ITニュース解説

この解説では、ユーザーが入力した文章からアプリケーションを自動生成するシステムにおいて、その計画部分に言語モデル(AIの一種)が本当に必要かどうかを検証した実験について説明する。

まず、このシステムの中心にあるのは「appgen」というツールだ。appgenは、「優先順位、コメント、検索、チケットのクローズ機能を持つサポートデスクシステム」といった文章を入力すると、その内容に基づいてPythonのアプリケーションコードを自動で生成し、実行する。このappgen自体は言語モデルを一切使わず、依存関係のない単一のPythonファイルを生成し、プライベートポートで起動し、要求されたすべての機能をHTTP経由で検証した後でユーザーに提示する。

appgenを囲むように、「olo」というより大きなシステムが存在する。oloには「プランナー」と呼ばれるコンポーネントがあり、これがユーザーの文章を「ビルドプラン」と呼ばれる具体的な計画に変換する役割を担う。ビルドプランは、プログラムの種類、使用言語、アプリケーションの形状、アプリケーションの主題となる名詞など、7つのフィールドから構成され、そのうち6つは特定の選択肢の中から選ばれる。残りの1つはユーザーの要求文そのままだ。

このプランナーこそが、このアーキテクチャの中で唯一、ニューラルネットワーク(言語モデル)を使用している「負荷を支える」部分だった。言語モデルが通常担うとされる少なくとも6つの役割のうち、このシステムでは5つの役割について、ニューラルネットワークではない代替手段が既に存在していた。残る1つ、つまり漠然とした要求からプログラムの提案を行う役割だけが言語モデルに頼っていたのだ。今回の実験は、このプランナーが本当に価値を提供しているのかどうかを検証するために行われた。具体的には、この言語モデルのプランナーを取り除いた場合に何が起こるかを測定したのである。

実験では、「florinpop17/app-ideas」というリポジトリから88件のアプリケーションアイデアの要求文を使用した。プランナー以降のコードは一切変更せず、言語モデルのプランナーがある場合と、プランナーの席を空にした場合(何も入れない場合)とを比較した。結果は驚くべきものだった。言語モデルのプランナーを空席にしても、システムの全体的な性能(生成できたアプリケーションの数や精度)は低下しなかった。むしろ、一部のケースでは空席の方が成績が良かった。この結果は、評価方法を4通りに変えても変わらなかった。

なぜこれまでこの検証が行われてこなかったのかというと、過去の実験ではプランナーとoloスタック全体を比較したり、改修後のスタックと比較したりと、いずれもプランナーが存在する状態での比較が行われていたため、プランナー単体の価値を測ることができていなかった。oloはappgenとは異なり、ルーター、プランの検証機能、エンティティの読み戻し、appgenのコマンドラインを組み立てるコードなど、プランナー以外の多くの要素を含んでいる。そのため、もしプランナーに何らかの利点が見出されたとしても、それがプランナー単独の貢献なのか、それともこれらの追加コンポーネントと合わせての貢献なのかを区別することは難しかった。

今回の実験の鍵となった変更は、oloがプランナーの席を空にした状態で動作できるようにする「継ぎ目」を設けたことだ。具体的には、「--planner defer」というオプションを導入し、これによりプランナーはすべてのフィールドの決定を拒否し、ユーザーの要求文をそのまま後続の処理に渡す。また、「--plan PATH」オプションを使えば、外部で作成されたプランをファイルから読み込むことも可能にした。これらのオプションは、言語モデルが生成したプランと同様に、同じ検証プロセスとコマンドライン組み立てプロセスを通るようにした。

最も重要な変更は、コマンドラインを組み立てる部分でのたった一つの分岐だった。プランナーが拒否したフィールドは、appgenのデフォルト値で埋めるのではなく、コマンドラインから完全に「削除」されるようにしたのだ。これは単なる整理整頓ではない。appgenは、プログラムの種類や使用言語に関するフラグがコマンドラインに存在しない場合にのみ、要求文を解析してこれらを判断する。例えば、「--kind web」というフラグが存在すると、それは「デフォルト値のwebを指定している」のではなく、appgenが独自に解析する機能を「抑制する」働きをしてしまう。もし、プランナーが空席の場合に「web」を親切に補完してしまうようなバージョンを使っていたら、それはappgenの内部ルーターが無効化された状態でのappgenを測定していたことになり、誤った結果を招く可能性があった。この重要な振る舞いを保証するために、3つのテストが用意され、それぞれが意図した欠陥を意図的に埋め込むことで、テストが適切に機能することを確認した。

実験には3つの「腕」があった。「bare」はappgenを要求文のみで直接駆動したもの。「defer」はoloスタック全体を使用するが、プランナーの席は空にしたもの。「model」はoloスタック全体と、ローカルなQwen3-coder:30b言語モデルのプランナーを使用したものだ。

結果は次のようになった(88件の要求中)。

  • bare: 29件の動くアプリを生成、31件をデリバー(一部は拒否)、精度0.9355
  • defer: 29件の動くアプリを生成、31件をデリバー、精度0.9355
  • model: 22件の動くアプリを生成、32件をデリバー、精度0.6875

bareとdeferがまったく同じ29件の成功数を記録したのは偶然ではなく、88件の要求すべてにおいて、これら二つの腕は全く同じ結果を返した。これは、プランナー以外のoloのコンポーネントは、プランナーが空席の場合、システムの性能に何のコストももたらさず、何の利点ももたらさないことを示している。

さらに詳細な分析として、二つの腕が異なる結果を出した要求について比較した。最初の評価方法では、deferがmodelに7対0で勝利した(deferが動くアプリを生成したがmodelはできなかったケースが7件、逆は0件)。これは統計的に有意な差だった。しかし、この結果には測定システムの課題が隠されていた。

初期の分析でdeferがmodelに7対0で勝ったとされた7件のうち2件は、「戦艦ゲームをプレイする呼び出し可能なエンジンを作成する」や「NASAの系外惑星アーカイブをクエリする」といったAPIを生成する要求だった。当時のドライバは、ウェブページからHTMLフォームを探して実行することしかできず、APIとして構築されたアプリケーションは、たとえ正しく動作していても「駆動できない」と記録されてしまっていたのだ。APIを自身の公開エンドポイントで実行できる新しいドライバが導入された後、これらの要求を再実行すると、どちらも正常に動作することが確認された。これらはbareやdeferが既に正しく評価していたものと同じ結果だ。

これにより、当初deferの勝利とされた2件は引き分けとなり、残りの不一致は5件に減少した(deferがmodelに5対0で勝利)。この5件の要求も、プランナーが原因で問題が発生していた。これら5件すべてにおいて、モデルのプランは「web/python」と指定しており、これはappgenが独自に要求文から導き出す値と全く同じだった。つまり、プランナーが決定したフィールドが原因で失敗したわけではなかった。

これらの失敗のほとんどは、プランナーが生成する「エンティティ」(生成されるアプリの主役となる名詞、例えば「FacebookのInstagramアプリのクローン」なら「post」)フィールドに関係していた。モデルのプランが、要求文に含まれないエンティティ名を指定すると、appgenはその値をそのまま受け入れてしまい、自らの文脈解析を抑制してしまう。その結果、appgenが不適切な名詞でアプリケーションを生成し、動作しなくなることがあった。これは、コマンドライン組み立ての層で起こっていた問題と全く同じメカニズムだ。上位の層が下位の層が独自に決定できる値を埋めてしまうと、下位の層はその計算を停止し、結果として誤った振る舞いにつながる。

この問題の解決策は、プランが要求文に含まれないエンティティ名を指定した場合、oloがそのフィールドをappgenに渡さないようにすることだった。そうすることで、appgenは自ら要求文から適切なエンティティ名を導き出すことができるようになる。この修正により、上記の3つのエンティティ関連の失敗のうち2つは正常に動作するようになった。

さらに、過去の実験結果を引用するのではなく、今回の環境で再実行したところ、過去に公表された数値(appgenで21/88、フルスタックで15/88)は、現在のコードベースではそれぞれ29/88と22/88に改善していることが判明した。これは、コードが進化する中で、過去の数値がもはや現状を反映していないことを示している。

この実験結果は、「プランニングが無用である」と主張するものではない。むしろ、「このスタックのプランナーは、下位のレイヤーが既に導き出せるフィールドを埋めており、その埋め方が下位レイヤーの自律的な導出を抑制している」ということを示している。もしプランナーが、appgenが全く読み取らないような新しい機能(CSVエクスポートやメール機能、追加のバリデーションなど)に関するフィールドを計画するなら、それは依然として有用である可能性は高い。

また、「プランナーがない方が言語モデルよりも優れている」と断言するものでもない。これは一部の初期評価ではそう見えたが、テスト環境の改善や詳細な分析によって、最終的には両者にほとんど差がない、あるいは特定の条件下でモデルが若干不利になる程度という結論に落ち着いた。より堅牢で確実な結論は、「言語モデルを取り除いても、このシステムでは何のコストもかからなかった」というものだ。

この実験から得られる最も重要な教訓は、特定のシステムに限定されるものではない。それは、「上位の層から提供されるデフォルト値は中立ではない」ということだ。下位の層が独自に計算するはずの値と同じ値を上位の層が埋め込んでしまうと、それは一見何もしないように見えるが、実際には下位の層はその計算を停止してしまう。今回の実験で発生した5つの失敗のうち3つは、まさにこのメカニズムが原因だった。実験を開始するために行ったコマンドラインの変更も、同じ原則の逆方向の適用だった。もし「一貫性のために」webを自動的に埋め込んでいたら、それはappgen自身のルーターを無効化した状態での測定となり、誤った結果を公表することになっただろう。

もう一つの教訓は、測定器の限界と、その限界がもたらす結論の信頼性だ。最初に最も劇的な結果(7対0での勝利)が出たのは、皮肉にも測定器が最も不完全だったためだった。より良いドライバを開発することで、その結果は修正され、最終的に生き残った結論は、当初は「つまらない」と思われた「言語モデルは何の勝利ももたらさなかった」という、より堅牢で保守的なものだった。

今回の結果は、一つのシステムにおける、一つのプランナー、一つのデータセット、一つの言語モデル、そして一回の実行に基づくものだ。この結果をもって、実際に製品として出荷されているツールからプランナーが完全に削除されたわけではない。しかし、空席のプランナーでもシステムが動作するようにした「継ぎ目」や、不適切なエンティティ名を渡さないようにする修正などは、今後の開発やさらなる実験にとって重要な進展となった。

関連コンテンツ

関連IT用語