← ブログに戻る

Knowledge Graph×CI: PRレビューコメント3ヶ月で62%減の実測

先に念のため差別化から。

過去に書いた Knowledge Graphでコードレビュー範囲を9割削った実測 は、AIレビューに投げるファイル数を91%削った話でした。KPIは「トークン」と「AIレビューの騒がしさ」です。

今回はもう一歩CI寄りで、人間レビュアーがPRに書き込んでいたコメントの数が対象。90日回したら62%減っていた、という話です。KPIも機構も違います。KPIは「人間コメント数/PR」、機構は「retrospective」ではなく「毎コミットKG差分再構築 + CI自動投稿」。

同じKGを別の面から絞ってる、と言い換えても良いです。

なぜCIに寄せたのか

前回の記事を書いたあと、社内でひとつだけ問い合わせが増えました。「その仕組み、AIじゃなくて人間レビュアーの負担も減るんですか?」。

正直、最初は「減らない」と答えるつもりでした。人間レビュアーの指摘は文脈依存で、KGでどうにかなる範囲は狭いはずだ、と。

でもログを見直したら考えが変わりました。人間レビュアーが90日でつけたコメントを300件ぶん分類したところ、45%が「これ、他のここも直したほうがいい」「この関数、実は呼ばれてる別ファイルにも影響が出る」の類だったんです。

つまり、レビュアーが手で影響半径を追いかけて、それをコメント欄で共有していた。ここはKGが得意な仕事です。

CI差分再構築 + KG影響半径コメント: 90日で人間コメント62%減の構造

アーキ: 差分再構築 + 影響半径コメント

構成はシンプルです。

  1. main へのpushで完全build (10秒級、tree-sitter 19言語)
  2. PR単位で 差分再構築 — 変更ファイルだけAST再パース、ノード/エッジをKGに上書き
  3. CIジョブが blast-radius クエリを走らせ、影響半径ノードを YAML で吐く
  4. reviewdog が YAML を PR コメント欄に整形して投稿

差分再構築が肝で、tree-sitter 側の incremental parsing を素直に使うと変更ファイルあたり100〜300ms、300ファイルの中規模リポで初回buildが約4分、差分updateが2〜5秒に収まります。Neo4j側のCypher UPSERT は差分あたり数十msなので、CI待ち時間はほぼ tree-sitter コストです。

肝心のクエリはこんな感じです。

MATCH (f:File)-[:CHANGED_IN]->(pr:PR {id: $prId})
MATCH (f)<-[:CALLS|IMPORTS*1..2]-(caller:File)
WHERE NOT (caller)-[:CHANGED_IN]->(pr)
RETURN caller.path, count(*) AS hopStrength
ORDER BY hopStrength DESC
LIMIT 10

2-hopまで見に行くのは、hop 3以降は影響がノイズに埋もれるからです。前回の記事のblast-radius分析と同じ設計思想で、色は red/orange/yellow の3段だけ残しました (green/blue はコメントしない)。

CIコメントの形が全て

これがコケると全部台無しなので、コメント形式は3回書き直しました。

最終形はこうです。

### KG影響半径 (自動)

このPRで変更された `payment/refund.py` は次の2ファイルから2-hop以内で呼ばれています:

- 🟠 `payment/webhook.py` (直接: `refund()` 呼び出し, 3箇所)
- 🟡 `admin/dashboard.py` (間接: `PaymentService` 経由, 1箇所)

**影響ファイルを一緒にレビューしたい場合**: `.reviewdog.yml``blast_radius: expand` を有効にしてください。

3つ守った点があります。

  1. 上限 10 ファイル — 30も並べると誰も読まない
  2. hopStrength でソート — 呼び出し回数の多い順、心理的にトリアージしやすい
  3. 提案は 1 個だけ — 「もっと知りたければ設定変えて」だけ添える。CIコメントが説教くさくなると即無視される

Danger.js でも同じことができますが、reviewdog は Go 製で軽く、既にlint統合で入っていたので流用しました。

3ヶ月の実測

対象: 中規模Nextアプリ、PR 137本、レビュアー実働3名。

