📊

AI Generated Coverage

に公開

開発における生成AIの活用を定量的に示したいと考えました。

誰が一番活用できているのか、どの部分では上手く活用できているのか、1か月前と比較してどう変化したのか。完璧でなくとも定量的な指標があれば比較して改善を繰り返したり、あるいは組織的な目標が立てやすくなります。

定量的な指標として生成AIサービスへの課金量や生成したトークン数、生成AIによるコミット数、PR数などがあります。しかし複雑なサービスでは生成AIによる差分と人間の差分は混じり合っており、活用を図る度合いとしてはやや大雑把です。

そこで「ソースコードの変更差分のうち生成AIによる差分の割合」を示す指標 AI Generated Coverage というものを考え、社内で測定しているのでご紹介します。

AI Generated Coverage とは

AI Generated Coverage は、とあるコード差分(本記事では GitHub の PR)のうち、生成AIによって生成された差分の網羅率を示す指標です。指標は 0% ~ 100% の間を取ります。高いほど、とあるコード差分が生成AIによって生成されたことを示します。生成AIの活用度合いを定量的に測ることを目的とした指標です。

本記事の測定では、計測の最小単位を「行」としています。トークンや AST レベルでの比較なども面白そうです。また後述するようにどこまでを「生成AIによる差分」とみなすかという決めの問題があります。

計測方法

前述のように本記事では「行」単位での測定をしています。この結果を AI Generated Line Coverage と読んでいます。 AI Generated Line Coverage は PR 単位でも、ファイル単位でも計測します。

これは以下の単純な分数によって算出されます。

AI\ Generated\ Line\ Coverage = \frac{PRのうちAIによって生成された差分の行数}{PRの差分の行数}

具体例を見てみましょう。

たとえば生成AIがコードを生成し以下のような差分が生成されたとします(コードにはあまり意味がないので読まなくて大丈夫です)。

+def calculate_totals(orders)
+  subtotal = orders.sum(&:item_price)
+  tax = subtotal * 0.10
+  shipping_fee = 500
+  total = subtotal + tax + shipping_fee
+  { subtotal: subtotal, tax: tax, shipping: shipping_fee, total: total }
+end

生成AIが生成したコードをみて開発者が以下の追加修正を手で加えました。

+def calculate_totals(orders)
+  subtotal = orders.sum(&:item_price)
+  tax = subtotal * 0.10
+  shipping_fee = subtotal > 10000 ? 0 : 500 # 開発者が修正した行
+  total = subtotal + tax + shipping_fee
+  { subtotal: subtotal, tax: tax, shipping: shipping_fee, total: total }
+end

さらに linter が以下の記法の修正を行いました。最終的な PR の差分は以下のようになります。

+def calculate_totals(orders)
+  subtotal = orders.sum(&:item_price)
+  tax = subtotal * 0.10
+  shipping_fee = subtotal > 10000 ? 0 : 500 # 開発者が修正した行
+  total = subtotal + tax + shipping_fee
+  { subtotal:, tax: tax, shipping: shipping_fee, total: } # linter による自動修正
+end

この結果 AI Generated Line Coverage は \frac{5}{7}=71.4…\% と算出されます。後述する社内測定ツールの結果で見るとわかりやすいです。

実際の算出画像

ところで「PRのうちAIによって生成された差分の行数」を測定するのは少々厄介です。
正確な実情を表現するには以下のような問題について考える必要があります。

  1. 生成AIが生成したコードに対して、人間が少し編集を加えた場合はどう評価するか?
  2. 生成AIが生成したコードを、人間がコピペして行移動をした場合はどう評価するか?
  3. 生成AIが生成したコードに対して、フォーマッターがフォーマットした場合はどう評価するか?

今回はあまり実装の時間もなかったので、単純に「PR全体の変更行数に対する、各ファイル内の編集の過程で生成AIが生成した行差分とPRの変更行が完全一致する行数の割合」を計測しています。

