世界を動かす技術を、日本語で。

制約の減衰:バックエンドコード生成におけるLLMエージェントの脆弱性

概要

  • LLMエージェント はコード自動生成で高い性能を示すが、構造的制約には弱い傾向
  • 既存ベンチマーク は機能要件のみ重視し、構造的要件を軽視
  • 80件の新規生成タスク20件の機能追加タスク で体系的評価を実施
  • 構造的制約が増加 するほど、エージェントのパフォーマンスが大幅に低下
  • データ層の欠陥 が主な失敗要因であり、課題解決が今後の焦点

LLMエージェントの構造的制約下でのコード生成能力の評価

  • LLMエージェント は、仕様が緩い場合には自律的なコード生成で優れた成果を発揮
  • プロダクションレベルのソフトウェア には、アーキテクチャパターンやデータベース、ORMなどの厳格な構造的制約が必要
  • 従来のベンチマーク は、機能的に正しいが構造的には恣意的な解決策を評価しがち
  • 本研究では、 統一API契約 を用いて80件の新規タスクと20件の機能追加タスクを設定
    • 8種類のWebフレームワーク (Flask, FastAPI, Djangoなど)を対象
  • エンドツーエンドの振る舞いテスト静的検証ツール による二重評価を実施

主な発見と課題

  • Constraint Decay現象
    • 構造的要件が増すほど、エージェントの性能が大きく低下
    • 高性能な設定でも、ベースラインから完全指定タスクまでで アサーション合格率が平均30ポイント低下
    • 一部の弱い設定では 合格率がほぼゼロ まで低下
  • フレームワーク感度
    • 最小限かつ明示的なフレームワーク(例:Flask)では成功率が高い
    • 規約重視型のフレームワーク(例:FastAPI, Django)では平均的に性能が著しく低下
  • エラー分析
    • 最大の失敗要因は データ層の不備
      • クエリ構築ミス
      • ORMのランタイム違反
  • 機能要件と構造要件の同時充足 は、依然としてコーディングエージェントの大きな未解決課題

今後の展望

  • 構造的制約を考慮したエージェント設計 の重要性
  • データ層やORMの理解強化 が性能向上のカギ
  • ベンチマークや評価指標の見直し による現実的なエージェント評価手法の確立

Hackerたちの意見

最近の論文を思い出すな、異なる分野でドキュメント編集のタスクをLLMに委任するってやつ。[1] その論文では、プログラミングがほとんどのLLMがエラーを蓄積せずに長期的なタスクをこなせる唯一の分野だって言ってた。まだこの論文の要約しか読んでないけど、プログラミングに焦点を当てて、似たような現象を示しているみたい。長期的なタスクじゃなくて、むしろ「長いスタイルの制約セット」についての話みたい。[1] https://arxiv.org/abs/2604.15597 ディスカッション: https://news.ycombinator.com/item?id=48073246

簡単に検証できないなら、LLMは得意じゃないよ。

「私たちの体系的な研究は、LLMベースのコーディングエージェントにおける制約の減衰現象を明らかにします。現在のモデルは制約のない生成では優れていますが、明示的なアーキテクチャルールを強いられるとパフォーマンスが低下します。エンドユーザーにとって、この二分法はエージェントが迅速なプロトタイピングには信頼できるが、製品グレードのバックエンド開発には信頼できないことを意味します。」この研究の大きな弱点は、コストの理由で最前線のモデルを完全にはテストしていないので、具体的なパフォーマンス結果は鵜呑みにしない方がいいってこと。でも、行動とアーキテクチャの両方が正しくなければモデルが劣化するという全体的な結論は興味深いし、注目すべき点だね。

「二つの異なる目的の最適化はできない」というのが根本にあると思う。機能要件だけなら、実質的にプログラム合成の一種をやってることになるし、RL(強化学習)はそれをかなりうまく最適化できる。機能要件と非機能要件が混在していると、モデルに不完全な仕様を与えていることになって、ユーザーの意図を推測して空白を埋めなきゃいけなくなるんだ。だから、欲しいコードのスタイルの例をプロンプトに追加するのがめちゃくちゃ効果的なんだよね(このアドバイスはantirezに感謝!)。

彼らが使った最強のフロンティアモデル、GPT 5.2でも、エージェントプログラミングにはほとんど使えないと思う。こういうモデルの弱点を分析するのにはあまり興味がないんだけど、私の経験上、モデルが強くなるにつれて多くの弱点は完全に消えるからね。特に、何をしてほしいかを伝えれば。さらに、受け入れ基準が増えると失敗率が上がるのは驚くべきことじゃないよね。

