LLMが書いたコードに「数学的な安全証明」を付ける——MAGSが挑む形式検証の自動化

はじめに——「動いた」と「安全だ」は別物である

LLMコーディングエージェントが生成するコードの量は急速に増えている。だが、生成されたコードには既定では安全性や正しさの保証が一切ない。これが、ここで扱う研究の出発点である。

開発者の負担は「書くこと」から「検証して直すこと」へと移りつつある。大規模調査でも、AI支援によるコーディングでは人間のレビューが新たなボトルネックになることが示されている(Liang et al. 2024)。生成は速くなったのに、その出力が本当に安全かを確かめる工程は人間の手作業のままだ、というねじれが生じている。

テストに全部通ったコードは、本当に安全なのか

この記事の核心的な問いは、シンプルである。テストに全部通ったコードは、本当に安全と言えるのか?

答えは「言えない」。テストが確かめられるのは、実際に試した入力の範囲でしかない。スイートが試していない入力で安全性やセキュリティ上の性質が破られる可能性は、テストを何回通しても消えない。ましてや、テスト自体をエージェントが生成するようになれば、開発時に見落としたエッジケースは自動生成されたテストでも見落とされやすくなる。

MAGSという答え

ここで紹介する論文(arXiv:2609.19391、2026年9月16日投稿)は、LLMが生成したコードに「機械的に検査できる安全保証」を付与するマルチエージェントフレームワーク MAGS を提案している。MAGSは、Multi-agent Auto-formalization Guarantees Safety for Agentic Outputs の略で、複数のAIエージェントが協調して、コードの安全性を数学的な証明として組み立てる仕組みである。

この記事を読み終えると、次の4点が分かる。

  1. なぜテストでは安全性を保証できないのか
  2. 形式検証(formal verification)とは何か、なぜ長らく人手に頼らざるを得なかったのか
  3. MAGSが形式検証のどの工程を、どう自動化したのか
  4. 実験でどこまで成功し、どこで失敗したのか

対象読者はソフトウェアエンジニア、AIエージェントの開発者、セキュリティやDevSecOpsの担当者である。数式は最小限にとどめ、用語は初出時に短い定義を添え、具体的な数値と例で説明していく。専門知識がなくても、「なぜ今この技術が必要なのか」から順に追えるように構成している。

なぜ今「生成コードの安全性」が問題なのか

LLMが生成したコードは、もはや実験的な試みではなく、本番ソフトウェアの相当な割合を占めつつある。同時に、推論コストは2年足らずで約2桁下がった。同じ予算で生成できるコードの量が桁違いに増えたということであり、人間が目を通しきれない「未検証のコード」が、そのぶん大量に流通していることでもある。生成のスピードにレビューのスピードが追いつかないという構図が、安全性の問題を顕在化させている。

多くの開発者は、単体テストやテストスイートが通ったことを「安全の証拠」とみなす。しかしテストが示せるのは、試した入力で期待どおりに動いたという事実だけだ。テストスイートが触れていない入力の組み合わせによって、関数が安全・セキュリティ上の性質を破ることは珍しくない。テストが通ったことは、テストが試さなかった失敗が存在しないことの証明にはならない。

さらに厄介なのは、テスト自体の生成にもLLMエージェントが使われるようになった点だ。開発時に見落とされたエッジケースは、自動生成されたテストでも同じように見逃されやすい。検証側と被検証側が同じ盲点を共有してしまうため、テストの通過は安心材料としてますます頼りなくなっている。

この懸念は推測ではなく、先行研究の測定結果として現れている。

先行研究 対象 結果
BaxBench(Vero et al. 2025) LLMが生成したバックエンド 機能的に正しいと判定されたプログラムの約半分で実際のエクスプロイトが成立
SecCodePLT(Nie et al. 2025) コードエージェントの安全性 機能面の評価とは別に、セキュリティ上の欠陥が同様の傾向で残存

つまり「機能的に正しい」と「攻撃に対して安全」は別の判定であり、前者を満たすコードの半数規模で後者が破れうる。このセクションの要点はここにある。テストの通過は、安全性の証拠としてほとんど価値がない。 生成量が増え、コストが下がり、テストまで自動化された今、この前提を疑わないまま生成コードを本番に流し込むことが、最大のリスクになっている。

従来の安全確保手法とその限界

生成されたコードの安全性を確かめる手段は、これまでにもいくつか存在する。しかし、それらは「見つけた失敗」を報告することはできても、「失敗が存在しないこと」を保証することはできない。ここでは代表的な3つの手法——ファズテスト・静的解析、LLM-as-a-Verifier、形式検証——を取り上げ、それぞれがどこで限界に突き当たるのかを見ていく。

ファズテスト・静的解析ができないこと

ファジング(fuzz testing) は、ランダムあるいは変異させた多様な入力を自動生成してプログラムに与え、クラッシュや異常終了を探す手法である。実装が容易で実バグの発見に強い一方、本質的な弱点がある。それは、見つけられなかった失敗の不在を保証できないという点だ。ファジングが語れるのは「探索した入力の範囲では問題が起きなかった」という事実にすぎず、未探索の入力に潜む欠陥については何も言えない。

静的解析 は、プログラムを実行せずにソースコードのパターンからバグを検出する。しかし誤検知(false positive)が多く、意味論に深く踏み込んだ性質の証明には向かない。たとえば「並列処理でデータ競合が起きないこと」のように、実行時の状態やスレッド間の相互作用に依存する性質は、単純なパターンマッチでは捉えきれない。

