生成AIによるより賢いAPIレビュー

本稿は「Smarter API Reviews With Gen AI」の抄訳です。
 

生成型AIの普及に伴い、企業は自社の環境に導入して何らかの工程を高速化できないか模索しています。おそらく、着手するのに最適な場所は、あらゆる工程における既存の課題であり、その状況にAIを適用する方法を検討することです。

(このブログ記事は本物の人間が書きました😜)

DALL·E 2024-01-05 12.15.13 - A futuristic, intelligent robot with a sleek design sitting at a computer, analyzing code diffs on a screen. The robot has a friendly, approachable ap

Dall-E 3画像の指示:未来的なデザインの洗練されたロボットがコンピュータの前に座り、画面上のコードの差分を分析している。このロボットは親しみやすく、近づきやすい外見で、画面にはハイライトされたセクションを含む複雑なコードが表示されている。この環境は、現代的なハイテクオフィスを思わせる。

 

歴史

The Qt Projectにおける大きな問題のひとつは、これまでリリース前にAPIレビューの期限を守ることでした。APIの追加や変更は、Qt Frameworkの使用方法に大きな変更をもたらすため、既存のAPIにこのような変更を加えると、ユーザーに支障をきたすことがよくあります。そのため、最終リリースにこれらの変更を加える前に、慎重に検討する必要があります。Qtに新しい機能を追加することは、新しいAPIを追加することを意味することが多く、将来的にユーザーにとって安定した、よく設計された新しいAPIとなることを望んでいます。しかし時には既存のAPIを変更する必要があり、その変更には十分な理由があり、ユーザーの互換性を損なわない代替案がないことを確認する必要があります。

長年この方法でうまく対応してきましたが、APIに大幅な変更が最初に統合されたものの、リリース時のレビューで最終リリース前に取り消しや大幅な変更が必要となったために、リリース予定日が遅れるという事態が何度も発生しました。API変更の実装と最終的なリリース準備の間に生じるこの遅延に対処するために、API変更のレビューのタイミングを開発サイクルのより早い段階に移行したいと考えました。しかし、どのようにすればよいのでしょうか?

この問題に関する最初の議論では、単純にヘッダーファイルの変更にタグを付け、手動でレビューを行うという案が中心となりましたが、これによって生じる混乱は過剰であると判断されました。このような解決策は、さらなる作業につながるだけでしょう。しかし、初期のコード分析の一部をAIに任せることで、少なくとも変更が「重大」と見なされるべきかどうかを判断することはできないでしょうか?

GPTとは?

GPTは「Generative Pretrained Transformer (生成型事前学習トランスフォーマ)」の略で、データ間の関係性を「理解」する複雑な数学モデルです。 皆さんが使用したことがあるかもしれないChatGPTのような用途では、言語データに基づいてトレーニングされたモデルが、言語内の単語同士の関係性をモデル化します。書籍、文書、ブログ投稿、音声記録の書き起こしなど、数十億語を研究することで、文脈に応じて1つの単語が別の単語とどのように関連するかをモデルが理解します。このようにして、新しい入力も理解し、単語ごとに出力を行い、人間が話すのと同様の応答を生成することができます。新しい単語を出力する際には、文脈を再確認し、その文で次に発生する可能性が高い単語を生成します。

現在ではLLM(大規模言語モデル)が数多く存在しており、OpenAIのGPT-4モデルもその一つです。GPTはこれまでに4つの主要なバージョンアップが行われており、それぞれが前モデルの機能、メモリ、理解力を上回るものとなっています。

GPTは従来、人間の言語理解に使用されてきましたが、これは単なるモデルであり、コードなどの他の種類のデータにおける関係性も理解できるように訓練することができます。つまり、コードのブロックについてチャットで話し、分析を依頼したり、コードをより深く理解するために、双方向の会話を行うことができるのです。

現在の状況

12月中旬より、弊社ではcodereview.qt-project.org に提出された変更を監視し、生成された差分を GPT-4 分析にかけるという、概念実証用のボットを運用しています。 生成型 AI は単独で動作するわけではないため、出力を生成するにはプロンプトが必要です。 プロンプトには、指示、リクエスト、文脈情報などを含めることができます。 以下は、API レビューで使用しているものです。指定されたコード変更の生の diff と共に使用します。

[省略] 「タスク:公開ヘッダーファイルの変更を、API の動作や使用法にとって重要かどうかで分類する。追加条件:公開ヘッダーの「private:」セクションへの変更は重要ではない。プラットフォームプラグインへの変更は、ファイルパスによって特定される場合があるが、重要ではない。空白のみの変更は重要ではない。」

GPTが関連する応答を提供できるようにするためのバックエンドのいくつかのトリックとともに、この動作はGPT-4を使用する際には一般的に信頼できることが証明されています。運用開始から約1か月間で、230件以上の変更に「APIレビューが必要」というハッシュタグが付けられ、それぞれに、パブリックAPIの使用と運用にとって重要な変更点についての簡単な分析が記載されています。

