自律型AI開発ループでAdaコンパイラを構築する

最近、私はAdaで書かれたAdaコンパイラ adac を開発しています。

現在、このコンパイラが対応しているのはAda 2022のごく小さな部分集合だけです。 分離されたフロントエンド、抽象構文木、意味解析、独自の中間表現、ネイティブ バックエンドを経由して、最小限のプロシージャをコンパイルできます。現在の ネイティブバックエンドは、x86-64 LinuxおよびFreeBSD向けのプログラムを 生成します。

この説明は技術的には正確ですが、私がこのプロジェクトで最も興味を持っている 部分が抜けています。

私にとってadacは、単に新しいAdaコンパイラを作る試みではありません。同時に、 自律型AI支援ソフトウェア開発の実験でもあります。

中心となる問いは次のとおりです。

リポジトリ自体がアーキテクチャ、エンジニアリング規則、検証要件、作業選択 方針、停止条件を定義しているとき、AIエージェントはシステムプロジェクトで 一貫した長期的進展を実現できるでしょうか。

これは、大きなプロンプトからAIにコンパイラを生成させることとは異なります。 何かが動いているように見えるまで、生成されたコードを無造作に受け入れること とも異なります。

目標はエンジニアリング規律を取り除くことではありません。AIエージェントが その中で作業できるよう、十分な規律をリポジトリに組み込むことです。

運用契約としてのリポジトリ

多くのソフトウェアリポジトリには、ソースコード、テスト、場合によっては ロードマップが含まれています。adacでは、リポジトリが開発作業の進め方も 定義します。

プロジェクト文書には、アーキテクチャ境界、所有権規則、失敗の分類、対応対象、 検証要件、マイルストーン完了基準が記述されています。

AGENTS.md ファイルは、自律開発ループを定義しています。一般的な実装指示は、 おおよそ次のような手順を開始できます。

  1. 作業ツリー、最近のコミット、ロードマップ、関連文書を調査します。
  2. 既知の基準状態を確立するため、リポジトリ全体の検査を実行します。
  3. まだ満たされていない最も早いロードマップ前提条件を見つけます。
  4. その前提条件を前進させる、最小の完全な作業項目を選択します。
  5. 契約、成功基準、影響を受ける境界、必要なリファクタリングを定義します。
  6. 契約が変わる場合は、実装前に文書を更新します。
  7. 変更を完全な垂直スライスとして実装します。
  8. 正常系と異常系のテストを追加します。
  9. 対象を絞ったテストを実行し、その後に完全な検証スイートを実行します。
  10. 完全なdiffを確認し、一貫性があり検証を通過した変更だけをコミットします。
  11. ロードマップを再評価し、次の作業項目へ進みます。

実装がすでに許可されている場合、エージェントは計画を作成しただけで停止しては なりません。文書化された停止条件に到達するまで作業を続けます。

停止条件も明示されています。たとえば、既存の契約からアーキテクチャ上の判断を 安全に導けない場合、破壊的操作に承認が必要な場合、ユーザーの変更と選択した 作業が重なる場合、または環境で結果を検証できない場合には、作業を停止します。

これは重要です。停止規則のない自律開発は、本当の意味での自律エンジニアリング ではありません。単なる監督されていない変更にすぎません。

「コンパイラを生成する」ことではありません

コンパイラは浅い実装に耐えにくいため、この実験の対象として適しています。

パーサーがいくつかの構文を受け入れるだけでは不十分です。言語機能は複数の 異なる段階を通過しなければならない場合があります。

source
  -> lexer
  -> parser
  -> AST
  -> semantic analysis
  -> custom IR
  -> backend
  -> executable

機能がパーサーにしか存在しないのであれば、その機能は未完成です。

正しく表現され、検証され、低水準表現へ変換され、出力され、テストされる必要が あります。また、ソースプログラムが無効な場合には適切に拒否されなければなりません。 内部コンパイラエラーは通常のソース診断と区別できなければなりません。未対応の ターゲットは、誤解を招く出力を生成する前に失敗しなければなりません。部分的な ビルド成果物を成功結果として公開してもいけません。