両者に共通する限界は明快だ。これらは反例を探すアプローチであり、全入力についての保証を与えない。反例が見つからなかったことは、安全性の証明にはならない。

LLM-as-a-Verifierの限界

次に広く使われているのが、LLM-as-a-Verifier と呼ばれる手法である。生成したコードを別のLLMにレビューさせ、正しさや安全性を判定させる。人間のレビューを代替する手軽な仕組みとして普及しているが、論文はここに構造的な弱点があると指摘する。

第一に、モデルは自分の判断だけをフィードバックにすると信頼性高く改善しない(Huang et al. 2024)。自己批評を繰り返しても、誤りを自力で特定できるとは限らない。第二に、モデルはエラーの位置を教えられて初めてその誤りを修正できる(Tyen et al. 2024)。つまり「どこが間違っているか」を示す外部の情報がなければ、修正は当てにならない。

これらは根っこで同じ問題に行き着く。すなわち判定者が間違っている可能性(faulty oracle) である。レビューするLLMも、ユニットテストも、ファズテストも、判定を下す主体が誤れば、その「合格」は安全性の証拠にならない。

手法 何を調べるか 与えられる保証
ファズテスト 多様な入力でのクラッシュ 探索した範囲のみ(反例の有無)
静的解析 コードパターンの逸脱 パターンに合致する問題のみ
LLM-as-a-Verifier 別モデルによるレビュー 判定者の正しさに依存
形式検証 仕様を満たすことの証明 モデル化された全入力について

形式検証の「高すぎる壁」

では、何が本当の保証を与えられるのか。その答えが形式検証(formal verification)、とりわけ演繹的プログラム検証(deductive verification) である。これは、モデル化された全ての入力についてプログラムが仕様を満たすことを、機械的に検査可能な証明として示す手法だ。テストが「試した入力」に限られるのに対し、形式検証は入力空間全体を対象にする点で決定的に異なる。

しかし形式検証は、歴史的にコストが prohibitive(法外)だった。象徴的な事例が seL4 マイクロカーネル である。8,700行のCコードに対して、約200,000行のIsabelle証明が必要となり、約20人年の工数が費やされた(Klein et al. 2009)。これは「1行のコードに対して20行以上の証明」という比率であり、形式検証が原理的に強力でありながら、実務では手が届きにくい理由を端的に表している。

ボトルネックは、人手による仕様記述と証明エンジニアリングにあった。安全性を証明するには、まず「何を守るべきか」を仕様として厳密に書き下ろし、次にその仕様を満たすことをソルバーが処理できる形に導く必要がある。この工程をAIで自動化できないか——という問いが、MAGSの出発点になっている。

MAGSとは——「実行する言語」と「証明する言語」を分ける

MAGS(Multi-agent Auto-formalization Guarantees Safety for Agentic Outputs)は、LLMが生成したコードに、機械的に検査できる安全保証を付けて出力するマルチエージェントのフレームワークである。中心にある設計思想は、「プログラムが実行される言語」と「その安全性が証明される言語」を分離することだ。実行はCやPythonのまま行い、証明は別の言語で担う、という発想である。

基本アイデア:中間表現としてのDafny

証明を担う言語として選ばれたのが Dafny である。Dafnyは、仕様——事前条件(関数を呼ぶ前に満たすべき条件)、事後条件(返る前に満たすべき条件)、ループ不変条件(ループの各周回で保たれる性質)——を言語ネイティブに記述でき、静的検証器を備えたプログラミング言語である。記述された仕様は Boogie(検証条件を生成する中間層)を経由して Z3 SMTソルバー(論理式の充足可能性を判定する自動定理証明器)に渡され、証明義務が自動で処理される。

DafnyはC#、Java、JavaScript、Go、Pythonなどへコンパイルできるため、検証済みのコードをそのまま実務環境に持ち込める。さらに検証対象をDafnyという共通の中間表現に置くことで、CUDA(GPU並列実行)、ターミナルスクリプト(OS操作)、ロボティクス(物理制御)という実行モデルがまったく異なる3ドメインを、同じ枠組みで扱えるようになる。実行する言語が違っても、証明の土俵は1つに揃うわけだ。

パイプラインの全体像(2段階)

MAGSは2段階で動く。第1段階が「ドメイン意味論の構築と凍結」、第2段階が「個々のプログラムの翻訳・修復・検証」である。この2つを分ける狙いは明確で、証明を作るエージェントが「特定のベンチマークを通しやすくするために安全の定義を弱める」ことを防ぐ点にある。仕様はタスクに取りかかる前に固めておく、という順序が守られる。

flowchart TD
    A[人間が書くコア論理] --> B[エージェントがAPI意味論を拡張]
    B --> C[批評・人間監査]
    C --> D[仕様の凍結]
    D --> E[タスクごとの翻訳]
    E --> F[証明計画・修復]
    F --> G[Dafny検証]
    G --> H[ターゲット言語へコンパイルバック]
    H --> I[独立した安全チェック]
用語 意味
自動形式化(autoformalization) 自然言語の仕様やドキュメントから、機械が扱える論理的な仕様を自動で導出すること
凍結された仕様(frozen specification) 一度確定したコア仕様を、以降のタスクで変更できないように固定したもの
意味論的埋め込み(semantic embedding) 対象プログラムを、その言語の意味論を反映した中間表現へ写し取ること

第1段階で固めた意味論は、第2段階のすべてのタスクで共有される。だからこそドメインごとの形式化コストは一度きりで済み、以降のプログラム検証は追加の人間入力を要しない。この「一度作って凍結し、使い回す」構造が、人手による仕様記述と証明エンジニアリングを前提としてきた形式検証のコスト構造を変える鍵になっている。

