コンテンツへスキップ

QualiArtsengineer blog

Unityのビルドエラーを解析するAIエージェントの試み

Unityのビルドエラーを解析するAIエージェントの試み

9 min read

はじめに

株式会社QualiArtsでUnityエンジニアをしている住田です。Unityのプロジェクトに従事し、並行して「CA.unity」や「技術書典」といったサイバーエージェントの子会社を跨いだ横軸活動の牽引、ならびにQualiArtsの技術広報をつとめております。

Unity開発をしていると、ビルドエラーが日々発生しがちです。原因の特定には、大量のログから問題箇所を見つけ出し、その背景を理解する作業が必要になります。しかし、これは時間のかかる作業で、なおかつ特定のエンジニアしか効率的に行えなかったりする、いわば属人化した作業になりがちです。

本記事では、こうした課題を解決すべく、AIエージェントを活用してビルドエラーログを自動分析し、迅速な問題特定と解決策提案を行うシステム「build-log-analyzer」の開発と運用について紹介します。

ビルドエラーによる日々の開発課題

Unityの複雑なビルドプロセスでは、単一のビルドで場合によっては数万行にもなる大量のログが出力され、その中からエラーの根本原因を見つけ出す作業が必要になります。

プラットフォーム依存のコンパイルエラー、アセット設定の問題、環境に依存するエラーなど、多様なエラーパターンがあり、本当の原因が数百行前のログに隠れていることも少なくありません。こうしたログ解析には、エラーパターンに関する専門知識が必要で、特定のエンジニアしか効率的に対応できない属人化した作業になりがちです。

この属人化によって、該当エンジニアが不在時の対応遅延や、解決ノウハウの暗黙知化が起こり、開発チーム全体の効率を下げる要因になっていました。

AIエージェントを用いたビルドエラー解析ツール「build-log-analyzer」

近年、ChatGPTやGeminiといった大規模言語モデルの進歩によって、AIエージェントを活用した業務効率化が話題です。QualiArtsでも、過去にGeminiを使ってシンプルなAIエージェントを作ってみようでAIエージェントの自作過程を紹介しました。

今回のビルドエラー解析課題に対しても、AIエージェントを活用した解決策として、build-log-analyzerという仕組みを作りました。これは、ビルドの失敗ログを収集し、AIが分析してSlackに結果を投稿するシステムです。 このシステムを導入することにより、開発チームはビルドが失敗したときに自動的にエラー原因の分析結果を受け取れるようになります。

ビルドエラー解析の概要

まず、AIエージェントとしてはGoogle Gemini 2.5 FlashのAPIを用いて実現しました。Google Gemini系列のAPIは安価ながらも、ビルドログのエラー解析において十分な精度を発揮することが検証できたため採用しております。専門性の高いビルドエラー解析をAIエージェントに任せるため、次の2点を意識した解析システムを構築しました。

  • Function Callingによる調査行動の制御:ファイル閲覧機能をtoolとして提供し、効率的な検索処理でエラーを解釈
  • 反復的な自律判断プロセス:調査結果をもとに次に何をするかを自動で決定

この2点について詳しく説明していきます。

Function Callingによる調査行動の制御

人間がログを調査するときは、まず全体を俯瞰してエラーメッセージを探し、気になる箇所があれば詳細に読み込んで、関連する情報を検索します。AIエージェントにも同じような調査が効率的にできるよう、Function Callingを使ってツールを提供しました。

# Function Declarationでツールを構造的に定義
read_file_declaration = types.FunctionDeclaration(
    name="read_file",
    description="指定されたファイルのコンテンツを読み取ります",
    parameters={
        "type": "object",
        "properties": {
            "file_path": {"type": "string"},
            "line_range": {  # 行範囲の指定でトークン節約
                "type": "object",
                "properties": {
                    "start": {"type": "integer"},
                    "end": {"type": "integer"}
                }
            }
        }
    }
)

Function Callingとは、特定の処理を関数として提供し、AIがそれを外部プログラムとして解釈しながら選択して実行させられる機能です。 提供しているツールは主に3つです

  • read_file:ログファイルを行番号付きで読み取り
  • search_files:ripgrepベースの高速検索で、正規表現パターンにマッチする箇所を効率的に発見
  • attempt_completion:調査が完了したと判断したら、分析結果をまとめて出力