つまり前述の問に対しては以下のようになります。

  1. 生成AIの生成したコードではないと評価する。
  2. 同一ファイルであれば生成AIが生成したコードと評価する(されてしまう)。別ファイルであればされない。ファイル内での行の移動がロジックに大きく影響することは少ないので、その行移動後のコードを生成AI判定にすることは許容する。
    また人間が生成AIが生成した複数行のコードを行入れ替えしてロジックを修正した場合も生成AIのコードと判定されてしまうが、経験上そのような修正が起こることは稀なので許容する。
  3. 生成AIが生成したコードではないと評価する。これはやや厳し目の評価であり生成AIに求める性能ではないが、技術的な制約から許容している。トークンや AST での比較があればより正確な評価ができるかもしれない。

これは理想というより現実的な回避策です。これ以外にも様々な考慮や観点があると思います。

Claude Code での測定

AI Generated Line Coverage を最近流行りの Claude Code で測定する例を紹介します。

Claude Code の Hooks では Claude Code のファイルの編集イベントを取得することができます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/save-patches.sh"
          }
        ]
      }
    ]
  }
}

.claude/save-patches.sh は Claude Code が生成したパッチを保存するスクリプトです。編集途中の意図しない情報が保存されないよう注意してください。

#!/bin/bash

# 標準入力からJSONを読み込む
json_input=$(cat)

tool_name=$(echo "$json_input" | jq -r '.tool_name')

if [[ "$tool_name" == "Edit" || "$tool_name" == "MultiEdit" || "$tool_name" == "Write" ]]; then
    file_path=$(echo "$json_input" | jq -r '.tool_input.file_path')
    
    # gitignoreされているファイルは取得しないように
    if git check-ignore "$file_path" 2>/dev/null; then
        echo "$json_input"
        exit 0
    fi

    # 現在のブランチ名をキーとして取得し後ほど PR と照合
    branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "no-branch")

    # パッチ履歴を記録
    echo "$json_input" | jq -c '{
        session_id: .session_id,
        tool_name: .tool_name,
        file_path: .tool_input.file_path,
        type: .tool_response.type,
        content: .tool_response.content,
        structured_patch: .tool_response.structuredPatch
    }' >> "$jsonl_file"

    # あとで PR と照合できるように保存処理を書く。(略)
fi

# 戻り値は加工しないのでそのまま出力
echo "$json_input"

あとは GitHub Actions の script で PR の差分と保存したパッチをもとに、前述の計測方法で AI Generated Line Coverage を算出できます。

弊社では算出した AI Generated Line Coverage を PR のコメントに出して盛り上げたり、レポートでどの部分が生成AIによって記述されたかを詳細に確認したり、統計を取って生成AIの活用のための分析をしています。

PRへのコメント

詳細レポート

ソースコードは社内ツールと密結合なため公開していませんが、おそらくこの記事を Claude Code に読ませれば、みなさんの環境に合わせた同様の仕組みが作れるかと思いますので、興味のある方はぜひお試しいただくと楽しいかもです。

余談ですが、この先の世界では便利なオープンソースが共有されるのではなく、便利なオープン仕様定義書が共有され、各自がそれぞれの環境に合わせた最適実装をAIに作ってもらう世界になっていくのかもしれないですね。

AI Generated Coverage との向き合い方

AI Generated Coverage は一つの指標であり、生成AIの活用を完全に表現するものではありません。あくまで偏った数字です。ましてや、組織の生産性を直接表現するものではありません。

極端な話、README.md の1文字のタイポを修正するために、生成AIだけでコード差分を生成すれば AI Generated Coverage は 100% ですが、それは生産的ではないでしょう。

Test Coverage で 100% を目指すことに意味がないように AI Generated Coverage も 100% が絶対正義ではないと考えます。

Test Coverage のようにある程度の目安として使ったり、人や部署ごと、モジュールやプロダクトごとの値の差から何か示唆を得たり、改善の記録につかったり、あるいは ROI の算出などに使えるかも知れません。

個人的にはなにより数字を見て盛り上がり楽しむことが良いと思います。

皆さんの組織の AI Generated Coverage はいくつでしょうか?

We are hiring!

TAIANでは、このような開発・技術・思想に向き合い、未来をつくる仲間を一人でも多く探しています。少しでも興味を持っていただいた方は弊社の紹介ページをご覧ください。

https://taian-inc.notion.site/Entrance-Book-for-Engineer-1829555d9582804cad9ff48ad6cc3605

TAIANテックブログ

Discussion