新しいAPIの追加の例

この例では、新しい機能がQRemoteObjectHostに追加されています。

diff --git a/.../qconnectionfactories.h b/.../qconnectionfactories.h
index b56a34f..7eabfd6 100644
--- a/src/remoteobjects/qconnectionfactories.h
+++ b/src/remoteobjects/qconnectionfactories.h
@@ -18,7 +18,7 @@
#include <QtNetwork/qabstractsocket.h>

#include <QtRemoteObjects/qtremoteobjectglobal.h>
-
+#include <QtRemoteObjects/qremoteobjectnode.h>

QT_BEGIN_NAMESPACE

@@ -116,6 +116,7 @@

Q_SIGNALS:
void shouldReconnect(QtROClientIoDevice*);
+ void setError(QRemoteObjectNode::ErrorCode);

protected:
virtual void doDisconnectFromServer() = 0;

Git diff: Show more

diff --git a/.../qremoteobjectnode.h b/.../qremoteobjectnode.h

index 18f75bc..13e2540 100644
--- a/src/remoteobjects/qremoteobjectnode.h
+++ b/src/remoteobjects/qremoteobjectnode.h
@@ -6,6 +6,7 @@

#include <QtCore/qsharedpointer.h>
#include <QtCore/qmetaobject.h>
+#include <QtNetwork/qlocalserver.h>
#include <QtRemoteObjects/qtremoteobjectglobal.h>
#include <QtRemoteObjects/qremoteobjectregistry.h>
#include <QtRemoteObjects/qremoteobjectdynamicreplica.h>

@@ -61,7 +62,8 @@
MissingObjectName,
HostUrlInvalid,
ProtocolMismatch,
- ListenFailed
+ ListenFailed,
+ SocketAccessError
};
Q_ENUM(ErrorCode)

@@ -181,6 +183,7 @@
~QRemoteObjectHost() override;
QUrl hostUrl() const override;
bool setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas=BuiltInSchemasOnly) override;
+ static void setLocalServerOptions(QLocalServer::SocketOptions options);

Q_SIGNALS:
void hostUrlChanged();

 

GPT-4は、新しいAPIの追加について、以下のように分析し、わかりやすくまとめています。

  1. qconnectionfactories.h には、新しい #include <QtRemoteObjects/qremoteobjectnode.h> ディレクティブが追加され、このヘッダーで以前は利用できなかった追加のシンボルやタイプが公開される可能性があります。この変更は、このヘッダーをインクルードするユーザーに影響を与える可能性があります。
  2. qconnectionfactories.h ファイルに setError(QRemoteObjectNode::ErrorCode) シグナルが追加されたことは、重要な変更です。これは、エラーコードを使用してエラーを通知するための新しい API を意味し、API を使用するユーザーがエラー状態を処理する方法に影響します。
  3. qremoteobjectnode.h の ErrorCode 列挙型に新しい SocketAccessError 列挙値が追加されたことは、重要な変更です。これにより、APIのユーザーが処理する必要のある新たなエラー状態が追加されます。
  4. qremoteobjectnode.hのQLocalServer::SocketOptionsオプションの静的void setLocalServerOptions() 関数がQRemoteObjectHostクラスに追加されたことは、重要な変更です。これにより、ローカルサーバーのオプションを設定するための新しいAPIが導入され、ユーザーがQRemoteObjectHostのインスタンスを構成する方法に影響します。

これらの変更は、既存のコードの動作(以前のエラーコードに依存していたり、新しい setError シグナルを想定していない場合)と、開発者が API を使用する方法(新しいメソッドや構成オプションの提供)の両方に影響します。

この場合、GPTは新しい動作を特定しますが、追加であるにもかかわらず、既存のユーザーのコードが新しいシグナルを処理する準備ができていない場合、問題を引き起こす可能性があることも指摘します。このような警告は、変更をマージする前に、より詳細な調査と、場合によってはドキュメントへの注釈が必要であることを確認者に思い出させるのに役立つでしょう。

既存のAPIへの変更の例

qvariantanimation.hの既存のAPIに簡単な変更が加えられた場合、ボットが認識するのは次のとおりです。

diff --git a/.../qvariantanimation.h b/.../qvariantanimation.h
index 640c057..4bdb971 100644
--- a/src/corelib/animation/qvariantanimation.h
+++ b/src/corelib/animation/qvariantanimation.h
@@ -26,7 +26,7 @@
BINDABLE bindableEasingCurve)

public:
- typedef QPair<qreal, QVariant> KeyValue;
+ using KeyValue = std::pair<qreal, QVariant>;
typedef QList<KeyValue> KeyValues;

QVariantAnimation(QObject *parent = nullptr);


GPT-4は、求められた場合、どのような変更が行われたかを後でレビュアーに伝えるために使用できる以下の分析を提供します。

