
はじめに
こんにちは、サービス戦略課の宮崎です。
コミットする時、避けて通れないのがコミットメッセージです。どんな時でも意味のあるコミットメッセージを書かなければいけませんが、考えるの大変ですよね?
そこで作成したのがAIコミットメッセージ自動生成ツールです。文字通りAIがコミットメッセージを自動で生成してくれるツールですが、言うなれば反応型AIエージェントに分類されるでしょうか。
使い勝手の良いツールになったので、今回はその紹介をします。
なぜ生成AIでコミットメッセージを作るのか
コミットメッセージが必要な理由
日々の細かなコミットは開発者個人の裁量に委ねられ、重大なコミットはレイアウトから書き方までキッチリ決まっているバージョン管理ツールのような運用をしている企業は多いのではないでしょうか? 日々の開発効率を楽にしてくれてバージョン管理的な側面も兼ね備えているのであれば、それは良い開発運用体制と言えるでしょう。
問題はバグやデグレが発生した時です。
コミットメッセージに詳細な情報が与えられていたとしたら、非開発者であったとしても問題になってる機能からその周辺範囲までアタリを付けて探すことができるかもしれません。もちろん熟練の開発者にとっても有益な情報であることに疑いはありません。
あるいは障害が出る前、リリース前、レビュー前の段階で、コミット内容から程度の状況を把握することができれば、コードに問題がないか事前に察知する確率は飛躍的に向上することでしょう。
良いシステムは良いドキュメントから始まるということです。
コミットメッセージの規格
元々はコミットメッセージを自動で作成してプッシュまでする構想だったのですが、PoC(概念実証)を繰り返して行くと色々と使えるシーンが増えそうだという感触を得ました。
まず、コミットメッセージには大別してsubjectとdescriptionがあります。
- subject は「主題」という意味で、git の概要やタイトルのことです。Fork のような git ツールでは git ツリーの画面が subject で表示されています。その git がどんな変更をしているか簡潔に記述するもので、通常は50文字以下が推奨されているそうです。 
- description は「詳細」という意味で、git の変更内容を詳細に説明するものです。72字で改行するのがマナーだそうで、10行ほどが一番読みやすいそうです。 
ここまで「〜そうです」と書き連ねてますが、実は git リファレンスにコミットメッセージの記述方法のガイドラインはありません。そのため、オープンソースプロジェクトや大規模開発PJの場合は独自のコントリビューションガイドラインを設計することもあります。例: Angularのコミットメッセージガイドライン
ただ、いくら開発者がガイドラインを遵守したところで品質のバラつきは必ず出てきます。それでもルール化された誰にでも読みやすい形で書かれるのであれば、コミットメッセージ自体が有益な情報資産であることに疑いはありません。ならば、生成AIでコミットメッセージを作ればどうなるか?
これが今回のPJの着想です。
生産性向上効果
生産性向上効果も狙っています。メッセージの明瞭さに留まらず、時間削減効果で言うと、
コミットメッセージ作成時間30秒 * 100回コミット/日 = 50 分/日 * 20 営業日/月 = 16.6h 削減/月!!!
という目論見を立てています。これから紹介するサンプルコミットメッセージを(内容を考える時間も含めて)30秒で書ける人はそうそういないと思うので、結構良い線行ってるんじゃないかと思ってます。
実際にツールを作ってみた
1. 仕様策定
生成AIでコミットメッセージを作成するにあたり、まずは実現可能性と製品選定から探っていきました。言語は Python で、setup.py を使ってローカルPCにインストールする形式です。
アーキテクチャとしては単純で、git diff HEADでコードの変更箇所のみ Amazon Bedrock の LLM にプロンプトを投げて、コミットメッセージを返してもらうというのがメイン機能になります。
エムオーテックスでは様々な製品を日々開発・改良しておりますが、中でもLANSCOPEエンドポイントマネージャークラウド版は AWS を利用して開発していますので、Amazon Bedrock の LLM を利用することしました。
PoC で複数のモデルとの比較検証を経て選定されたAIモデルは、
- 日本語能力に秀でていること
- Scala、Python、CloudFormation コードを中心に正確に解釈してくれること
- Conventional Commits 規則に則って指定のフォーマットに整形して出力してくれること
- コスト観点
等々の理由から、Amazon Bedrock Claude 3.5 Sonnetを現時点で最も適しているモデルであると判断して採用しました。LLM の性能向上に伴い今後も更新されていく予定です。
コミットメッセージのフォーマットについては様々なリファレンス規約を検討した結果、Conventional Commitsを採用しました。RFC 2119 で採用されている SemVer(Semantic Versioning) 規格に則っていることが主な理由ですが、上述の Angular Commit ガイドラインにインスピレーションを受けて作成されたという背景からも、WEBサービスであるLANSCOPEエンドポイントマネージャークラウド版の開発者と相性も良いと考えたのも採用理由の一つです。
2. プロンプトエンジニアリング
LLM は適切なプロンプトを投げることで適切な回答を得ることができます。
下記は PoC 段階で実際に使っているプロンプトになります。ここに至るまで何度か更新しており、今後も更新される可能性はありますが、一例として参考にしてください。
以下のステップに基づいて、出力を生成してください。 1. 与えられたGit diffの中身を確認してください。 2. Conventional Commitsの規則に基づいて、適切な"commit_type", "commit_scope", "commit_subject", "commit_discription"を考えてください。 3. 出力例に基づいたフォーマットで、それらのプロパティを出力してください。 出力を生成する上での注意事項を以下の<warnings>タグに記載します。 <warnings> - "commit_scope"は必須ではありません - "commit_subject", "commit_discription"は日本語で書いてください - "commit_subject"は1行で書いてください - "commit_discription"は必ず個々の変更をリストで書かず、説明文で書いてください - "commit_discription"は必ず10行未満で書いてください - "commit_discription"は必ず句点の後で改行を入れてください - "commit_discription"は必ず1行につき最大72文字に制限してください - "commit_discription"な必ず名詞で終わる必要があります </warnings>
お気づきでしょうか? プロンプト内で出力フォーマットに関する命令を与えていますが、基本的な構文については「Conventional Commitsの規則に基づいて」という部分で表現しています。
Claude 3.5 Sonnet が Conventional Commits 規則を学習しているからこそ、このような簡単なプロンプトでも実現できるのです。これも採用理由の一つでした。
Claude 3.5 Sonnet の入力制限は最大200Kトークン(約80万文字)になるため、初回作成コミットや画像ファイルの混入がなければ大体のコミットは賄えると思います。
3. 実証実験
下記はサンプルコードの SAMテンプレート yaml ファイルに対して、1行だけ変更したドキュメント部分をgit diff HEADで表示した内容です。
@@ -1,7 +1,7 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > - SAM Template for chatbot to Cloudwatch. + SAM Template for chatbot to Cloudwatch Alarm on high alert.
この変更に対してコミットメッセージを完結に書こうとしたら「一旦コミット」「微修正」「ドキュメント更新」のように書きがちではないかと思います。
では、実際に上記の変更部分だけを生成AIに渡して、コミットメッセージを作成するように依頼してみます。すると数秒で下記のような返答が返ってきました。
docs (sam) SAMテンプレートの説明文を更新 SAMテンプレートのDescription部分を修正し、より具体的な内容に更新。 CloudWatchのアラーム機能に関する言及を追加し、テンプレートの目的を 明確化。
如何でしょうか? パッと見るだけでSAMテンプレートのドキュメント部分を更新したということが subject(1行目)だけで明確に分かると思います。尚且つ変更箇所と変更内容だけに留まらず、変更の意図まで汲み取って description(空白を挟んだ3行目以降)を記述しています。
どんな修正をしたか? ということだけでなく、なぜ修正したか? ということを汲み取ってくれるのは非常に助かります。コミットした事実は変更箇所だけ見れば分かりますが、コミットした理由は内容を精査しないと分からないからです。
4. コミットする前に
最初に言及した通り、「元々はコミットメッセージを自動で作成してプッシュまでする構想」でしたが、この時に想定していたコミットメッセージは subject だけでした。実装段階で description も生成できて精度も良いので機能拡張を目指しました。
ユースケースとユーザーストーリーを考えると、少なくとも PoC段階ではコミットに留めてプッシュはしない方がベターだということで、現在の形に落ち着きました。AI を使った開発といっても、誰が使うかを想定するのはシステム開発の現場においては変わりませんね。
LLM はハルシネーション(尤もらしい嘘を吐く)がありますが、最近のモデルは解釈の違いなんじゃないかと思うくらい正確になってきました。それでも大量のコードを修正をすると過不足や意訳をすることもあるため、やはり最終的には自分でチェックする必要があります。そこでプログラム実行時に下記のチェック機構を設けました。
[0] コミットせずに処理を終了する。 [1] AI で生成された subject/description で自動コミットする。 [2] 自分で編集してコミットする。 数字を入力して Enter を押下してください。: 1 [INFO] 『[1] AI で生成された subject/description で自動コミットする。』が選択されたため、コミットを実行します。 [INFO] Committing Message... [INFO] コミットが成功しました。
こうすることで開発者が意図しないメッセージをコミットする可能性が少なくなります。
アーキテクチャの変遷
PoC に至る前にアーキテクチャの変遷もありました。下記は初期フローチャートですが、最初は Amazon Bedrock のモデルIDやプロンプトを構成ファイル化してユーザーに更新可能にさせたり、コミットメッセージの正確性などを測定する方法を考えていました。

しかし、前述の通りユーザーストーリーやユースケースを考えると「ただコマンドを打って返信が返ってくるシンプルな構成」が一番良いということに落ち着き、PoC に入る前には下記のフローチャートのような処理になりました。PoC 関連処理は省略していてここからもう少し変わりますが、初期に比べるとシンプルな処理になったことが分かります。

AWS構成図で描くと下記のようになります。このレベルの構成図を描くことはそうそう無いのですが、本当に簡単な構成のツールになりました。反応型AIエージェント概念図と表現した方がいいかもしれません。

メンテナンス性も含めてツールらしくシンプルな構成にするのが、目的を明確化させる上でも良い取り組みだと考えました。
おわりに
簡単な変更には簡単なメッセージしか残さない、という人は多いのではないでしょうか? そのようなコミットメッセージが溢れかえると、どれだけ重大な変更であってもコミットツリーだけでそれを判別することが難しくなります。
作業平準化を目指してルール化やガイダンス化をしても追いつかないところはどうしても出てきますが、コミットメッセージはコーディング上のラストワンマイルです。
生成AIをエージェントとして扱うことで組織のレベルアップを図れる好例として、今回の取り組みをご紹介させて頂きました。