うーん、これが本当だっていう経験談はあるよ。Opusとインタラクティブにプランを考えていると、何度か互換性のない解決策が出てきたことがあって、追加のコンテキストや要件を加えると、元のアーキテクチャに「アンカー」して適応するのが難しくなる傾向があるんだ。時々、元のプランに変更をこっそり入れようとすることもあるし。

明示的なアーキテクチャルールに従わされるとパフォーマンスが落ちる どんなに優れたモデルでも、生成されたコードのスタイルに関するルール(このくらいインデントする、こういうパターンで名前をつける、など)に従うのが難しいんだよね。どんなにAIファーストのコーダーでも、そういうことが珍しくないって認めるはず。でも、それでも彼らはこれらのモデルが毎回詳細な仕様に従うと思い込んでいるんだ。

プロンプトが「アラインメント」や「ガードレール」を強制しようとするときに見られる同じ問題かもしれない。パフォーマンスが落ちるんだ。どうやら、大きな解決策の領域が到達不可能になってしまったみたい。例えば、約1年前の画像生成モデルに「ガードレール」を適用すると、みんな同じように見えるようになる。ストーリー生成モデルも、標準的な名前だけを使うようになる。それが去年の話。フロンティアモデルでも同じことが起こってるのかな?

AIアシストで書かれた本でも似たようなことに気づいたよ。最初はまあまあだけど、章が進むにつれて各章の始まりが前の章の終わりを繰り返すようになって、明らかにLLMの特徴が増えてくる。進むにつれて、前の内容の繰り返しに頼ることが多くなるんだ。著者が後の章に対してあまり注意を払わなくなって、編集にも力を入れなくなる可能性もある。Amazonに大量にあるけど、LLMはまだちゃんとした文章を書くレベルには達してないね。

それに、PythonやJSのような動的型付けの言語を使ってた。私の経験では、静的型付けのコードベースの方が人間にとってメンテナンスしやすいから、エージェントにとってもそうかもしれない。Codex/Claude Codeを使ってGoのコードを書くと、エージェントが何か変更して、エラーをチェックするためにビルドを実行して、エラーを見つけて修正する回数は数えきれないよ。

Pythonがデフォルトで動的型付けだと思ってる人がいるのが信じられない。強い静的型付けは何年も前からPythonで選べるオプションなのに、普通はそれがデフォルトであるべきだよね。

8つのウェブフレームワークにまたがるタスク。LLMが既存のフレームワークを使うよりも、純粋なHTML+CSS+JSを作る方が良いって経験した人いる?

GPT-5.4以降、ウェブフレームワークは「危機的状況」にあると思う。もうReactみたいなものを使うなんて想像できないよ。最近見た中で一番すごい組み合わせは、JavaScriptを使ったRazor Pagesのプログレッシブエンハンスメントだね。このアレンジだと、新しいモデルがサーバーサイド(cshtml)で何かをするべきか、クライアント(js)でやるべきかをうまく判断する傾向があるよ。

これは「チャットが長くなるにつれて、ガードレールが曖昧になる」っていう別のバージョンみたいだね。すべてのコンテキストウィンドウを使えないから、最後には出力が制約(またはガードレール)を尊重しなくなる。でも、製品グレードのコードを確実に生成するには、モデルが広範な認識を持っていて、コンテキストウィンドウをすぐに埋めてしまうのが理想なんだ。「この6つのディレクトリのすべてを頭に入れて、この変更を加えて」って言ってるようなもので、すでにすべてを頭に入れるだけでコンテキストウィンドウが埋まって、制約(またはガードレール)に従う能力を失っちゃうんだよね。

でも、これは新しい問題じゃないよ。だからこそ、モジュラーコードや厳格なインターフェースを書き始めたんだ。

じゃあ、もっと厳しいガイドラインを設けるってこと?SonarQubeとか。だけど、そうすると失敗のパターンはリンターを満足させることになって、要求事項を徐々に忘れちゃうかもね…(もしくは、そんなにゆっくりじゃなくて、試行錯誤のループがコンテキストに優しくないから)。

長期的なエージェントコーディングを結構試してみたけど、特定のアーキテクチャパターンに強制されるとエージェントのパフォーマンスが悪くなるみたいだね。後から制約を追加するよりも、途中で含める方が少し良いことがわかった。コードベースにパターンが現れ始めて、エージェントがそのパターンに従うことで文脈を支配して自己強化される「石灰化」っていう副作用があるみたい。この現象は、コードベースの質によって強みにも弱みにもなり得る。もっと詳細な洞察が得られるのは、アーキテクチャのガイダンスを最初から含む新しい試行が終わった時だね。