これらの要件により、エージェントが互いに接続されていないコードで進捗の錯覚を 作り出すことは難しくなります。

リポジトリは垂直な機能スライスを明示的に要求します。実装する各機能は、診断と テストを含め、必要となるすべてのコンパイラ段階を通過しなければなりません。

この規則は、AIが生成したシステムコードでよく見られる失敗を防ぐためのものです。 つまり、もっともらしい構成要素が多数あっても、信頼できるシステムを形成して いない状態を防ぎます。

最近の作業:機能より先に基盤を整える

外から見えるAdaの部分集合は、意図的に小さく保っています。

現在のadacは、nullreturn などの単純な文を含む最小限の プロシージャをコンパイルできます。実用的なAdaプログラムにはまったく不十分であり、 利用可能な汎用コンパイラとして紹介できる段階ではありません。

最近の開発では、安全に拡張するために必要な基盤へ重点を置いています。

その作業には次のものが含まれます。

  • 明示的なコンパイルコンテキスト
  • コンテキストが所有する診断状態
  • 構文ノード、シンボル、意味エンティティの安定した識別子
  • 構文オブジェクトに付加されたソース範囲
  • コンパイラ段階の境界における検証
  • 上限のあるソース入力
  • 上限のあるASTノード生成
  • インターンされたシンボル増加の上限
  • 不正または過剰な入力の制御された拒否
  • 決定論的なテスト
  • 中断された自律作業を回復するための規則

これらは、利用者から見て刺激的な機能ではありません。印象的なデモプログラムを 作れるようになるわけでもありません。

しかし、後になって高コストになる問題に対処しています。所有権の不明確さ、 プロセス全体のグローバル状態、無制限のメモリ増加、無効な内部表現、意図しない 部分出力、中断後に安全に再開できない開発セッションなどです。

自律的に開発されるシステムでは、この基盤は構文対応範囲を急速に拡大すること より重要かもしれません。

AIエージェントは機能を素早く追加できます。より難しいのは、追加のたびに リポジトリが、次のエージェント実行によって調査、理解、検証、継続できる状態に 保たれることを保証することです。

リソース制限も正しさの一部です

最近取り組んだ領域の一つが、明示的なリソース制限です。

現在のコンパイラには、ソース文字数、ASTノード数、異なるインターンシンボル数に 対する上限方針があります。これらの制限は、隠れたグローバル動作ではなく、 コンパイルコンテキストに属します。

このように小さなコンパイラに対しては、時期尚早な堅牢化に見えるかもしれません。 しかし、これには複数の目的があります。

第一に、ソースコードは信頼できない入力として扱わなければなりません。不正または 敵対的なソースファイルによって、内部構造が無制限に増加してはなりません。

第二に、リソース枯渇には定義された失敗経路が必要です。アロケータのクラッシュ、 無効なコンパイラ状態、部分的に公開された出力ファイルへつながってはいけません。

第三に、明示的な制限によって動作をテストできます。テストでは意図的に小さな 予算を使用し、内部ストアを破壊せず、正しい境界でコンパイラが失敗することを 確認できます。

最後に、リソース契約は自律エージェントに、より明確な枠組みを与えます。失敗の 可能性が見える場所へ曖昧な防御チェックを追加するのではなく、所有権と観測可能な 動作が定義された、文書化済み方針を維持しなければなりません。

責任は依然として人間にあります

これを自律開発と呼んでも、人間がいなくなるわけではありません。

私は引き続き長期的な方向を定義します。どの製品目標が重要かを決めます。 アーキテクチャ上の選択を確認し、リポジトリ契約を調整し、必要に応じて作業を 停止または方向転換します。

この実験は、どのようなソフトウェアが存在すべきかをAIが独立して決定できるかを 試すものではありません。

人間が個々の変更を毎回選択し監督しなくても、AIがエンジニアリングループの 意味ある部分を実行できるかを試すものです。