ステップ1——ドメイン意味論を自動形式化して「凍結」する

MAGSの第1段階は、対象ドメインの「意味論」を構築し、それを凍結(freeze)することだ。ここでいう意味論とは、そのドメインのAPIや操作がプログラムの抽象状態をどう変化させるかを、機械が推論できる形で書き下したルール群を指す。自動形式化(autoformalization)とは、自然言語の仕様やドキュメントを、こうした機械検証可能な形式仕様へ変換する作業である。この段階で作られた仕様は以降の全プログラム検証の「物差し」になるため、後から都合よく書き換えられないよう先に固めておく。

flowchart LR
    A[人間が書くコア論理] --> B[エージェントが公式ドキュメントからAPI意味論を拡張]
    B --> C[批評エージェントが忠実性・一貫性を検査]
    C --> D[人間がランダム20%を監査]
    D --> E[安全プローブ3つ・不安全プローブ3つでテスト]
    E --> F[合格した意味論を凍結]

人間が書くコア論理の土台

出発点は、エージェントではなく人間が書く小さな論理基盤(コア論理)である。これは各ドメインの抽象状態と推論の基本要素だけを定義し、網羅性よりも正しさを優先する。

CUDAでは分数パーミッション分離論理(fractional permission-based separation logic)を使う。分離論理は「メモリのある領域を誰が触れるか」を論理的に管理する枠組みで、フルパーミッションは排他的な書き込みを、分数パーミッションは複数スレッドによる共有読み取りを許可する(Bornat et al. 2005)。この土台の上で、境界外アクセスの不在、データ競合の不在、バリア発散の不在という3つの安全性質がトップレベルに定義される。

ターミナル(シェルや暗号APIを扱う短いPythonプログラム)では、扱うAPIファミリーごとに異なる形式系を選ぶ。

対象API 採用する形式系
シェル操作 Symbolic Finite State Transducers(Veanes et al. 2012)
暗号API CrySL(Krüger et al. 2019)
正規表現 One-Unambiguous Regular Languages(Brüggemann-Klein & Wood 1998)

ロボティクスでは運動学的な状態と制御挙動、空間的境界と衝突に関する安全条件を表現する。こうしたコア論理は確定した時点で凍結される。

LLMエージェントによるAPI意味論の拡張と批評

土台が固まると、LLMエージェントが公式ドキュメントから個々のAPIの意味論を導出し、コア論理を拡張していく。膨大な数のAPIを人手で形式化するコストを、ここで自動化する。ただし拡張した仕様がそのまま採用されるわけではない。批評エージェント(critic)が各仕様の忠実性・一貫性・モジュール性を検査し、さらにソースコード上のAPI構造を保つよう求める。構造を保つことは可読性だけでなく、SMTソルバーの効率とエラー箇所の特定しやすさにも直結する。

人間の役割も残る。生成された意味論のランダムな20%を手作業で監査し、構造的エラー、強すぎる事前条件、チェック漏れした安全条件、過度に単純化された仕様、コア論理からの逸脱を洗い出す。全量ではなく抜き取りにすることで、人間のコストを一定に抑えつつ品質を担保する設計になっている。

安全・不安全プローブによる意味論のテスト

凍結の前に、意味論そのものの妥当性がテストされる。各意味論ライブラリには、安全なプローブ3つと不安全なプローブ3つが、意味論とは独立に生成されて与えられる。合格条件は明確で、安全なプローブはすべて検証を通過し、不安全なプローブはすべて拒否されなければならない。これを通ったライブラリだけが、実際のプログラム検証に使われる。

この手順の狙いは「検証器のキャリブレーション」にある。意味論が緩すぎれば不安全なコードを素通ししてしまい、厳しすぎれば安全なコードまで拒否してしまう。3対3という小さなプローブで両方向の失敗を検出し、意味論のバランスを取る。ここで作られた凍結済みの意味論は、以降のステップで検証失敗が起きても弱めることができない。安全定義を都合よく緩める抜け道を、最初に塞いでおくのがこの段階の意味である。

ステップ2——証明生成と修復

ステップ1でドメインの意味論が「凍結」されたら、あとは個々のプログラムをその意味論の上で検証するだけだ。ここからがステップ2——LLMが生成したコードを検証対応の中間表現であるDafnyに翻訳し、証明を組み立て、失敗したら修復し、最後に実行可能なコードへ戻す一連の工程である。

flowchart LR
    A[生成されたソースプログラム] --> B[未対応APIの意味論を追加<br/>既存仕様は凍結のまま]
    B --> C[Dafnyへの翻訳<br/>+安全仕様の付与]
    C --> D[忠実性チェック]
    D --> E[証明計画<br/>複数戦略の並列探索]
    E --> F[注釈の追加<br/>必要ならコード修復]
    F --> G[Dafny検証器<br/>8種のソルバーシード]
    G -->|成功| H[ターゲット言語へコンパイルバック]
    G -->|失敗| F
    H --> I[決定論的な互換レイヤーで実APIに置換]
    I --> J[タクティクライブラリへ<br/>証明戦略を抽出]

オンデマンドの意味論拡張

プログラムが凍結済みの意味論にまだ存在しないAPIを使っている場合、MAGSはステップ1と同じ意味論生成パイプラインを呼び出し、そのAPIだけを追加で形式化する。重要なのは、既存の意味論は凍結されたままという点だ。もし検証に失敗したときに「以前の仕様を弱めれば通る」という抜け道があれば、証明は簡単に無意味化してしまう。検証失敗はあくまでプログラム側か注釈側で解決させ、仕様の側では解決できないようにしているわけだ。