「qvariantanimation.h において、KeyValue の typedef から using 宣言への変更により、基本型が QPair から std::pair に変更され、API ユーザーが KeyValue インスタンスとやりとりする方法に影響を与える可能性があります。」

初期の試み

生成型AIは気まぐれなものです。前述の通り、次の単語を推測し、それが止めるべきだと考えるまで、1つずつ、何度も繰り返します。(LLMの仕組みを確認してみてください。非常に驚くべきものです。)GPT 3.5はチャットには非常に優れていますが、話した内容をしばしば忘れてしまい、指示をうまく理解できません。また、「最近のバイアス」という問題もあり、プロンプトの後半の単語が前半の単語よりも重要であるとみなされることがあります。これは、一部の指示が無視される可能性があるため、少し問題です。さらに悪いことに、変更の差分が単に考慮されないまま、出力が生成されることもあります。そのため、GPT 3.5を分析に使用した際のヒット率はあまり高くありませんでした。より一貫性があり、独創性のない出力を要求する措置を講じても、依然としてリクエストの全体が不規則に無視されたり、変更で追加または削除された内容を完全にでっち上げることもありました。

DALL·E 2024-01-05 12.22.01 - A confident robot in an office, proudly presenting incorrect information on a whiteboard. The robot has an air of self-assurance, using a marker to un

Dall-E 3 プロンプト:オフィスで自信満々のロボットが、ホワイトボードに堂々と不正確な情報を提示しています。ロボットは自信に満ちた様子で、間違った答えを笑顔でマーカーで下線しています。この場面は少しユーモラスで、ロボットが不正確な答えに自信を持っていることを強調しています。環境は現代的なオフィスで、ホワイトボードには方程式やテキストが書かれており、一部は明らかに不正解とマークされていますが、ロボットは気づいていないようです。


11月、マイクロソフトはGPT 3.5の強化版である3.5-instructを発表しました。これは、その名の通り、命令に従う能力を向上させるはずのものでした。これはより効果的でしたが、モデルは依然として、実際にdiffで変更された内容についての幻覚に悩まされていました。

これらの問題のいくつかを回避するために、3つの候補のうち2つが変更の重要性を合意した上で結果を採用するという、ベスト・オブ・スリーのモデルが試されました。これにより、少なくとも全体的な精度は向上しましたが、出力は依然として詳細に欠け、ソフトウェア開発レベルでの変更を完全に理解できていないことが明らかでした。

GPT-4のコストが下がると、簡単なテストでも大幅に改善された結果が示されました。GPT-4は読み取った内容をよりよく記憶しており、決定の裏付けとなる自身の理由についてより明確にコメントできるようになっていました。精度が向上したため、このボットは単発構成に復帰し、それ以来、プロンプトの微調整のみが必要となっています。

次のステップとその後

AIは万全の策では決してありません。そうなるとしても、まだかなり時間がかかります。人間とは異なり、AIは自分が実際に何をしているのかを理解していません。文脈に応じた指示に対して、最も可能性の高い次の単語を選択することで応答するだけです。つまり、AIがどこかの時点で誤った選択をすると、その後の応答が誤った方向に進み、自信に満ちた、しかし非常に誤った回答が生成される可能性があるということです。この問題を軽減するための対策はいくつかありますが、時間とコストをかけずに実現できるものではありません。現世代の生成型AIには欠点がありますが、APIレビューの労力を軽減する取り組みは着実に進んでいます。

次に、変更の重要性を評価するために、より大きな文脈を探求したいと考えています。具体的には、現在行われているファイルごとの評価ではなく、変更内容を完全に理解できるように、複数のファイルを一括評価することを検討しています。さらに、GPTや関連するLLM技術の今後の改良により、ヒット率と指示の遵守が改善されることを期待しています。GPT-4は、このユースケースでは3.5を大きく上回っていますが、それでも間違いを犯したり、文脈を無視したりすることがあります。

まとめ

マージの承認時に変更ごとのレビューにシフトすることで、リリース直前の繁忙期におけるレビュー作業時間を節約することができます。また、この新しい方法では、変更の必要性を議論する際に、より多くの背景情報を提供できるため、その議論は一度で済みます。従来のレビューでは、かなり複雑なスクリプトがハードコードされたロジックの束を実行し、無関係なコード行を除外して、APIの変更をすべてレビュー用にまとめて捨てるコミットを作成していました。以前の方法では変更の概要を素早く把握することはできましたが、必要な背景情報が欠けていました。また、変更のソースを突き止め、その重要性を議論するには時間がかかる作業でした。変更が書き込まれてからAPIレビューが行われるまでにかなりの時間が経過する可能性があるため、各変更の理由を覚えておくのは難しいかもしれません。

この概念実証APIレビューボットは、The Qt Projectの全員がより簡単に貢献し、変更にふさわしい注意をより早く得られるようにするためのツールです。最終的には、すべての変更に対して人間の目による確認は依然として必要ですが、この新しいボットがその作業を少し楽にしてくれることを期待しています。


Blog Topics:

Comments