指標導入前 (90日)導入後 (90日)
人間レビュアーのコメント数/PR平均 7.2平均 2.7-62.5%
「他のファイルも直したほうが」型コメント3.20.4-87%
PR初回レビュー着手までの中央値6h 12min2h 41min-57%
PR完了までの中央値47h22h-53%
CIの追加コスト (時間)+11s / PR
CIの追加コスト (金額)+0円 (self-hosted)

「他のファイルも直したほうが」型が87%減ったのが、内訳としては一番効いていました。人間はこの手のコメントを書くのに1件2〜4分使うので、単純な工数削減としても週あたり数時間浮きます。

副作用も1つ。CIコメントに慣れると、人間レビュアーがKG影響半径をチェックしなくなる。当たり前ですね、CIがやってくれているので。ただし、KGが腐ると気付かないまま影響半径が壊れるリスクが上がります。ここは後述します。

落とし穴: KGは腐る

これは前回別記事にも書いたのですが、Knowledge Graphは3ヶ月で腐る — 37.7%のノードが90日で dead になっていました。

CI連携の場合、腐りが影響するのは主に2種類。

  • 動的import系の取りこぼし — Pythonの importlib、TypeScriptの import() は tree-sitter 静的解析では捕まえられない。呼ばれているのに影響半径から漏れる
  • 削除ファイルの残骸 — CIの差分再構築で「追加」「変更」は追いつくが「削除」の後始末が甘いと、ノードが KG に残ってコメントにゴーストが出る

対策として月1回の cron 監査ジョブを別に立てました。

MATCH (n:File)
WHERE n.updatedAt < date() - duration({days: 30})
   AND NOT (n)<-[:LINKS_TO|IMPORTS|CALLS]-()
RETURN n.path, n.updatedAt

出てきたリストは自動で削除しません。Slack に投げて、私が「削除/復活/そのまま」の3択で判断します。復活する場合は必ず1本以上リンクを張るルール。

自動削除にしなかったのは、初月に自動削除で必要なノードを2本消して肝が冷えたからです。

実装ステップの落とし穴 3つ

同じ構成をやる方への申し送り。

  1. 初回buildは PR CI で走らせない — 4分待たされるとレビュアーが怒る。main の push か nightly job で
  2. 差分再構築のロックを忘れない — 並列PRが同じファイルを触ると Cypher UPSERT が競合する。KG別インスタンス or ファイル単位ロックのどちらか
  3. KGコメントは CI 別ジョブにする — メインの test 実行と混ぜると red がKGコメント遅延の原因になる。分離すれば独立に fail する

3番だけは、私が最初やらかしました。KGコメントが遅くて test も遅い、と言われ続けて2週間。

KGは「もう1人のレビュアー」ではない

書いていて気付いたのですが、KG影響半径コメントは「もう1人のレビュアー」ではありません。レビュアーの手作業を先回りする道具、が正確です。

もう1人のレビュアーが増えたら、コメント数は減らないはずです。むしろ増える。減ったのは「人間がやっていた手作業のうち、機械が確実にやれる部分をKGが引き取った」からです。

前回記事のAIレビュー91%削減が「AIに投げる前段の絞り込み」だったのに対し、今回は「AIレビューの後段、あるいは手前で人間の作業を減らす」用途です。同じKGを、別々の面から絞っています。

そういう意味では、code-KG はもう「1つの機構」ではなく、レビュー全工程に薄く敷いてある基盤層、と呼ぶほうが実態に近い気がします。

まとめ

  • 90日で人間レビュアーのコメント数を 62%削減
  • 効いたのは「他のファイルも見て」型 (-87%)
  • CIコスト +11秒/PR、金額差ほぼ0
  • KGは腐るので、月1監査は必須

KGをここまで実務に寄せた設計論と、CI以外の応用 (エンプラRAG、コード検索、影響半径での障害調査) をまとめた本があるので、続きが気になった方は ナレッジグラフ実践入門 をどうぞ。CIの詳細セットアップは第8-10章です。

ナレッジグラフ活用大全 関連書籍 ナレッジグラフ活用大全 ナレッジグラフ 活用大全 | GraphRAG・Neo4j・RDF・Property Graph・Emotion AI の実践書 書籍ページを見る →