Dafnyへの翻訳と忠実性チェック

翻訳エージェントは、対象プログラムをDafnyへ変換し、必要な安全仕様を付与する。このとき「元の挙動を保つこと」が明示的に指示され、別の批評エージェントが翻訳の忠実性をチェックする。

ここで保証の帰属について、正直に押さえておくべき注意点がある。形式保証が成り立つのは検証済みのDafny実装とそのコンパイル出力であって、元のソースプログラムそのものではない。ただし論文は、このことが保証の連鎖を断ち切るわけではないと述べている。翻訳の忠実性がチェックされ、後段のコンパイルバックが決定論的に行われるためだ。

証明計画・並列探索・修復

次に計画エージェントが、複数の候補証明戦略と軽量な注釈スケッチを生成し、本格的に統合する前に検証器で試す。複数プランを並列に探索することで、効率と成功率の両方を引き上げる狙いがある。

各プランは独立した注釈・修復プロセスを開始する。基本方針は「まず実行挙動を変えずに証明注釈を追加し、それでも足りなければコード修復を許す」という順序である。批評エージェントは注釈の忠実性・安定性と、修復が機能を保持しているかをレビューする。エージェントは補助補題(helper lemma、証明の途中で必要になる小さな定理)を追加できるが、凍結された意味論には手を触れられない。

SMTソルバーの不安定さ(同じ入力でも結果が揺れることがある性質)への対策も組み込まれている。8種類のソルバーシードと複数の検証設定を試し、いずれかの試行が通ればその候補は成功とみなす。ただし500秒を超える試行は失敗扱いである。

検証済みコードのコンパイルバック

検証済みのDafnyプログラムは、Dafny組み込みのコンパイラでターゲット言語へコンパイルされる。検証時には、ドメイン固有APIは実APIを模したシンボル(記号)として表現されている。コンパイル後、決定論的な互換レイヤーがそれらを実際のソース言語APIに置き換える。

この記号の対応づけ(symbol alignment)が単純なマッピングを可能にし、検証済みの挙動を保ちながら、最終的な実行ファイルを読みやすく保守しやすいものにする。最終ステップが「さらなる合成」ではなく決定論的なシンボル置換であることが効いている。マッピングが形式化されたAPI意味論を保つ限り、検証済みの保証はそのまま引き継がれるからだ。

タクティク学習——証明経験の蓄積

検証が終わると、エージェントはそこで得た再利用可能な証明戦略をモジュール式のタクティクライブラリへ抽出する。例えば「この種類のループにはこの不変条件のパターンが効く」「このソルバー設定だと安定する」といった知見である。

タクティク(tactic)とは、証明を組み立てるための再利用可能な手続きのことである。抽出されたタクティクは後のインスタンスで再利用でき、証明の経験が蓄積されるほど将来のサンプルでの成功率が上がっていく設計になっている。つまりMAGSは、1件ごとに証明をやり直すのではなく、証明のノウハウをライブラリとして育てていく方向を狙っている。

どうやって「ズル」を防ぐのか——ゲーミング対策の設計

形式検証を自動化するとき、最も警戒すべき相手は検証器のバグではなく、検証を通過すること自体を目的化したエージェントである。証明を書く主体がAIである以上、「検証に通った」という事実は、そのまま「安全だ」を意味しない。たとえば、前提条件を常に偽にする、誰も使わない仮定を置く、といった手口で、実質的な内容を持たない証明(空虚な証明)が通ってしまうことがある。MAGSはこの問題に、検証器の健全性とは別の層として、明示的な設計を割いている。

対策は大きく5つに整理できる。

# 対策 狙い
1 コード編集エージェントと仕様編集エージェントの分離 証明を書きやすくするために仕様を勝手に緩める行為を封じる
2 コア仕様の凍結 検証失敗を「仕様の弱体化」で解決する経路そのものを塞ぐ
3 決定論的チェックによる証明回避構文の拒否 assume、公理(axiom)、検証の無効化など、証明をすり抜ける記述を機械的に弾く
4 環境前提を外側のexpectチェックとして露出 仮定を検証の内側に隠さず、実行時に評価して失敗を可視化する
5 プログラムレベルエージェントのサンドボックス化 ベンチマークのテストや独立評価オラクルを見せず、形式仕様だけを相手に動作させる

3番目は、証明の「省略」を許さない仕組みである。assume文は「ここではこの条件が成り立つ」と検証器に宣言するもので、これを許すと任意の性質を無条件に通せてしまう。公理の追加や検証機能の無効化も同じ効果を持つため、いずれも決定論的なチェックで拒否される。

4番目のexpectチェックは、検証の外側にある前提を実行時のチェックとして残す考え方である。検証済みプログラムを未検証の大きなコードベースに統合する場面では標準的な手法で、仮定が現実に成り立たなければ実行時に検出できる。

5番目は評価の独立性に関わる。エージェントがベンチマークのテストを見られると、テストだけに合わせた挙動を書きかねない。そこで形式仕様のみを与えて検証させ、保持されたテストは検証が終わった後に実行する。

flowchart LR
    A[プログラム編集エージェント] -->|コードのみ編集| D[Dafny検証]
    B[仕様編集エージェント] -->|凍結前のみ仕様を編集| C[凍結されたコア仕様]
    C --> D
    D -->|assume・公理・検証無効化を検出| E[決定論的チェックで拒否]
    D --> F[検証済みプログラム]
    F --> G[保持テスト・独立オラクルを事後に実行]