有用な自律エージェントは、次のことができる必要があります。

  • 古い会話コンテキストに依存せず、現在の状態を調査する
  • 文書化された前提条件から作業を選択する
  • リファクタリングが構造的に必要な時点を認識する
  • 契約と実装を一緒に更新する
  • 完全な結果を検証する
  • 一貫した変更だけをコミットする
  • 中断後に回復する
  • 判断する権限がない場合に停止する

これは「AIが単独でソフトウェアを構築できる」という主張より限定的ですが、 より具体的で検証可能でもあります。

なぜAdaなのでしょうか

Adaがこのプロジェクトに適している理由はいくつかあります。

Adaは、明示的なインターフェース、強い型付け、制約された表現、正しさに関する 慎重な推論を促します。これらの性質はコンパイラ実装に役立ちますが、AIが生成する 変更を制約しようとするときにも役立ちます。

Adaには成熟した言語仕様もあり、そのコミュニティは正確な意味論、移植性、安全性、 長期的な保守性を重視する傾向があります。

そのため、成功した玩具的な例の陰に隠れることは難しくなります。adacが有用な コンパイラへ成長するには、構文認識以上のものが必要です。信頼できる意味解析、 検証された中間表現、正しい実行時動作、定義されたABI境界、複数ターゲットへの 規律ある対応が必要です。

したがって、Adaはこの開発実験に非常によく適しています。

長期的な方向

長期目標は、独自のターゲット非依存中間表現を備えた、セルフホスティング可能な クロスプラットフォームAdaコンパイラです。

ロードマップには次の項目が含まれます。

  • より広範なAda 2022フロントエンド対応
  • スカラー型、式、制御フロー、サブプログラム呼び出し
  • 型付き中間表現と制御フロー中間表現
  • 信頼できるネイティブバックエンド
  • パッケージと分離コンパイル
  • 複合型とストレージモデル
  • 例外、ジェネリック、タグ付き型
  • 段階的なセルフホスティング
  • WebAssemblyを含む追加ターゲット
  • オプションのLLVMバックエンド
  • スタイルチェッカーやフォーマッターなどのツール

ロードマップは日付ではなく能力を基準にしています。マイルストーンは、その契約、 実装、検証器、診断、テストが文書化された完了基準を満たした場合にのみ完了します。

この違いは重要です。自律開発は、大量のコミットを作ることや、表面的な機能名を チェックすることに最適化すべきではありません。検証済み能力に最適化すべきです。

私が実際に検証していること

この方法が機能すると主張するには、まだ早すぎます。

コンパイラは依然として小さく、最も難しい言語機能の多くはこれからです。名前解決、 オーバーロード解決、パッケージ、ジェネリック、例外、ストレージ管理、分離コンパイル、 ABIの詳細、セルフホスティングは、アーキテクチャと開発プロセスの両方へ、より大きな 負荷を与えるでしょう。

注目すべき失敗は、明白なコード生成ミスとして現れないかもしれません。

次のような形で現れる可能性があります。

  • 個別には妥当に見える多数のコミットを通じて進行するアーキテクチャの漂流
  • 実装と一致しなくなった文書
  • 契約ではなく例だけを検証するテスト
  • 長期的な保守性を損なう局所的な修正
  • 十分な根拠のない過剰なリファクタリング
  • 大きなdiffを避けるための不十分なリファクタリング
  • 停止すべき状況でも継続する自律ループ
  • 未完了の作業と完了済みの作業を区別できないエージェント

これらはまさに、リポジトリで定義したプロセスによって明らかにしたい問題です。

したがって、adacは同時に二つのプロジェクトです。

一つはAdaコンパイラです。

もう一つは、どれだけのエンジニアリング判断を、リポジトリ状態、実行可能な検証、 AIエージェント向けの明示的な運用規則として表現できるかを探る試みです。

コンパイラは目に見える成果になります。

開発履歴のほうが、より興味深い成果物になるかもしれません。

ソースコードはGitHubで公開しています。

github.com/hodong-kim/adac