生成AIはソフトウェアの産業革命になるのか
「Happy Elements Advent Calendar 2023」 12月24日の記事です。
はじめに
Happy Elements株式会社 カカリアスタジオの新規タイトル開発チームで働いているryoooです。新規開発と並行して社内ツールの開発も担当しています。
今年の春にGPT4に触れてから(日本ディープラーニング協会の)E資格を取得したり、LLMで自動でissueからPull requestを作成するGitHubアクションを書いてみたり、LLMによるテストコード自動生成ツールを開発するなどし、生成AIがどういう技術なのかを理解し、「LLMにはどれくらい高度なソフトウェア開発業務を任せられるのか?」について考える機会が増えました。
AIまわり、進化の速度もパないですよね。世界中の天才たちが持てる限りのリソースをぶちこんで競争しているこの勢いがどこまで続くのか、私達をどこまで連れて行くのかまったく想像できません。
そしてAIの進化はソフトウェア開発の新たな可能性を開きつつあります。
今年の春にGPT4をみて将来的にLLMによってソフトウェア開発業務の大部分が機械化される可能性を感じていたのですが、AIエディターのCursorやGitHubのCopilot workspaceが発表され少し現実味を帯びてきたように感じています。
本記事では、ソフトウェア開発の中でLLMに人間が行うような高度な設計・開発業務を任せることを考えた際、どのあたりが難しいのかについて、私が感じたことを書きます。
※ まだまだ人間の代替としては課題だらけであり、「LLMはあくまでも人間をサポートするツールである」という認識が前提ではありますが、LLMに人間が行うような高度な設計・開発業務をさせるにあたっての難しさを記します。
どういった課題があり、クリアするためにどういったムーブメントが今後発生しそうなのかについて考えながら、ゆく年くる年できればと思います。
GitHub Copilot workspaceについて
2023/11/8にGitHubから「GitHub Copilot workspace」を来年(2024年)にリリース予定との発表がありました。
発表の中でデモ動画がありましたが、こちらの発表は「RESEARCH PROTOTYPE」というステージの内容のため、まだまだ研究段階の状況とのことです。
まずはこれがどのような機能なのか利用の流れを簡単に紹介したいと思います。
- 🙎 [人間] GitHub issueを立てる。
- 🙎 [人間] issueに対してGitHub Copilot workspace起動ボタンを押下する。
- 🤖 [生成AI] issueの内容から解決策・開発方針・修正対象ファイルを検討しコメントする。
- 🙎 [人間] AIが考えた3の内容を確認し、必要に応じて修正して実装ボタンを押下する。
- 🤖 [生成AI] リポジトリ内の複数の対象ファイルを自動で修正し、Pull requestを作成する。
- 🙎 [人間] AIが実装した5の内容を確認し、実行ボタンを押下する。
- 🤖 [生成AI] リポジトリの内容をビルドしてGitHubのコードスペース内にデプロイされる。(ビルドでエラーが発生した際はAIで自動修正される模様)
- 🙎 [人間] デプロイされたアプリのURLにアクセスして動作確認ができる。
という開発体験を目標にしているようです。
簡単にいうと、「やりたいことを自然言語で書けば、生成AIがプログラムを修正してくれる仕組み」 がGitHub上に実装されるという話です。
長期的にこれが本当に高品質なレベルで実現すれば、ソフトウェア開発業務の中でLLMによって機械化される部分が今以上に大きくなると思われます。
※ 上記のような機械化のフローの中に人間の工程を挟む思想に加えて、GitHubは記事の中で以下のように書いており、GitHubとしても「人間をサポートするツールである」という姿勢をとっています。
Because LLMs are not perfect, we expect many tasks to require “last mile” finishes, and so Copilot Workspace lets you open the Codespace and pick up from where it left off, so that you can finish the task in a full cloud IDE with a secure runtime.
本当に高品質なレベルで実現すれば...
従来のソフトウェア開発において、プログラムはプログラマーが手書きで作成しています。
プログラミング以外のtest、format、静的解析、デプロイといった部分こそ自動化が進みましたが、プログラムを設計して書く業務自体は人間にしかできない領域でした。
しかしながら、もしも本当にもっと高度なソフトウェア開発業務を人間に匹敵する高品質なレベルでLLMが実現できるとすれば、これまで人間しかできなかった「リポジトリ全体に修正が及ぶような設計・開発業務」が、(エンジニア採用という手段に頼らずとも)スケール可能となるので、開発の中でLLMを上手に使う組織とそうでない組織でプロダクトの改善速度に大きな差が生じることになります。
生成AIはソフトウェアの産業革命になるのか
ハードウェアの産業革命
18世紀にイギリスで起こった産業革命も、それまでは家内制手工業で職人が製品を手作りしていた状況から、産業革命による機械化を経て、現代では工場全体がオートメーション化されるまで進化してきました。
ソフトウェアの産業革命
生成AIというスケール可能な手段によって、これまで人間が手作りするしかなかったソフトウェア開発業務の設計・コーディング業務を代替できるようになるとしたら、ソフトウェア開発はどんどんオートメーション化されることでしょう。そしてそれはソフトウェアにおける産業革命と言えるのではないでしょうか。
この話は現実的なのか
最近はChatGPTやCopilotの力を借りてLLMによる小さなプログラム生成の恩恵を実感している人も多いと思います。
X上にはアングリーバードのゲームのコピーを(画像などのアセットも含めて)完全に生成AIだけで開発した報告も上がってくるなど、小さなプログラムの開発では生成AIはプログラマを驚かせることもしばしばありました。(以下のアングリーバードは600行弱)
しかし、現実にLLMにより高度な開発業務(複数ファイルの修正を伴う開発業務)を任せるとなると、現時点では様々な課題があると感じています。
今年試したことについて
以下は、私が開発した機能についての記事で、この機能はGitHub issueを立てた際に生成AIがリポジトリ内のソースコードを参照し、修正・テストを行い、自動でPull requestを作成します。
この記事の中ではエージェント機構を使ってプログラマー役のLLMとレビュワー役のLLMでキャッチボールをしながらソースの修正を行い、ファイル一覧取得関数、ファイル参照関数、ファイル修正関数、テストコマンド実行関数などの関数を駆使してLLMがPull requestを作成するまでを自動化しています。
これを作ってみて感じたのは、GitHub Actions上でLLMを動かすよりも、ローカルPC上でLLMに開発サポートをさせる方が、人間にとって便利だということです。その結果をうけて、次に「LLMにテストコードを自動生成させるツール」を開発することにしました。その詳細については以下の記事をご覧ください。
この記事では、私が開発したghostestというgemを紹介しています。このgemをRailsプロジェクトに追加して、ターミナルでghostestを起動した状態で人間がRailsアプリ本体のコードを実装すれば対象ファイルのテストをLLMが自動で生成するというものになります。
また、GitHubのPull requestを作成すると、自動でLLMがコードサマリーを書き、コードレビューも行うツールも開発しました。
今年試したことの中で考えたこと
これらの試みを通じて、LLMに人間しかできなかったような高度なソフトウェア開発業務を任せることを考えた際の主な課題点について以下に記述します。
(なお、これらの試みはいずれもAzureのGPT4-32kを利用しています。)
1. トークンがもっと必要
32kのトークンに、ファイル一覧・参考ファイル内容・コンパイルエラーの内容・ファイル修正履歴などがスタックされていくので、参考ファイルが巨大になるにつれてトークンが足りなくなります。
業務利用するような巨大なリポジトリの修正業務となると、非常に大きなコンテキストを理解させる必要があるため、トークン数の上限解放(もしくは後述する別の工夫)が継続的に必要になります。
2. タスクを細分化してそれぞれ最適化する
LLMの出力精度はシステムプロンプトの出来に大きく左右されます。
テストを書かせるなら「テストを書く際に考えること、テストを実装する手順」を(Few-shotなども取り入れて)事細かくシステムプロンプトに書けば精度が上がりますし、レビューをさせるならレビューの観点を適切にプロンプトに含めなければ思った通りの出力は得られません。
前述のテストコード自動生成ツールでは、「テストプログラマー」と「レビュワー」以外に、「テストケース設計エンジニア」という一般には聞き慣れない専門家を作りました。「必要なテストケースを考えるだけのエージェント」として「テストケース設計エンジニア」を設定することで全体の品質の向上を試みました。
実際これにより、テストプログラマーはテストケースを作成する必要がなくなり、指示されたテストケースを実装することだけに集中できるためシステムプロンプトが書きやすくなり、精度が上がったように感じています。
現実世界ではテストケースを考えるだけのエンジニアは多くの場合で採用しませんが、LLMエージェントを活用する世界においては、業務を細かく分けて細分化して各業務を行うエージェントのシステムプロンプトを個別に最適化することで精度をあげられるという現実世界と違った面白い特性があると感じました。
そのため、RubyプログラマとUnityプログラマを分けることで精度が上がる可能性があります。さらに、GPT4を使うのではなく、細分化したエージェントごとに専門のファインチューニングを行ったLLMモデルを使うことが可能なら、さらに精度を上げることができると考えられますし、いずれそうなっていくように思います。
※ 例えばRubyプログラマーとしての精度を高めるために、システムプロンプトを工夫するだけでなくRuby言語に特化したLLMを使わせることで精度を上げるイメージ。
このような流れが考えられるため、今後は優秀なLLMモデルを有しているプログラミング言語の方が新規プロジェクトで選択されやすいといったこともあるかもしれないと感じました。
3. 命名が重要
プログラミングに関わる人にとって命名が重要なのは言うまでもありませんが、LLMにプログラムを生成させることを考えたときにプログラム内の変数・関数・クラスの命名はこれまで以上に重要なものになると思います。
というのも、LLMはコードを理解する際に変数・関数・クラスの名前から推測を行うため、正確で具体的な英語の名前をつけないと、LLMに誤った解釈を与えてしまうという事象が観察されました。
具体的には、前述のテストコード自動生成ツールにおいて、Agentのフィールド名がtypeという名前の状態だとテストが正しく生成されなかったのですが、フィールド名をroleに変更すると正しいテストが生成されました。
これは、typeという単語の意味が広範囲すぎてLLMが何のタイプなのか解釈しきれなかったものと思われます。
実装を詳しく見れば役割を意味するtypeであることはわかるのですが、現在のLLMはそこまで自己懐疑的にソースを読まないため、より明確なroleという名前に変更する必要がありました。
4. 英語力が重要
私の認識では現在のLLMについては日本語よりも英語の方が精度が高いため、システムプロンプトはもちろん、LLMへの指示やソースファイル内のコメントについても英語で記述したほうが精度がよいようです。(トークン効率がよく料金的にも英語のほうが安い)
※ テストコード自動生成ツールについても、テスト内容の説明文を日本語で出力させたら精度が下がり、英語で出力させたら精度が上がる現象がみられました。
日本語LLMの完成度も今後高まるとは思いますが、最高品質のLLMを活用したいのであれば今後も英語での利用が前提になると思われます。
ソースコードのコメントは日本語でいい派でしたが、LLMが開発に本格的に参入するとなると英語化が求められるのかもしれません。
となると、issueやPull requestやRFCやADRも似たような話があるのですが...
5. ライブラリのバージョン差を扱うのが難しい
テストコード自動生成ツールについては、gemのファイルを参照する関数もLLMに実装していたのですが、rspecなど著名なgemについてはLLMがもともと知識を持っているため、gemのソースファイルを参照せずLLMの持っている知識で解決しようとしていました。
※ これ自体は、Rubyだと目的の関数がどのファイルに実装されているのかわかりづらいことが多いですし、メタプログラミングされたrubyのコードを読むよりも生成精度的には良いと思われます。
しかしながら、LLMが持っている知識が古いバージョン向けの知識だったりすると自己解決できずエラーと修正のループに陥ることがありました。
このあたりは任意のライブラリの任意のバージョンのドキュメントをLLMから(RAG方式などを使って)読めるようにする必要があると思われます。
6. あらゆる開発ツールはLLMからも利用されることを前提と考える
昨今のプロジェクトではだいたい達成できているかもしれませんが、lint・format・test・buildといった開発業務の中で使うコマンドやツール群については、コマンドラインから実行可能にしておくと自動化の余地が広がると思います。
特にフォーマットやlintについて、システムプロンプトでコーディングルールを記述してLLMが生成するコードにルールを守らせるよりも、LLMには自由にコーディングさせてツールで自動修正するようなアプローチの方が良いでしょう。
自動で修正できなかったものについては、エラーメッセージ内でLLMにとってわかりやすい修正方法が示されていればLLM自身で修正できると思うので、大量のエラーを同時に出力せず少量ずつエラーを表示するなど、LLMが読むことを前提にしたオプションも必要かもしれません。
テストについても同様で、表示されたエラーメッセージをもとにLLMがソースの修正を行うので、LLMにとって読みやすいメッセージにする工夫が必要かもしれません。
GUIツールについても、コマンドラインから実行できるクチが今以上に求められるかもしれません。
7. 疎結合な方がLLM的に扱いやすい
前述したようにLLMにはトークンの制約があるため、大きなコンテキストを前提にした修正は苦手です。
そのため、クラスや関数の責務が明確に分かれていて影響範囲が狭ければ、それだけLLM活用の幅が広がると思われます。
もしかしたら、リポジトリについてもモノレポのような巨大なコードベースにするのでなく、マイクロサービス的な小さな機能ごとにリポジトリを分けた方が(LLMが作業対象を明確に見つけやすくなるため)LLMによる開発の恩恵を得やすくなるかもしれません。
8. チームの考え方やこれまでの経緯をどう理解させるか
LLMの考えがチームの考えに寄ればそれだけLLMによる開発の恩恵を得やすくなりますが、チームの考えをどのようにLLMに伝えるのがよいでしょうか。
このあたりについては、Notionなどドキュメント管理サービスがRAG(Retrieval Augment Generation)用のAPIを提供(普通にほしい) してくれれば、LLMからナレッジを検索してプロンプトに含められるので、LLMがチームドキュメントを活かすことに繋がりLLMの精度向上に役立ちそうです。
いずれにせよ、ADRやRFCなどチームの考えや実装の経緯がわかるドキュメントを蓄積しておくことはLLMの利用でも有利になるのではないでしょうか。
9. 今以上にテストが重要になる
仮に本当にLLMによって開発がスケールするような状況になったとして、「ユーザー目線で何が正しいのか?」は結局人間にしか定義できないと思います。
もし開発業務がスケールしたとしても、Pull requestを確認してマージする人間のエンジニアがボトルネックになると思われるので、そのあたりの対策を重視しなければ安心してリリースできない状況に陥りそうです。
結局「上位のレイヤーで担保したいこと」を考えるのは引き続き人間の重要な業務になりますし、ビジュアルリグレッションテストなどできるだけ人間がプログラムの正常性を確認しやすくなるような仕組みが重要性を増してくるように思います。
10. 量より質
また、仮に本当にLLMによって開発がスケールするような状況になったとしても、バンバン機能を追加すればよいかというとサービス運営の観点からはそうはならないと思います。
結局ユーザーがサービスを理解する速度は変わらないので、(不具合の修正速度は上がるとよいと思いますが)機能を追加する速度は今とさほど変わらないでしょう。
大切になるのは量でなく品質になると思われます。
人間のプログラマーによる開発ではプロトタイプを開発するにも時間がかかるため、チーム内でプロトタイプをさわってレビューやブラッシュアップをする回数は増やしづらかったかと思いますが、本当にLLMによって開発がスケールできるとなれば(さながらNightly prototypingといった速度感で)機能追加のプロトタイプを数パターン作り直して使用感のブラッシュアップに時間を使い、丁寧に仕上げてからリリースすることに注力できそうです。
これが実現すれば大きなユーザーにとっても大きなメリットにつながります。
11. プログラミング言語の進化はあるのか
現在のプログラミング言語はLLMが扱うことを前提に最適化されておらず、人間が読み書きすることを前提に設計されています。
仮にLLMによる開発が本格的に進化していくならば、プロジェクトとしてはLLMによる開発の恩恵を得られる言語を採用したくなるでしょうし、LLMにとって最も生産性が上がる言語はどんな言語なのか考えてみたいと思います。
この点についてはまったく見えておらず手応えはないのですが、以下のあたりが重要になるのかも?という気はしています。
- コンパイラのメッセージがLLMにとって読みやすい。(もしくはLLM用の出力オプションがある)
- その言語専用にファインチューニングされたLLMモデルが存在する。
- メタプログラミングが少ない。
LLMが考えることを減らしてあげる必要があるので、可能なら静的にコード生成したほうが良いと思います。もしくは、メタプログラミングがあっても動的に生成されるコードに関するしっかりしたドキュメントがあればLLMにとっても扱いやすいのかもしれません。 - 関数やクラスの責務がLLMにとってわかりやすい。
- ファイルのすべてを読まずとも、関数やクラスの概要がわかる(トークンの節約ができる)仕組みがある。(ファイルが長すぎる場合や、クラスの実装が複数ファイルに分かれているような場合は、実装を読み込ませるのが困難)
Autogenerated APIの世界へ
2023年現在では上記のような点で難しさがあり、今後はこのあたりを解消しつつ進化を重ねていくことと思われます。
まだまだ産業革命というには課題がたくさんある状況ですが、LayerXの方の以下のスライドではとてもエキサイティングな概念が紹介されていました。
これはドラえもんみたいな夢物語じゃなくて、生成AIの進化速度やポテンシャルはここまでのパラダイムシフトも現実的なものとして視野に入っているんだと感じています。
コードは”事前に”書いておくものではなくなる
コードを瞬時に生成できるとすると、究極的にはリクエストに合わせてコードを書くことも可能
Autogenerated Frontend / Autogenerated APIの世界
「Softwareの機能が事前に固定されているのは、携帯電話にキーボードが固定されていたのと同じだ」
おわりに
「LLMに人間しかできなかったような高度なソフトウェア開発業務を任せる」という目標に対して、2023年時点でのGPT4ではまだ課題が多く残っています。
しかし、現在の生成AIの進化速度は驚異的であり、ソフトウェア開発の中でLLMがもっと中心的な役割を果たすような時代が到来することに、私は期待を寄せています。
個人的には自ら考え手を動かしてコーディングするのが好きなのですが、既に起こってしまったイノベーションを止めることはできないからこそ、今後も時代の流れを楽しみたいと思っています。
最後まで読んでくださりありがとうございました!
Discussion