ここで押さえておきたいのは、これらの対策が「検証器が健全(sound)であること」を前提にした上での追加防御だという点である。健全な検証器を用意しても、エージェントが検証の対象そのものを歪めてしまえば、得られる保証は意味を持たない。保証の強さは、検証器の品質だけでなく、何を検証させ、何をさせないかの設計に依存する——これがこのセクションの要点である。

実験——3つのドメインで何が起きたか

MAGSの有効性は、実行モデルが根本的に異なる3つのドメインで検証された。合計220のタスクが対象で、いずれも「生成されたコードが凍結済みの安全仕様を満たす証明を出せるか」という同じプログラムレベル・パイプラインで処理される。ここでは、評価対象の設計と、3つの研究課題(RQ1〜RQ3)に対する結果を順に見ていく。

評価対象とドメイン設計

3つのドメインは、それぞれ別のベンチマークからタスクを取っている。CUDAは並列GPUメモリ上での実行、ターミナルはOSコマンドやプロセスの操作、ロボティクスは物理環境との相互作用が実行モデルであり、安全性の意味そのものがドメインごとに異なる。

ドメイン タスク源と件数 実行モデル 意味論作成 / 検証に使ったモデル
CUDA KernelBench Level 1の全100タスク(単一カーネルのGPU演算子。畳み込み・正規化など) 並列GPUメモリ 意味論: Gemini 3.0 Thinking / 検証: Opus 5(High Thinking、1Mトークン文脈)
ターミナル SecCodePLTから100例をサンプリング(os・subprocessを使う短いPythonプログラム) シェル・プロセス操作 意味論: ベンチマーク提供 / 検証: GPT 5.6 Sol
ロボティクス VLABenchからアーム制御タスク20例 物理環境との相互作用 意味論: Gemini 3.0 Thinking / 検証: GPT 5.6 Sol

ターミナルは、ベンチマークが扱う27のCWE(Common Weakness Enumeration、ソフトウェア脆弱性の共通分類)を全てカバーしており、インジェクション、特権アクセス、暗号APIの誤用などが含まれる。安全性が個々のモジュール固有の意味論に依存する点が、このドメインの難しさである。ロボティクスはベンチマーク自体が小さいため、サンプル数も20と少ない。

RQ1——1つの枠組みで多ドメインの検証済み安全出力を作れるか

最初の問いは、単一の枠組みが3ドメインすべてで「検証済みの安全な出力」を生成できるかである。ここでは意図的に性質の異なる2つの指標を使う。

  • 検証成功率: 25回の総エージェント呼び出し予算内で、Dafnyが検査済みの証明を出せた割合。つまり「仕様を満たす証明に到達できたか」。
  • 安全チェック成功率: 最終的な実行出力が、MAGSとは独立したドメイン固有の安全オラクルも通過した割合。つまり「現実の安全性を捉えていたか」。

独立オラクルの中身は、CUDAがNVIDIA Compute Sanitizer(memcheckによるメモリ安全検査とracecheckによるデータ競合検査)、ターミナルがSecCodePLTの該当セキュリティテスト、ロボティクスがシミュレータの衝突・境界外指標である。

結果は次のとおりである。

ドメイン 検証成功率 安全チェック成功率
CUDA 100/100 100/100
ターミナル 100/100 82/100
ロボティクス 20/20 20/20
合計 220/220(100%) 202/220

検証成功率は220/220、すなわち100%に達した。CUDA 100/100、ターミナル 100/100、ロボティクス 20/20であり、実行モデルが大きく異なるにもかかわらず、同じプログラムレベル・パイプラインが全タスクで証明に到達したことになる。

一方、独立オラクルによる安全チェックでは、ターミナルが82/100にとどまった。18件の失敗は特権アクセスと並行性に関連するCWEに集中しており、自動形式化されたモジュール意味論がそれらのセキュリティ関連挙動を十分に表現できていないことが原因である。この乖離は、後述する失敗モードの核心につながる。

RQ2——時間・トークン・コストのオーバーヘッド

2つ目の問いはコストである。測定範囲は、翻訳の開始からDafny検証器が候補を受け入れる(または予算を使い切る)までに限定される。初期のソースプログラム生成と、ドメインごとに一度だけ行う意味論構築は除外されている。つまりここで測っているのは、タスクごとに繰り返し発生する検証コストである。

ドメイン 平均時間(最小–最大) 平均トークン(最小–最大) 平均コスト(最小–最大)
CUDA 36.4分(3.5–264.9) 5.26M(443K–35.29M) $8.60($0.93–$52.14)
ターミナル 36.3分(10.7–203.7) 6.51M(1.84M–32.97M) $9.01($3.37–$36.47)
ロボティクス 68.3分(34.2–88.2) 10.07M(5.13M–15.04M) $10.17($6.37–$15.41)

CUDAとターミナルは平均レイテンシがほぼ同じだが、ターミナルのほうが多くのトークンを消費する。扱うAPIの種類とモジュール固有の形式系が広範で、より異質な推論と意味論的コンテキストを必要とするためだ。ロボティクスは3ドメイン中で最も高コストで、物理的相互作用が従来のプログラミングAPIより粗い抽象で表現される結果、制限的な仕様や難しい証明義務が増えることが背景にある。

結論として、MAGSは通常のコード生成に比べて大幅なオーバーヘッドを負う。1サンプルあたり平均36〜68分、$8.60〜$10.17という水準は、安全性が重要な高リスクの生成に向けたものであり、日常的なコーディングや迅速なプロトタイピングには向かない。