エージェントは特定のアーキテクチャパターンに強制されるとパフォーマンスが悪くなる。 これ、私も気づいた。エージェントやモデルにはそれぞれのスタイルがあって、ほとんどが冗長すぎるってまとめられるんだ。さらに、モデルは実装を「計画」するスペースが与えられるとモジュール化はまあまあできるけど、後から抽象化が役立つとはあまり考えない(つまり、グリーンフィールドのコードベースで何度も繰り返したり、レガシーコードベースに投入されたりした後)。これが「神ファイル」を生むことが多くて、ユーザーやアーキテクトが指摘すると、モデルが正しく批評する(最初にそのコードを書いたのは彼らなのに、面白いことに)。

だから私の発見は、計画を立てる価値があるってこと。少し複雑な変更の場合、私はいつも最初にコーデックス(5.5-high)を計画モードで動かすんだ。AGENTS.mdから関連するドキュメント({ARCHITECTURE,BACKEND-GUIDELINES,NESTJS-DI,..}.mdなど)をリンクしておいて、必要な時にすぐに見つけられるようにしてる。例えばバックエンドの問題を扱うときにReact特有のことを知っている必要はないからね。私は通常、エージェントが新しいコンテキストで作ったプランを盲目的に承認するんだけど、それはまるで私がプロンプトを出したかのようだ。これが一番うまくいく。/goalを使うと、ただひたすらコンパクトにして自分のやるべきことをやってるだけだから、もちろん雑になることもある。もしチケットを計画モードのプロンプトに変換する状態遷移機械があったら、そして、なんか「プロダクトマネジメントの視点レンズ」でプランを承認したり変更したりして、能力が低いエージェントがそのプランを実行するようにしたら、うまくいくと思うんだけど。

コードベースのいくつかの部分をイディオマティックにするために時間をかけて、そのファイルを例として@-するのをおすすめするよ。マークダウンで誘導するより、こっちの方がずっと効果的だよ。FastAPIにはまあまあ合ってるけど、JavaScriptは最悪みたい。ガイドや例があっても、APIを指示通りに使うよりも、ゴミをインラインで書くことを好むみたい。

私は考え方が変わった。LLMのコード生成には100%懐疑的だったけど、今では書くプロのコードの80%以上が生成されたものだ。とはいえ、限界は明らかで、いくつかのプロジェクトでそれが見え始めてる。この文章は私の疑念を裏付けるように見える。これが単なる確認バイアスかどうかはまだわからないけど、私の経験では、複雑なものにはどんどん制約、スタイルガイド、コーナーケース、エラーハンドリング、最適化ガイドラインなどをMarkdownの仕様、ルール、スキルに追加しなきゃいけなくなる。ある時点で、私たちはプログラミング言語のより形式的で決定論的な世界から、自然言語の非形式的で非決定論的な世界に複雑さを移しているように見える。執筆速度の向上はすごいけど、ビジネスはこれを生産性の向上と見なすし、もちろんそのプレッシャーは常にある。でも、トレードオフは明らかで、多くの人がそれを無視している気がする。

もし生成しているコードの80%がLLMからのものなら、ただ既存のものをリミックスしてるだけだよ。つまり、スラップ。LLMは新しいものを生成できないからね。

より形式的で決定論的なプログラミング言語の世界から、非形式的で非決定論的な自然言語の世界に複雑さを移すのは、毎回実行するたびに意味的に異なるコードを生成するコンパイラを使っているようなもの。基本的には、UB(未定義動作)だらけのプログラムをコンパイルしてるけど、ほとんどの時間「うまくいっているように見える」って感じ。 > ビジネスはこれを生産性の向上と見なす。 つまり、LoC(行数)/sを「生産性」の指標として戻ってきたってことだね。

私はこれを避けるために、記憶管理プロジェクト「アリストテレス」に基づいたプラグインを作ってる。TDDパイプラインスキルに従って仕事をするLLMの活動を監視するためにステータスマシンを追加して、要求の明確化から納品までを行うんだ。この2つのプロジェクトはGitHubにあるから、alexwwang/aristotleとalexwwang/tdd-pipelineを検索して詳細を見たり、LLMにスキャンさせて興味のあるポイントを教えてもらったりしてみて。