コードグラフ × AIエージェントで「リファクタリング自動化」を組む実装パターン
リファクタリングを自分でやる時代は、そろそろ終わるかもしれません。少なくとも私の体感では、2026年に入ってからの数か月で、AIエージェントの「コードベース理解」が一段深くなりました。
理由はシンプルで、AIエージェントがコードを グラフ として扱い始めたからです。grepと埋め込みベクトルだけでは届かなかった「変更の影響範囲」や「依存関係の核」を、エージェントがクエリで取り出すようになっています。
本記事は、その実装パターンを書きます。コードをグラフ化する基礎は別記事に譲り、ここでは「AIエージェント × コードグラフ」の組み合わせで、どうやってリファクタリングを半自動化するかに絞ります。

なぜ「コードグラフ × AI」なのか
私はずっと、リファクタリングを「勘」でやってきました。「この関数、なんか嫌な予感がする」みたいなアレです。それで何度か正解もしたし、何度か外しもしました。
最近のClaude Codeのようなエージェントに「リファクタリング候補を出して」と頼むと、それなりに答えは返ってきます。ただ、根拠が薄い。エージェントは目の前のファイルしか読めず、コードベース全体の構造を把握しないまま提案します。
ここで効くのが コードグラフ です。Tree-sitterでAST(抽象構文木)を抽出し、関数・クラス・モジュールをノード、呼び出し・継承・importをエッジとしたグラフを作る。エージェントはそのグラフをMCP経由でクエリし、「どの関数が中心に居るか」「どこを変えると何が壊れるか」をデータで持ったうえで動けます。
私はこれを「エージェントに地図を渡す」と呼んでいます。地図を持っていない探検家は、勘で道を選ぶしかありません。AIエージェントも同じです。
自動化したいフローを3段に分ける
リファクタリング自動化と一言で言っても、自動化できる範囲には段階があります。私が今動かしているのはこの3段です。
| 段階 | 内容 | 自動化レベル |
|---|---|---|
| 1. 候補抽出 | 「直すべき関数」をグラフから選ぶ | 完全自動 |
| 2. 変更案生成 | 各候補にどう手を入れるかをAIが書く | 半自動(レビュー要) |
| 3. 適用と検証 | 実コードに反映してテストを走らせる | 人間が承認 |
完全自動にできるのは段階1だけです。段階2は提案の質にばらつきが出るのでレビューが要ります。段階3を完全自動にしたい誘惑は強いですが、私は今のところ手を出していません。CIで全テストを通したとしても、テストが網羅していない領域でバグが眠っている可能性を、AIに任せるのは怖いからです。
人間が手を抜いていい場所と、抜くと痛い場所を分ける。これがAI自動化の設計の核だと思っています。
段階1: グラフから候補を抽出する
最初のステップは「どの関数からリファクタリングするか」を決めることです。ここがブレるとAIが下流で迷子になります。
私はスコアを3軸で計算します。
- 中心性スコア: その関数を呼び出している関数の数(入次数)
- 複雑度スコア: 循環的複雑度や行数
- 変更頻度スコア: git logから取れる過去6か月の変更回数
中心性だけ高い関数は「設計の要」で、変更頻度が高ければ「火がついている要」になります。両方高い関数が、優先順位リストの上位に来ます。
実装は意外と素朴で、CodeGraphContextやcode-review-graphのようなツールで作ったグラフに、git log由来のメタデータを追加するだけです。Cypherなら次のように書けます。
// 中心性 × 変更頻度 のスコアリング
MATCH (f:Function)<-[:CALLS]-(caller:Function)
WITH f, COUNT(caller) AS in_degree
WHERE f.change_count_6m IS NOT NULL
RETURN f.name,
in_degree,
f.change_count_6m,
f.cyclomatic_complexity,
(in_degree * 0.4 + f.change_count_6m * 0.4 + f.cyclomatic_complexity * 0.2) AS score
ORDER BY score DESC
LIMIT 20
このクエリで出てくる上位20件が、AIに渡す「リファクタリング候補リスト」です。
私が最初に自分のリポジトリでこれを回したとき、上位3件のうち2件は予想通りでした。残りの1件は、過去半年で一度も話題に出なかった地味なユーティリティ関数でした。10年やってきた勘が、Cypherクエリ1本に半分負けた瞬間です。悔しいので「いやでも勘は半分当たってた」と自分に言い聞かせました。
段階2: AIエージェントに変更案を書かせる
候補リストが出たら、AIエージェントに「この関数のリファクタリング案を出して」と頼みます。ここでコードグラフが効きます。
エージェントへのプロンプトは、おおよそこんな形にしています。
以下の関数のリファクタリング案を出してください。 【対象関数】 service/user.py:update_user 【グラフから取得した情報】 - 呼び出し元: 14箇所(api/, worker/, cli/) - 呼び出し先: db.save, notify_change, audit_log - 循環的複雑度: 18 - 過去6か月の変更回数: 9 - インターフェースを共有する関数: create_user, delete_user 【制約】 - 公開インターフェースは変更しない - 呼び出し元14箇所を破壊しない - 既存のテストを通すこと
注目すべきは「呼び出し元: 14箇所」の情報をエージェントに 事前に 渡している点です。これがあると、エージェントは「引数を増やしてもいいか」「戻り値の型を変えてもいいか」を判断できます。
地図のない探検家に「橋を架けてくれ」と頼んでも、どこに架ければいいかわかりません。グラフから取った「橋を渡る人の数」を渡しておくと、エージェントは無理な変更を提案しなくなります。
ここで使うエージェントは、私の場合Claude Codeです。MCP経由でコードグラフツールを接続すると、エージェント自身がクエリを投げて文脈を取りに行きます。GitNexusやcode-review-graphはこの用途に最適化されていて、22個前後のMCPツールを提供しています。
ast-grepで「型のある置換」を回す
AIエージェントがゼロから書き直すと、変更が大きすぎてレビュー不能になることがあります。これを避けるために、私は ast-grep を併用しています。
ast-grepはRust製の構造的検索・置換ツールで、ASTパターンでコードを書き換えられます。2026年5月時点で0.42系がリリースされており、CLIとMCPサーバーの両方で動きます。
たとえば「全ての
print(...)を
logger.info(...)に置き換える」を、AIに任せず構造的なルールでやります。
# rules/print-to-logger.yml id: print-to-logger language: python rule: pattern: print($A) fix: logger.info($A)
これを
ast-grep scan -r rules/で流すと、コードベース全体に対して差分を出してくれます。AIエージェントは「ルールを書く」役割で、実際の置換はast-grepという決定論的なツールに任せる。この分業がうまく回ります。
AIは創造的だけど不安定です。決定論的なツールは融通が利かないけど信頼できます。両者を組み合わせると、不安定な創造性を信頼できる枠で囲える。私はこれを「AIにレールを敷く」と表現しています。
段階3: 適用と検証はパイプライン化する
リファクタリング案がレビューを通ったら、適用と検証はCI/CDのパイプラインに乗せます。
私の構成はこうです。
1. AIエージェントがブランチを切る 2. ast-grepルール or 直接編集で変更を適用 3. CodeLayers的なツールでblast radiusを再計算 4. 影響を受けるテストだけを優先実行 5. テスト通過後、人間にPRを投げる
ここで効くのが 影響を受けるテストだけ走らせる の部分です。コードグラフがあると、「変更ファイル→テストファイル」のホップを計算できます。1万件のテストを全部回さなくても、20件の関連テストだけで十分なケースが多い。
私は以前、「とりあえず全テスト回す」運用で、CIに30分かけていました。グラフベースの絞り込みを入れたら、平均5分まで縮みました。残り25分は、コーヒーを淹れて飲み終わる時間と等価です。私はその時間を、別のリファクタリング案を考える時間に使うようになりました。AIに仕事を任せたつもりが、結局自分の仕事が増えています。
ツール選びは「ローカル完結 vs クラウド」で分ける
2026年5月時点で、コードグラフ × AIエージェントの組み合わせに使えるツールは増えました。私が触ったものを、簡単に整理します。
| ツール | 特徴 | こんなときに |
|---|---|---|
| GitNexus | npx一発、ブラウザ内グラフ、Claude Code統合 | まず触りたい |
| code-review-graph | SQLite、22個のMCPツール、レビュー特化 | PR毎の影響範囲算出 |
| CodeGraphContext | Neo4j選択可、リアルタイム更新 | エンタープライズ運用 |
| CodeLayers | VS Code拡張、ホップ別カラー表示 | エディタで完結したい |
| Graphify | 2パス(AST+LLM)、ドキュメントも対象 | 設計意図も含めたい |
| ast-grep | 構造的置換のCLI/MCP、決定論的 | 大量パターン置換 |
選ぶ基準で一番効くのは「ローカル完結したいか、クラウドに振っていいか」だと思います。秘匿性が高いコードを扱うなら、AST抽出までは確実にローカルで完結させる。GitNexusやcode-review-graphはこの要件を満たします。
Graphifyのような2パスアーキテクチャは強力ですが、Pass 2でLLMに送る内容の範囲を慎重に絞る必要があります。「セマンティック抽出に使うのはコメントとREADMEだけ」とポリシーを決めておくと、運用がブレません。
私がハマった3つの落とし穴
実装を進めるなかで、いくつか痛い目に遭いました。同じ罠にハマる人を減らすために書いておきます。
1. 動的ディスパッチが見えない
Tree-sitter ASTは静的解析なので、Pythonの
getattrやJavaScriptのプロキシ経由の呼び出しは追えません。「呼び出し元0件」と出ても、実は動的に呼ばれている可能性があります。私は一度これで「不要なコードだ」と判断して削除し、本番で見事に壊しました。動的ディスパッチが多いコードベースでは、グラフの結果に必ず「最終チェック」の人間レビューを挟むこと。
2. monorepoだとグラフが巨大化する
ファイル数が3万を超えるリポジトリで、初回構築に2時間かかりました。差分更新に対応したツール(CodeGraphContextの監視モード等)を使うか、サブディレクトリ単位で分割すること。「最初から全部やる」は失敗しがちです。
3. AIエージェントが「グラフを盲信」する
これが一番厄介でした。グラフが「この関数は使われていない」と返すと、エージェントは何の躊躇もなく削除提案を出します。私が「動的ディスパッチで使われてるかも」と聞き返すと、「あ、その可能性はありますね」と素直に認めます。素直すぎて怖い。
エージェントへのプロンプトに、「グラフは静的解析の結果なので、動的呼び出しは含まれない可能性がある」と一文入れておくこと。これを入れないと、エージェントは数値を絶対視します。
「自動化」と「自律化」を混ぜない
最後に、設計思想の話を一つ書きます。
AIエージェントに任せるとき、私は「自動化」と「自律化」を意識的に分けています。
- 自動化: 人間が決めた手順をAIに代行させる
- 自律化: AIに目的だけ伝えて、手順は自分で考えさせる
リファクタリングの段階1(候補抽出)は自動化です。Cypherクエリと採点ルールは人間が決めて、AIはその実行に関与しません。段階2(変更案生成)は半自律化です。「複雑度を下げる」という目的だけ伝えて、手段はAIに任せます。
段階3(適用と検証)を完全自律化したくなる気持ちはわかりますが、私は今のところ「人間が承認」のゲートを残しています。AIが自分で書いた変更を、自分でテストして、自分でmergeする。これを許すと、何かが壊れたときに原因の特定が極端に難しくなります。
「自律」させる前に「説明可能性」を確保すること。これは私の中で、AIとコードを組み合わせるときの第一原則です。
まとめ
コードグラフをAIエージェントに渡すと、リファクタリングの精度と速度が両方上がります。私の実感では、優先順位の判断はほぼAIに任せられるようになりました。
ただし、変更の適用と検証はまだ人間の領域です。動的ディスパッチや設計意図のような、静的解析からこぼれる情報を補えるのは、今のところ人間の経験だけです。
AIに地図を渡し、レールを敷き、最後の承認だけ自分で握る。これが2026年5月時点で、私が落ち着いている運用パターンです。来年にはまた違う形になっているかもしれませんが、それまでは面白くやっていきましょう。