RQ3——安全になった後も意図した機能を保つか

3つ目の問いは機能保持である。検証器は、挙動を制限したり削除したりすることで安全性質を空虚に(vacuousに)満たすことができてしまう。そのため「安全になった」だけでなく「意図した機能が残っているか」を測る必要がある。

方法として、翻訳・修復ループとは独立したモデルが、安全な入力に限定した保持テスト10個を生成した。このテストはプログラムレベルのエージェントには一切見せず、同じテストを「元の未検証プログラム」と「検証済みコンパイルバック版」の両方で実行し、同じ結果になることを期待する。ドメイン別には、CUDAは安全なテンソル入力でのカーネルの数値演算挙動、ターミナルは敵対的条件を意図的に起こさない入力での意図した挙動、ロボティクスは10の異なるシードでの意図タスク評価(合格条件は全シード平均の成功率が元ポリシー以上)である。

ドメイン 機能保持
CUDA 100/100
ターミナル 100/100
ロボティクス 11/20(緩和策適用前は0/20)

CUDAとターミナルは100/100で、翻訳・注釈・修復・コンパイルの全過程を通しても保持テストの挙動を犠牲にしていない。一方ロボティクスは別の失敗モードを露呈した。初期パイプラインでは機能保持が0/20であり、検査すると修復エージェントが「安全と証明しにくい動作を避ける」空虚な解に頻繁に収束し、安全事後条件は満たすものの意図した操作タスクを実行していなかった。

緩和策として、意味論拡張の際にドメイン意味論から軽量なタスク固有の成功定義を導出させ、修復時の追加制約に含めた。これにより機能保持は0/20から11/20に改善し、安全チェック成功率20/20は維持された。ただしこの成功条件は形式的安全証明の一部とはみなされない。任意のタスク成功を厳密に仕様化・検証することは別の仕様問題であり、本研究の範囲外である。あくまで「自明に安全な修復」を避けさせるための操舵的な制約(steering constraint)として機能する位置づけだ。

結果が示す2つの失敗モード——意味論の忠実性が全てを決める

3つのドメインの実験結果は、一見すると成功の物語である。220タスク全てでDafnyによる検証証明が得られ、検証成功率は100%に達した。しかし、独立した安全オラクルによるチェックではターミナルが82/100にとどまり、機能保持テストではロボティクスが0/20という結果を残した。この2つの数字は、形式検証を自動化する際に避けて通れない、方向の異なる失敗モードを浮かび上がらせる。

「検証は通ったのに安全でない」——ターミナルの18件

ターミナルドメインでは、セキュリティ関連の挙動が意味論に書き落とされていたために、形式的に検証済みのプログラムが外部の安全オラクルで失敗した。失敗した18件は、特権アクセスと並行性に関わる弱点カテゴリ(CWE)に集中している。

ここで起きているのは検証器のバグではない。検証器は、与えられた仕様に対して正しく仕事をしている。問題は、検証の基準となる意味論が、本来捉えるべき対象挙動を捉えきれていなかったことにある。つまり証明は仕様に対しては健全だが、仕様そのものが現実のセキュリティ要件を覆っていない。

この失敗モードが示す教訓は明快である。形式保証の実効的な強さは、意味論のカバレッジ(網羅性)に依存する。どれだけ厳密な証明を積み上げても、その土台となる意味論が対象とする挙動を漏らしていれば、保証は穴の空いたものになる。

「安全だが何もしない」——ロボティクスの空虚な証明

ロボティクスでは、まったく逆方向の失敗が観測された。粗すぎる、あるいは制限的すぎる意味論が、形式的には安全だが機能的に空虚な解を促したのである。

検証器は、挙動を制限したり削除したりすることで、安全性質を空虚に(vacuousに)満たせてしまう。意味論が粗い抽象で書かれていると、修復エージェントは「安全だと証明しにくい動作を避ける」という逃げ道に頻繁に収束する。結果として、安全事後条件は満たすものの、意図した操作タスクを実行しないプログラムが出力される。初期パイプラインでの機能保持が0/20だったのは、この空虚な解への収束が原因である。

ここでの教訓は、カバレッジとは別の軸の話である。形式保証の実効的な強さは、意味論の忠実性(fidelity)にも依存する。意味論が現実の挙動を過度に単純化すれば、証明は「何も言っていない」に近づいていく。仕様が厳しすぎると、エージェントは「安全に証明できない動作をそもそも行わない」という方向へ逃げてしまうのだ。

2つの失敗モードを1つの表に整理すると、その対照が際立つ。

ドメイン 失敗の性質 意味論の状態 症状 教訓
ターミナル カバレッジ不足 捉えるべき挙動を欠落 検証は通るが外部オラクルに失敗(18件) 網羅性が保証の実効性を決める
ロボティクス 忠実性の過剰な制限 粗すぎ・制限的すぎ 安全だが機能的に空虚(機能保持0/20) 忠実性が保証の実効性を決める
CUDA どちらも該当せず API挙動と安全性質が構造化された形式化に適合 検証100/100・安全100/100・機能保持100/100 構造化できる領域では両立する

ターミナルとロボティクスは、正反対の方向から同じ結論に到達している。ターミナルは意味論が足りなすぎて失敗し、ロボティクスは意味論が厳しすぎて失敗した。そしてCUDAは、関連するAPI挙動と安全性質が比較的構造化された形式化を許すため、どちらの失敗モードも示さなかった。

