
AIが生成したコードと人のコードを区別するための方法論
はじめに
生成AI(Generative AI)の急速な普及に伴い、ソフトウェア開発の現場や教育機関において、目の前のソースコードが「人間によって書かれたものか」あるいは「AIによって生成されたものか」を判別する能力が求められるようになっています。
この区別が必要とされる背景には、主に以下の3つの観点があります。
教育および学術的な公正性: 情報科学を学ぶ学生が、課題レポートや試験において自身の力でアルゴリズムを実装しているかを確認するため。
セキュリティとコンプライアンス: 企業において、著作権的に問題のあるコードの混入防止や、機密情報を学習データとして吸い上げられるリスク(入力データ漏洩)の管理、あるいはAIが生成した脆弱性のあるコードのチェックのため。
開発プロセスの可視化: 組織としてAI導入を推進する中で、実際にどれくらいの割合でAIがコーディング補助に使われているかを定量的に把握し、生産性向上との相関を分析するため。
本レポートでは、AI生成コードと人手によるコードの定性的な違い、ログに現れる特徴、そして最新の研究に基づく技術的な識別手法について詳細に解説します。
生成AIのコードと人のコードの差異
AI(特にLLM)は確率的に「もっともらしい」次のトークンを予測してコードを生成するため、そこには人間とは異なる特有の癖が現れます。ここでは、コメント、コード本体、ログの3つの観点からその差異を深掘りします。
コメントとドキュメンテーション
コードに含まれるコメントやドキュメントは、書き手の「思考プロセス」が最も色濃く反映される部分です。
感情と文体の欠如:
AIが生成するコメントは、文法的に完璧で非常にフォーマルです。「教科書的」と言えるでしょう。一方で、人間の開発者が書くコメントには、開発現場特有のスラング、略語、あるいは感情的なメモが含まれることが多々あります。- 人間の例: // FIXME: 汚いけど一旦リリース優先。後でリファクタ
AIの例:
// この関数はユーザー入力を検証し、データベースへの保存処理を実行します。「What」と「Why」の違い:
AIはコードの行そのものを説明する(What)コメントを生成しがちです。対して熟練した人間の開発者は、なぜその実装が必要だったかという背景(Why)や、ビジネスロジック上の制約をコメントに残す傾向があります。
特徴的なマーカー:
プロンプトの内容がそのまま漏れ出しているケースや、AI特有のまとめ方が見られる場合があります。「★今回の改修ポイント」のような、人間が通常コード内に残さない要約が出力に含まれている場合は、AIが生成した解説文をそのまま貼り付けた可能性が高いでしょう。
コードの構造とスタイル
ソースコードそのものの構造にも、AI特有の「きれいすぎる」特徴や、逆に「文脈を無視した」特徴が現れます。
過剰な整然さと命名規則:
AI生成コードは、インデントや命名規則が一貫しすぎています。人間の場合、既存プロジェクトの(多少変則的な)ルールに合わせたり、独自の短縮形を使ったりする「揺らぎ」が生じます。- 研究による裏付け: Xuら(2024)の研究によると、ChatGPTなどのAIは、ドキュメントに基づいた意味的な長い変数名(例: add_request_response_pair)を好む一方、人間はappendやoといった短縮名や慣習的な変数名を使用する傾向が高いと分析されています( Xu et al., 2024)。
「幻覚(ハルシネーション)」によるライブラリ使用:
これはAIコードの決定的な特徴の一つです。AIは「名前からして存在しそうな便利な関数」をでっち上げることがあります(例: dateUtils.calculateBusinessDaysSkipHolidays() のような、実在しないメソッド)。人間はIDEの補完機能を利用しながら書くため、このような「存在しない関数を呼ぶミス」は基本的に犯しません。
プロジェクト固有の文脈欠如:
AIは、そのプロジェクトで長年使われている「古いが動く社内ライブラリ」や「特殊なデータベース仕様」を知りません。そのため、そのファイルだけを見れば「一般的なベストプラクティス」に沿った美しいコードであっても、プロジェクト全体から見ると整合性が取れていない(浮いている)実装になる傾向があります。
実装スタイルの差異:
同じ処理でもアプローチが異なります。- 人間: 古典的な手法や、泥臭い分岐処理(if文の連打など)を使いがちです。
AI: Pythonであればリスト内包表記、JavaであればStream APIなど、現代的で簡潔な構文を好んで使用します。ある研究では、人間が
System.getenv()を逐次記述するのに対し、AIはループ処理でスマートに記述する傾向が確認されています。
Distinguishing LLM-generated from Human-written Code
ログ・履歴
完成したコードだけでなく、そこに至るまでの「過程」を見ることで、より確実な判別が可能になります。
入力速度とバースト性(Burstiness):
人間は思考しながらコードを書くため、書いては消し、行を行ったり来たりします。一方、AI生成コードは「コピー&ペースト」されることが多いため、IDEの編集履歴やGitのコミットログを見ると、数百行の複雑なロジックが「数秒以内」に出現します。
修正の履歴パターン:- 人間: 骨組みを作ってから詳細を埋める、あるいはバグを細かく修正しながら機能を追加する、という漸進的なアプローチを取ります。
AI: 一度で完成形に近いものを提示します。もし修正が必要な場合、部分的な修正ではなく、関数全体やクラス全体がガラッと書き換わることがあります。これは、ユーザーが「〇〇の条件を追加して」と再生成を指示し、出力全体を貼り替え直すためです。
技術的・学術的な識別アプローチ
目視による確認だけでなく、近年ではツールやアルゴリズムを用いた自動識別手法の研究も進んでいます。
構文木(AST)とN-gram解析:
コードをテキストとしてではなく、抽象構文木(AST)という構造データに変換して分析する手法です。Bukhariら(2023)の研究では、ASTノードのパターン(N-gram)を分析することで、C言語において高精度な分類が可能であると報告しています( Bukhari et al., 2023)。AIは特定の構文パターン(決まりきった構造)を繰り返す傾向が強いため、これを機械学習で検出します。
専用検出ツール:
汎用的なAI検出器(GPTZeroなど)はコードに対しては精度が低いことが知られていますが、コードに特化したツールも登場しています。- Span: コードを意味的なチャンクに分割し、スタイルや構造パターンから判定を行うツールで、高い精度を主張しています (AI Code Detector by Span)。
JFrog: ソフトウェアサプライチェーン管理のJFrogは、リポジトリ内のAI生成コードを検出・監査する機能を追加しています (JFrog DevOps)。
これらの技術的アプローチは、目視での判断が難しいケース(フォーマッターで整形されたコードなど)において、強力な補助手段となります。
終わりに
AIが生成したコードと人間が書いたコードを区別することは、AIモデルの進化とともに「いたちごっこ」の様相を呈しています。AIはより人間らしく振る舞うよう調整されつつあり、また人間もAIツール(Copilotなど)を補助的に使うハイブリッドな開発が標準になりつつあるため、100%の精度で分離することは現実的に困難になりつつあります。
しかし、本レポートで挙げた「不自然なまでの整然さ」「文脈の欠如」「履歴のバースト性」といった特徴に着目し、自動検出ツールを併用することで、高い確度でAIの介在を推定することは可能です。重要なのは、単に「AIか否か」を判定することではなく、そのコードが品質基準を満たし、セキュリティ上安全であり、組織のポリシーに適合しているかを判断することです。
FAQ generated by AI
インデントやスペースの使い方による判定は難しくなりますが、変数名の付け方、コメントの文体、存在しないライブラリの使用といった論理的な特徴は残るため、それらを手がかりに判定可能です。
いいえ、100%ではありません。現在の技術でも誤検知(人間が書いたものをAIと判定)や見逃しは発生します。あくまで補助ツールとして使用し、最終的には専門家によるレビューが必要です。
履歴がない場合は、コード内のコメントの質(感情の有無)、変数名の命名規則(意味的すぎるか)、およびプロジェクト固有のルール(古い慣習など)が守られているかどうかが主な判断材料になります。
必ずしも悪くありませんが、著作権侵害のリスク、脆弱性の混入、ライセンス違反の可能性があります。また、内容を理解せずに使用すると、将来的な保守が困難になるため、人間による理解とレビューが必須です。
「Span」や「Pangram Labs」などが有用ですが、状況によります。簡易的な確認であればオープンソースのモデルも存在しますが、セキュリティが重要な場面ではJFrogのようなプラットフォーム統合型の利用が推奨されます。
