AIコーディングエージェント向けのGitHub PRスタイルのコードレビュー
Potatoでインラインdiffコメント、ファイル単位の品質評価、承認・却下の判定を備えたGitHub PRスタイルのコードレビューアノテーションを設定し、コーディングエージェントの出力を評価する方法です。
コードレビューのアノテーションが必要な理由
コーディングエージェントのベンチマークの多くは、評価を二値に落とし込んでいます。テストが通ったかどうかです。SWE-benchは解決したissueの割合を報告し、HumanEvalはpass@kを報告します。これらはリーダーボードには使えますが、コードの品質を理解する役には立ちません。
エージェントはすべてのテストを通しながら、誰も保守したくないコードを書けます。セキュリティホールのあるコード、遅い経路、コードベースの他の部分と噛み合わないスタイルなどです。人間のレビュアーなら、テストが緑でもそのPRには変更を要求します。人が実際にマージするコードを書くエージェントが欲しいなら、テストを走らせるだけでなくコードそのものをレビューする必要があります。
Potatoのcode_reviewアノテーションスキーマは、GitHubのPRレビュー体験をアノテーションツールに持ち込みます。アノテーターはシンタックスハイライト付きの統一diffを見て、diffの行をクリックしてインラインコメントを付け、いくつかの品質軸でファイルを評価し、approve・request-changes・commentのいずれかの判定を下します。実際のpull requestをレビューするのと同じ流れです。スキーマの完全なリファレンスは、コーディングエージェントアノテーションのドキュメントとエージェント評価ガイドを参照してください。
Potatoのコードレビューインターフェースです。インラインdiffコメントとファイル単位の評価が表示されています。
インラインdiffコメントとファイル単位の品質評価を備えたPotatoのコードレビューインターフェース
コードレビュースキーマの概要
code_reviewスキーマは3つの層で構成されています。
- インラインdiffコメント:アノテーターがdiffの任意の行をクリックして、カテゴリ付きのコメント(bug、style、performance、security、logic、suggestion、question)を付けます
- ファイル単位の評価:変更された各ファイルに対して、正確性(1〜5)とコード品質(1〜5)を独立に評価します
- 全体の判定:アノテーターが最終判定を下します。approve、request changes、comment onlyのいずれかです
実際のコードレビューをそのままなぞる形なので開発者には馴染みやすく、生成される構造化データはそのままコードレビューモデルの訓練に対応します。
CodingTraceDisplay:diffの描画方法
CodingTraceDisplayコンポーネントは、コーディングエージェントのトレースをツール呼び出しとその出力の連なりとして描画し、ファイル編集を特別に扱います。エージェントがファイルを編集すると、次の要素を含む統一diffが表示されます。
- 赤い行:削除された行(
-が先頭に付きます) - 緑の行:追加された行(
+が先頭に付きます) - グレーの行:コンテキスト行(変更なし)
- 行番号:旧・新の両方の行番号をガター部分に表示
- シンタックスハイライト:ファイル拡張子に基づく言語対応のハイライト
- クリックでコメント:任意の行をクリックすると、その行に紐づくコメントフォームが開きます
diffはエージェントの編集操作から自動的に計算されます。エージェントが検索置換ツールを使った場合、Potatoは変更前後の状態を復元して統一diffを生成します。
1つのトレース内で複数のファイルを編集するエージェント(現実のバグ修正ではよくあることです)の場合、ファイルごとに折りたたみ可能なdiffセクションが用意されます。GitHub PRの「Files changed」タブと同じ形です。
CodingTraceDisplayは、コードの変更を適切なシンタックスハイライト付きで描画します。
シンタックスハイライト付きの統一diffとファイルツリーのサイドバーを描画するCodingTraceDisplay
コメントカテゴリ
アノテーターがdiffの行をクリックしてコメントを追加するとき、カテゴリを1つ選びます。
| カテゴリ | 色 | 説明 | 例 |
|---|---|---|---|
bug | 赤 | コードに機能的な誤りがある | 「userがNoneの場合、ここでNullPointerExceptionが発生します」 |
style | 青 | コードスタイルや規約の問題 | 「このプロジェクトの関数名はcamelCaseではなくsnake_caseです」 |
performance | オレンジ | 非効率なコード | 「ループの中でデータベースに問い合わせています。バッチクエリを使ってください」 |
security | 紫 | セキュリティ脆弱性 | 「ユーザー入力がサニタイズされずに直接SQLクエリへ渡されています」 |
logic | 黄 | 即座には失敗しないかもしれないロジックの問題 | 「この条件は>ではなく>=にすべきです。境界でオフバイワンになります」 |
suggestion | 緑 | 誤りではなく改善の提案 | 「ここはコンテキストマネージャを使うとリソース管理がすっきりします」 |
question | グレー | 確認が必要 | 「このimportはなぜ追加されたのでしょうか。使われていないように見えます」 |
各コメントには自由記述の本文もあり、アノテーターは実際のPRコメントを書くのと同じように問題点を詳しく説明します。
ファイル単位の評価
各ファイルのdiffをレビューしたあと、アノテーターは2つの軸で評価します。
正確性(1〜5):
- 1:動作しない、新たなバグを持ち込んでいる
- 2:部分的に動作するが、大きな問題がある
- 3:正常系では動作するが、エッジケースを取りこぼしている
- 4:正しく動作するが、軽微な問題がある
- 5:完全に正しく、エッジケースも適切に処理している
コード品質(1〜5):
- 1:保守不能、構造がない
- 2:品質が低く、スタイルや設計に大きな問題がある
- 3:許容範囲、基本的な規約には従っている
- 4:品質が高く、整理されていて読みやすい
- 5:優れている、イディオムに沿っていて、ドキュメントも十分
判定の選択肢
すべてのファイルをレビューしたあと、アノテーターは3つの判定から1つを選びます。
- Approve:そのまま、あるいは些細な修正だけでマージできる状態です
- Request Changes:マージ前に相応の修正が必要です
- Comment Only:マージの可否を決めずにフィードバックだけを残します
これはGitHubの3つのPRレビュー状態にそのまま対応します。
セットアップ手順
ステップ1:コーディングエージェントのトレースを変換する
コーディングエージェントのトレース形式はさまざまです。ここではよく使われる3つのエージェントの例を挙げます。
Claude Codeから(JSONエクスポート):
python -m potato.trace_converter \
--input claude_code_sessions/ \
--output data/code_traces.jsonl \
--input-format claude_codeAiderから(チャット履歴):
python -m potato.trace_converter \
--input aider_logs/ \
--output data/code_traces.jsonl \
--input-format aiderSWE-Agentから(トラジェクトリのディレクトリ):
python -m potato.trace_converter \
--input swe_agent_trajectories/ \
--output data/code_traces.jsonl \
--input-format swe_agent_trajectoryコンバータは標準化されたJSONL形式を出力します。各行にはタスク、エージェントのステップ、ファイルのdiffを含むトレースが入ります。
{
"trace_id": "swe_bench_123",
"task": "Fix the TypeError in django/db/models/query.py when calling .values() on an empty QuerySet",
"repo": "django/django",
"base_commit": "a1b2c3d",
"steps": [
{
"type": "thought",
"content": "Let me look at the QuerySet.values() method to understand the TypeError."
},
{
"type": "action",
"tool": "search",
"input": "def values",
"file": "django/db/models/query.py"
},
{
"type": "observation",
"content": "Found at line 234: def values(self, *fields):\n ..."
},
{
"type": "action",
"tool": "edit",
"file": "django/db/models/query.py",
"old_content": " def values(self, *fields):\n clone = self._clone()\n clone.query.set_values(fields)",
"new_content": " def values(self, *fields):\n clone = self._clone()\n if not fields and not self.query.values_select:\n return clone\n clone.query.set_values(fields)"
},
{
"type": "action",
"tool": "edit",
"file": "tests/queries/test_values.py",
"old_content": "class ValuesTests(TestCase):",
"new_content": "class ValuesTests(TestCase):\n def test_empty_queryset_values(self):\n \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n qs = Author.objects.none()\n result = list(qs.values())\n self.assertEqual(result, [])"
}
],
"files_changed": [
{
"path": "django/db/models/query.py",
"diff": "--- a/django/db/models/query.py\n+++ b/django/db/models/query.py\n@@ -234,6 +234,8 @@\n def values(self, *fields):\n clone = self._clone()\n+ if not fields and not self.query.values_select:\n+ return clone\n clone.query.set_values(fields)"
},
{
"path": "tests/queries/test_values.py",
"diff": "--- a/tests/queries/test_values.py\n+++ b/tests/queries/test_values.py\n@@ -1,4 +1,10 @@\n class ValuesTests(TestCase):\n+ def test_empty_queryset_values(self):\n+ \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n+ qs = Author.objects.none()\n+ result = list(qs.values())\n+ self.assertEqual(result, [])"
}
]
}ステップ2:コードレビュースキーマを設定する
config.yamlを作成します。
annotation_task_name: "Coding Agent Code Review"
data_files:
- "data/code_traces.jsonl"
item_properties:
id_key: "trace_id"
text_key: "task"
# Display coding agent traces with diff rendering
display:
type: "coding_trace"
trace_key: "steps"
diff_key: "files_changed"
syntax_highlighting: true
show_line_numbers: true
collapse_large_diffs: true
max_uncollapsed_lines: 200
annotation_schemes:
- annotation_type: "code_review"
# Inline comment categories
# File-level ratings
# Overall verdict
# Annotator settings
annotator_config:
allow_back_navigation: true
# Output settings
output:
path: "output/"
format: "jsonl"ステップ3:アノテーションサーバーを起動する
potato start config.yaml -p 8000http://localhost:8000にアクセスします。最初のコーディングエージェントトレースが表示され、タスクの説明、エージェントの推論ステップ、シンタックスハイライト付きのファイルdiffを確認できます。
ステップ4:アノテーターの作業手順
典型的なレビューの流れは次のとおりです。
- タスクを読む:エージェントが何を指示されたかを把握します(例:「django/db/models/query.pyのTypeErrorを修正する」)
- トレースを確認する:エージェントの推論ステップを追い、そのアプローチを理解します
- 各ファイルのdiffをレビューする:
- シンタックスハイライト付きのdiffを読む
- 任意の行をクリックしてインラインコメントを付ける
- コメントカテゴリ(bug、style、performanceなど)を選ぶ
- 問題点を説明するコメント本文を書く
- 正確性(1〜5)とコード品質(1〜5)でファイルを評価する
- 判定を下す:approve、request changes、comment onlyから選びます
- 送信する:「Submit」をクリックするか、Ctrl+Enterを押します
キーボードショートカットで作業が速くなります。
| ショートカット | 動作 |
|---|---|
j / k | ファイル間を移動 |
c | 選択中の行にコメントを開く |
1-5 | 現在の評価軸にスコアを設定 |
a | 判定をapproveにする |
r | 判定をrequest changesにする |
Ctrl+Enter | レビューを送信 |
エクスポート形式
送信された各レビューは、構造化されたJSONオブジェクトになります。
{
"trace_id": "swe_bench_123",
"annotator": "reviewer_01",
"timestamp": "2026-03-22T14:32:11Z",
"review": {
"inline_comments": [
{
"file": "django/db/models/query.py",
"line": 236,
"side": "right",
"category": "logic",
"body": "This early return skips set_values entirely, but if fields are provided later via .values('name'), the previous empty .values() call will have returned a clone that never went through set_values. Consider checking if this clone is still valid downstream."
},
{
"file": "tests/queries/test_values.py",
"line": 5,
"side": "right",
"category": "suggestion",
"body": "Consider adding a test case for .values() followed by .values('name') to verify the chaining behavior after your fix."
}
],
"file_ratings": [
{
"file": "django/db/models/query.py",
"correctness": 3,
"code_quality": 4
},
{
"file": "tests/queries/test_values.py",
"correctness": 4,
"code_quality": 4
}
],
"verdict": "request_changes"
}
}この構造化形式は、コードレビューモデルの訓練にも集計分析にもそのまま使えます。
分析:レビューデータを扱う
レビューの読み込み
import json
import pandas as pd
from pathlib import Path
reviews = []
for f in Path("output/").glob("*.jsonl"):
with open(f) as fh:
for line in fh:
reviews.append(json.loads(line))
print(f"Loaded {len(reviews)} code reviews")コメントカテゴリの分布
from collections import Counter
all_comments = []
for rev in reviews:
for comment in rev["review"]["inline_comments"]:
all_comments.append(comment)
category_counts = Counter(c["category"] for c in all_comments)
print("Comment categories:")
for cat, count in category_counts.most_common():
print(f" {cat}: {count}")ファイル評価の平均
ratings = []
for rev in reviews:
for fr in rev["review"]["file_ratings"]:
ratings.append(fr)
ratings_df = pd.DataFrame(ratings)
print("Average ratings by file:")
print(
ratings_df.groupby("file")[["correctness", "code_quality"]]
.mean()
.round(2)
.to_string()
)判定の分布
verdict_counts = Counter(rev["review"]["verdict"] for rev in reviews)
total = sum(verdict_counts.values())
print("Verdict distribution:")
for verdict, count in verdict_counts.most_common():
print(f" {verdict}: {count} ({count/total*100:.1f}%)")エージェント別のバグ率
トレースにagentフィールドが含まれていれば、エージェント間でバグ率を比較できます。
agent_bugs = {}
for rev in reviews:
agent = rev.get("agent", "unknown")
bug_count = sum(
1 for c in rev["review"]["inline_comments"]
if c["category"] == "bug"
)
if agent not in agent_bugs:
agent_bugs[agent] = []
agent_bugs[agent].append(bug_count)
print("Average bugs per review by agent:")
for agent, bugs in sorted(agent_bugs.items()):
print(f" {agent}: {sum(bugs)/len(bugs):.2f} (n={len(bugs)})")活用例
コードレビューモデルの訓練
Potatoのコードレビューアノテーションから得られる構造化されたインラインコメント、ファイル評価、判定は、自動コードレビューモデルの訓練データに向いています。各レビューが提供するものは次のとおりです。
- 特定のdiff行に紐づいた局所的なフィードバック
- カテゴリ分けされた問題(bugかstyleかperformanceか)
- 複数の粒度(行、ファイル、全体)での品質シグナル
これはCodeRabbitやGraphiteのAIレビュアーといったツールが使うのと同じデータ形式ですが、LLMから蒸留したものではなく人間の専門家が生成したものです。
SWE-benchでのコーディングエージェント評価
SWE-benchが教えてくれるのは、エージェントがissueを解決したか(テストが通ったか)であって、そのコードがマージできるかどうかではありません。SWE-benchの解答にコードレビューアノテーションを適用すれば、きれいなコードで解決したエージェントと、その場しのぎで解決したエージェントを見分けられます。実際の開発者体験と相関する、より細かいリーダーボードが作れます。
コード品質データセットの構築
多数のトレースにまたがるコードレビューデータを集計すると、AI生成コードによくある品質上の問題を集めたデータセットが作れます。こうしたデータセットの用途は次のとおりです。
- ありがちなミスを避けるためのコード生成モデルのファインチューニング
- AI生成コード特有のパターンに向けたリンターの構築
- 人間のレビュー前にエージェント出力の問題箇所を洗い出す分類器の訓練
まとめ
Potatoのcode_reviewスキーマは、GitHubのPRレビューの作業手順をエージェント評価の中に持ち込みます。集まったインラインコメント、ファイル評価、判定は構造化されたコード品質データであり、合否だけのテスト結果よりずっと多くを教えてくれます。コードレビューモデルを訓練するにせよ、SWE-benchのきれいな解答とその場しのぎの解答を切り分けるにせよ、エージェントの品質基準を作るにせよ、必要になるのはこのデータです。