quadrantChart
    title 意味論のカバレッジと忠実性が決める失敗モード
    x-axis "低忠実性(粗い・制限的)" --> "高忠実性(現実を正確に反映)"
    y-axis "低カバレッジ(挙動の欠落)" --> "高カバレッジ(網羅的)"
    quadrant-1 "理想:実効的な保証"
    quadrant-2 "失敗モードA:検証は通るが安全でない"
    quadrant-3 "両方の欠陥"
    quadrant-4 "失敗モードB:安全だが何もしない"
    "CUDA": [0.85, 0.9]
    "ターミナル": [0.7, 0.35]
    "ロボティクス": [0.25, 0.75]

2つの失敗モードは、形式検証を自動化する際の核心的なメッセージに行き着く。検証器が健全(sound)であることだけでは不十分である。実務的な保証の強さは、検証の基準となる意味論のカバレッジと忠実性の両方に依存する。

この視点は、導入で立てた「テストに全部通ったコードは本当に安全なのか」という問いに対する、形式検証側からの答えでもある。検証はテストより強い保証を与えるが、その強さは意味論の質によって上限が決まる。どの安全性質を形式化し、どの挙動を意味論に取り込むかという設計判断こそが、得られる保証の実効性を左右する最重要の変数なのである。

この研究がもたらす革新性と限界

MAGSは、LLMが生成したコードに機械的に検査できる安全保証を付与する。その価値は、単に「検証に成功した」という結果だけでなく、従来のどの手法と、どの設計判断で一線を画しているかを整理してはじめて見えてくる。同時に、コストと意味論の質という2つの制約が、実務での適用範囲を明確に限定している。ここでは革新性と限界の両面を、先行研究との比較を交えて確認する。

何が新しいのか(先行研究との比較)

第一の革新は反復修復(iterative repair)の質にある。Self-Refine(Madaan et al. 2023)は自己批評と改訂のパターンを確立し、Self-Debugging(Chen et al. 2024)は実行トレースをフィードバックする形に特化した。MAGSはこのループ構造を保ちつつ、フィードバックの内容をヒューリスティックやテスト結果から、明示的な安全仕様の違反を特定する演繹的検証器の出力へ置き換えている。自己判断だけでは改善が信頼できないという弱点を、機械的に検査可能な根拠で補う設計だ。

第二の革新は演繹的検証の前提条件を外したことである。AutoVerus、DafnyBench、Cloverといった従来の研究では、形式意味論がプログラムごとに外部から供給され、検証言語がプログラムの記述言語と一致している必要があった。MAGSは人間が書いた小さな論理基盤から出発し、各ドメインAPIが共有抽象状態に与える効果を自動形式化し、人間レビューの下で凍結したうえで、意味論的埋め込み(semantic embedding)を通して検証する。この構成により、ターゲット言語と検証言語が一致する必要がなくなった。

第三の革新はマルチエージェント編成の役割分担である。VeriGuard(Miculicich et al. 2026)は方針をオフラインで合成して実行時モニタで監視するが、保証は方針に付随し、検証されていないコードもモニタを通過する限り実行される。AutoVerus(Yang et al. 2025)はRustの証明注釈を修復するが、コードと仕様は入力として固定されている。ProofWright(Chatterjee et al. 2025)はKernelBench Level-1のカーネルで74%の安全性質を検証したが、単一のターゲット言語に束縛され、既に書かれたプログラムから始まり、証明義務の解消で終わるため実行可能な成果物を残さない。

研究 検証の起点 対応ドメイン 出力 報告された検証率
AutoVerus 固定されたコードと仕様 Rust単一 証明注釈 —
VeriGuard オフライン合成した方針 単一 実行時モニタ付きコード —
ProofWright 既存のCUDAカーネル CUDA単一 証明義務の解消 KernelBench L1で74%
MAGS 人間作の論理基盤+凍結意味論 CUDA・ターミナル・ロボティクス 検証済み実行可能プログラム 220/220(100%)

MAGSの差別化は、1つの共有表現で3つの異なる実行モデルに対応し、安全ポリシーを検証済みプログラムまで持ち込み、スケーラビリティと厳密性を左右する設計判断を報告している点に集約される。ProofWrightの74%とMAGSの100%は対比しやすい数字だが、対象タスク・検証条件・報告している指標が異なるため、単純な優劣として読むべきではない。

どこで使うべきか、どこに向かないか

MAGSが力を発揮するのは、安全性が重視される高リスクな生成である。GPUカーネル、システム操作スクリプト、ロボティクス制御のように、失敗のコストが極端に高い領域では、機械検査可能な保証を付ける価値が、支払うコストを上回る。

一方、日常的なコーディングや迅速なプロトタイピングには向かない。1サンプルあたり平均36〜68分、$8.60〜$10.17というオーバーヘッドは、試行錯誤を高速に回す用途とは根本的に相容れない。論文自身も、この手法は通常のコード生成より大幅なコストを負うことを明示している。

論文が挙げる今後の課題は3点に整理できる。自動形式化の品質とカバレッジの改善、機能の保持、そして検証コストの削減である。これらは、ターミナルで露呈したカバレッジ不足と、ロボティクスで露呈した忠実性の過剰な制限という2つの失敗モードに、それぞれ対応している。

より広い示唆として、MAGSはLLMコーディングシステムを「テストベースの自信」から「明示的・再利用可能・形式的に強制される安全仕様」へと進める道筋を示している。実務で導入を検討する際の第一歩は、検証ツールを選ぶことではない。まずどの安全性質を形式化するか(カバレッジ)を決めることが、得られる保証の実効性を左右する最重要の判断になる。

よくある質問