特にsearch_filesツールでは、ripgrepによる検索処理の効率化を行っています。--json出力を活用して構造化されたデータを取得し、検索結果の前後1行のコンテキストも含めて返すようにしています。

# ripgrepによる検索と結果の構造化
def search_files_tool(path: str, regex: str, file_pattern: str = None):
    args = ["rg", "--json", "-e", regex, "--context", "1"]
    if file_pattern:
        args.extend(["--glob", file_pattern])
    # JSON形式の結果をパースして、AIが理解しやすい形に整形
    return format_search_results(parsed_results)

これらのツールを提供することで、闇雲にAIが膨大なエラーログを漁るのではなく、適切な意図で効率的にログを調査することができました。

反復的な自律判断プロセス

提供したツールによる調査をもとに、AIエージェントが段階的に思考できるよう、単純に調べて結果を出すという一方向の処理ではなく、仮説を立てて検証を繰り返す反復的な調査プロセスを設計しました。

  1. 仮説の生成:現在のログ情報から「このエラーが原因かもしれない」という仮説を立てる
  2. 証拠の評価:read_fileやsearch_filesツールを使って、仮説を裏付ける証拠を探す
  3. 原因の推定:集めた証拠から、より確度の高い原因を推定する
  4. 仮説の検証:推定した原因が本当に正しいか、別の箇所も調査して検証する
def run_main_loop(chat, target_directory, max_iterations=20):
    iteration_count = 0

    while iteration_count <= max_iterations:
        iteration_count += 1

        # AIからfunction callを取得(仮説に基づくツール選択)
        function_calls = get_function_calls_safely(resp)

        if function_calls:
            # ツールを実行して証拠を収集
            function_response_parts, should_exit = process_function_calls(
                function_calls, chat, resp
            )

            if should_exit:
                # 十分な確度で結論に到達
                break

            # 収集した証拠をAIに返して次の判断を促す
            resp = chat.send_message(function_response_parts)
        else:
            # ツールを使わない場合は、次の行動を促す
            continue_message = "調査を継続してください"
            resp = chat.send_message(continue_message)

実際の具体的な流れのイメージはこのような形です。

  1. AIがsearch_filesツールで「error」「failure」「exit code 1」といったキーワードを含む箇所を検索
  2. 該当箇所を見つけたら、read_fileツールで前後50-100行を読み込んで文脈を把握
  3. <thinking>タグ内で、エラーの種類と考えられる原因を推論
  4. 仮説を検証するため、関連するファイル(パッケージ設定、依存関係など)を追加で検索
  5. 複数の証拠から、より確度の高い原因を特定
  6. 推定した原因の確度を評価(証拠の一貫性、他の可能性の排除度合いなどから判断)
  7. 確度が不十分な場合は、新たな仮説を立てて1に戻る
  8. 十分な確度に達したらattempt_completionで分析結果を出力

このサイクルを繰り返すことで、最初は「コンパイルエラーが出ている」という表面的な理解から、「実はパッケージの依存関係が壊れていて、それが原因でコンパイルエラーになっている」といった根本原因の追求を行います。

このプロセスは最大20回まで繰り返され、確度が十分高い結論に到達するか、これ以上の調査が必要ないと判断するまで続きます。

CIでのビルドエラー解析運用

QualiArtsでは、UnityのビルドをGitHub Actionsで運用しています。 このビルドの仕組みにフックさせるため、GitHub Actionsで作成したAIエージェントを使用する仕組みを構築しました。

jobs:
  build:
    timeout-minutes: 120
    # Unityビルド処理
    
  log-analysis:
    needs: [build]
    if: always() && contains(join(needs.*.result, ','), 'failure')
    timeout-minutes: 30

Python実行環境の自動構築

GitHub Actions上でPython環境を構築するのはsetup-pythonを利用しています。 必要な依存関係をインストールし、作ったエージェントに出力されたログファイルを渡して実行します。