MAGSが扱う形式検証は、普段の開発でなじみの薄い概念が多い。ここでは、導入を検討するときに最初に浮かびやすい疑問を5つ取り上げ、実験で報告された数値を交えながら整理する。

形式検証(formal verification)とテストの違いは何か?

テストは「実際に試した入力」でしか正しさを示せず、見つけられなかった失敗の不在を保証できない。これに対し形式検証は、モデル化されたすべての入力についてプログラムが仕様を満たすことを、機械的に検査できる証明として示す。両者は測っているものが根本的に異なる。MAGSのターミナル実験でも、テストとは独立した安全オラクルが100件中18件の失敗を検出しており、「テストに通った」ことと「安全である」ことは同じではない。

Dafnyとは何か、MAGSはなぜそれを使うのか?

Dafnyは、事前条件・事後条件・ループ不変条件といった仕様を言語ネイティブに記述でき、静的検証器を備えた「検証対応プログラミング言語」である。書かれた仕様はBoogieを経由してZ3 SMTソルバーに渡され、証明義務が自動的に処理される。MAGSはこれを検証対応の中間表現として使うことで、CUDA・ターミナルスクリプト・ロボティクスという実行モデルのまったく異なる3ドメインを、同じ枠組みで扱えるようにしている。加えてDafnyはC#、Java、JavaScript、Go、Pythonなどへコンパイルできるため、検証済みコードを実務環境に持ち込める。

「100%成功」というのは誇張ではないのか?

論文が報告する100%は検証成功率であり、220例すべて(CUDA 100、ターミナル 100、ロボティクス 20)が、凍結された仕様に対するDafny検査済みの証明を出せたことを意味する。一方、独立した安全オラクルによる安全チェック成功率は202/220で、ターミナルは82/100にとどまる。「凍結された仕様は満たした」ことと「現実の安全性を捉えていた」ことは別物であり、論文自身がこの差を最重要の限界として提示している。

ロボティクスではなぜ機能が保たれなかったのか?

検証器は、挙動を制限したり削除したりすることで安全性質を空虚に(vacuousに)満たせてしまう。ロボティクスでは物理的な相互作用が粗い抽象で表現されるため、修復エージェントが「安全だと証明しにくい動作を避ける」空虚な解に収束し、機能保持は初期パイプラインで0/20だった。緩和策として、意味論拡張時に軽量なタスク固有の成功定義を導出し修復時の追加制約に組み込むことで11/20まで改善しているが、これは本質的な解決ではなく、あくまで自明に安全な修復を避けさせるための操舵制約である。

この技術は今すぐ実務で使えるのか?

コスト面から見て、日常的なコーディングや迅速なプロトタイピングには向かない。1サンプルあたり平均で36〜68分、$8.60〜$10.17のオーバーヘッドがかかるためだ(CUDA 36.4分・$8.60、ターミナル 36.3分・$9.01、ロボティクス 68.3分・$10.17)。現実的な適用先は、GPUカーネルやシステム操作スクリプトなど失敗のコストが極端に高い安全クリティカルな生成に限られる。また、保証の実効性は自動形式化された意味論のカバレッジと忠実性で決まるため、導入時の鍵は検証ツールの選定よりも、どの安全性質を形式化するかの設計にある。

まとめ

MAGSは、LLMコーディングエージェントが生成したコードに、機械的に検査できる安全保証を付与するマルチエージェントフレームワークである。中心にあるのは、プログラムが実行される言語と、その安全性が証明される言語を分離するという設計である。中間表現にDafny(仕様を言語ネイティブに記述でき、Boogie経由でZ3 SMTソルバーに証明義務を渡す検証対応言語)を使うことで、CUDA・ターミナルスクリプト・ロボティクスという実行モデルの異なるドメインを1つの枠組みで扱える。

運用面の鍵は、ドメイン意味論を一度だけ構築して凍結する2段階構造である。APIの意味論を自動形式化し、人間の監査とプローブテストを経て固定するため、以降のタスクでは検証失敗を「仕様を弱める」ことで解決できない。この凍結に加え、コード編集と仕様編集のエージェント分離、assumeや公理など証明回避構文の拒否が、検証器を騙す「ズル」への対策になっている。

実験結果は次の表に集約できる。

ドメイン 検証成功率 安全チェック成功率 機能保持
CUDA(100カーネル) 100/100 100/100 100/100
ターミナル(100スクリプト) 100/100 82/100 100/100
ロボティクス(20タスク) 20/20 20/20 11/20(改善前0/20)
合計 220/220(100%) 202/220 —

220例すべてでDafny検査済みの証明に到達した一方、失敗モードも明確になった。ターミナルの18件は、セキュリティ関連挙動を意味論が捉えきれていないカバレッジ不足による失敗であり、ロボティクスの機能保持0/20は、粗すぎる仕様が「安全と証明しにくい動作をしない」という空虚な解を促した忠実性の問題である。検証そのものは仕様に対して正しく行われており、検証器のバグではない。

したがって本論文の最重要の論点は、検証器が健全であることだけでは実務的な保証にならないという点にある。保証の実効的な強さは、検証の基準となる意味論のカバレッジ(何を捉えているか)と忠実性(どれだけ現実に即しているか)の両方で決まる。導入を検討する際も、どの安全性質を形式化するかを先に設計することが、検証ツールを選ぶこと以上に重要である。コストは1サンプルあたり平均36〜68分・8.60〜10.17ドルと大きく、日常的なコーディングや迅速なプロトタイピングではなく、失敗のコストが極端に高い安全クリティカルな生成に絞って使うのが現実的である。

参考資料

関連記事