steps:
  - name: Set up Python environment
    uses: actions/setup-python@v4
    with:
      python-version: '3.11'
      
  - name: Install dependencies
    run: |
      pip install requests google-genai
      sudo apt-get install -y ripgrep
      
  - name: Download and analyze failed job logs
    run: |
      # ジョブ一覧を取得
      workflow_jobs=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/$REPOSITORY/actions/runs/$RUN_ID/jobs")

      # 失敗したジョブのIDを直接取得
      failed_job_ids=($(echo "$workflow_jobs" | jq -r '.jobs[] | select(.conclusion == "failure") | .id'))

      # 失敗したジョブのログをダウンロード
      for job_id in "${failed_job_ids[@]}"; do
        job_name=$(echo "$workflow_jobs" | jq -r ".jobs[] | select(.id == $job_id) | .name")
        log_url="https://api.github.com/repos/$REPOSITORY/actions/jobs/$job_id/logs"
        # ファイル名を安全にする(スペース削除、スラッシュをアンダーバーに置換)
        log_file="$LOG_DIR/$(echo "$job_name" | tr -d ' ' | tr '/' '_').log"
        echo "Run ID: $RUN_ID, Job ID: $job_id"
        echo "Downloading log from: $log_url"
        curl -Ls -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" "$log_url" -o "$log_file"
        echo "=== $log_file ==="
      done

      # Pythonスクリプトでログを分析
      PYTHON_OUTPUT=$(python ${{ github.action_path }}/log-analyzer-agent/agent.py "$LOG_DIR")

必要なログについて、失敗したジョブから直接参照を行っているのが特徴です。これによって、workflowとして管理されているビルド処理のログをこのログ解析の処理側から参照することができます。

Slackスレッド投稿による分析結果の共有

分析結果は、ビルド開始時に作成されたSlackスレッドに自動で投稿されます。ビルド開始から完了、そしてエラー分析結果までの流れが1つのスレッドで追跡できるようにしています。

- name: Post analysis result to Slack thread
  uses: archive/github-actions-slack@master  
  with:
    slack-text: |
      Android ビルドログ分析結果
      ```
      ${{ steps.collect-logs.outputs.log-result }}
      ```
    slack-optional-thread_ts: ${{ needs.slack_notification_start.outputs.slack_thread_ts }}
    slack-optional-reply_broadcast: false

基本的にビルド結果やそのビルドのインストールをSlackのスレッドに投稿されるリンクなどから行うため、スムーズにビルドエラーの確認を行えます。

運用での実際の効果

ログ解析に費やす時間の効率化

運用してみたところ、プラットフォーム設定のエラーや内部の設定ミスなどで、ログを手動で調査することなく即座に問題を特定できました。開発作業を一旦止めて進める必要があったログ調査について、解析結果を見るだけで完了できるようになり、工数削減につながっています。

AI分析結果のSlack自動通知画面

属人化の解消

ビルドエラーはプロジェクトのビルドの担当者やエンジニアリーダーが担当しがちでした。理由として、専門性の高いUnityやプラットフォームの知識や、ログの見方などが求められる作業だからです。 それがAIエージェントで解析可能なレベルのものであれば、担当者がいなくとも原因を追求し、プロジェクト内で対応を進められるようになりました。

AIツール活用から得た学び

AIエージェントを活かした開発フローの自動化はQualiArtsでも手探りでしたが、AIエージェントの自前実装とCIフローへの組み込みについて具体的な事例を交えて進められたので、AIエージェントの活用イメージにつながりました。 社内では別の事例に対して、「このフローのログも同じように内容を要約できないか」などの提案につながっており、ビルドエラーという汎用的な事例で試したのも良かったなと感じています。

まとめ

本記事では、Unity開発におけるビルドエラー解析の属人化課題を、AIエージェントによる自動分析システムで解決した取り組みを紹介しました。 今回の取り組みは、汎用的なAI技術でも工夫次第で専門的なタスクに対応できることを実証し、現実の業務課題解決にフォーカスしたAI導入の有効性を示せたと考えています。 今後も継続的にシステムを改善し、ゲーム開発におけるAI技術活用の可能性をさらに探求していく予定です。この記事が同様の課題を抱える開発チームの皆様にとって参考になれば幸いです。

2018年にサイバーエージェントに新卒入社。その後、QualiArtsにて新規プロジェクトのUnityエンジニアとして開発に携わる。現在は、運用プロジェクトのUnityリードエンジニアとして従事。ゲーム・エンターテイメント事業部(SGE)全体のUnityの技術促進を目的とする横軸組織「Unityコミュニティ」の責任者としても活動している。