🐈

GitHub Copilot SDKでサーバメトリクス評価の自動化を試す

に公開

概要

GitHub CopilotをAmazon Bedrockのようにツールに組み込めたらいいのになと思っていたら、テクニカルプレビューではありますがCopilot SDKが公開されました。
https://github.blog/changelog/2026-01-14-copilot-sdk-in-technical-preview/

GitHubはこちら
https://github.com/github/copilot-sdk

Copilot SDKを使うにはCopilot CLIが必要となっているためBedrockと比較すると環境準備の面で使い勝手はよくない印象を受けますが、せっかくなのでCopilot Python SDKを使ってサーバのメトリクス評価の自動化をしてみました。

実行結果
実行結果

前提

  • Copilot CLIがインストール済みでログイン済みであること
  • Prometheusにメトリクスが格納されていること
    • 本記事ではGrafana Cloud上のPrometheusを使用しています。

ディレクトリ構成

.
├── main.py                   # メインスクリプト
├── prometheus_client.py      # Prometheus APIクライアント
├── metrics_queries.py        # PromQLクエリ定義
├── anomaly_detector.py       # 異常検知ロジック
├── llm_analyzer.py          # Copilot SDKを使ったLLM解析
├── config.py                # 設定管理
├── utils.py                 # ユーティリティ関数(フォーマット等)
├── requirements.txt         # 依存パッケージ
├── .env.example            # 環境変数サンプル
├── .env                    # 環境変数(gitignore対象)
├── README.md
└── output/                 # 結果出力ディレクトリ
    ├── metrics_*.json      # 全体の解析結果
    └── anomaly_*.json      # 異常検知時のログ(異常時のみ)

バージョン

今回の構築で使用したOSや主要なソフトウェアのバージョンを以下に記載します。
2026年1月21日に試した際のバージョンです。

名称 バージョン
Amazon Linux 2023 2023.9.20250929
Copilot CLI 0.0.388
Python 3.13.3
github-copilot-sdk 0.1.14
python-dotenv 1.2.1
requests 2.32.5

依存関係でインストールされたpythonのパッケージは以下でした。

インストールされたpythonのパッケージ一覧
pip list
Package            Version
------------------ -----------
annotated-types    0.7.0
certifi            2026.1.4
charset-normalizer 3.4.4
github-copilot-sdk 0.1.14
idna               3.11
pip                25.3
pydantic           2.12.5
pydantic_core      2.41.5
python-dateutil    2.9.0.post0
python-dotenv      1.2.1
requests           2.32.5
six                1.17.0
typing_extensions  4.15.0
typing-inspection  0.4.2
urllib3            2.6.3

準備

依存パッケージのインストール

uv で仮想環境を作成し、仮想環境に依存パッケージをインストールします。

uv venv --seed .venv
source .venv/bin/activate
pip install -r requirements.txt

環境変数の設定

.env ファイルに環境変数を記載します。

  • GitHub Copilotの設定

    環境変数名 値 備考
    COPILOT_MODEL gpt-5-mini GitHub Copilotで使用する基盤モデルの名称

    COPILOT_MODELに指定可能な値は、 copilot --help を実行することで確認できます。

    copilot --help | grep -A 7 -e '--model <model>'
    --model <model>                     Set the AI model to use (choices:
                                       "claude-sonnet-4.5", "claude-haiku-4.5",
                                       "claude-opus-4.5", "claude-sonnet-4",
                                       "gpt-5.2-codex", "gpt-5.1-codex-max",
                                       "gpt-5.1-codex", "gpt-5.2", "gpt-5.1",
                                       "gpt-5", "gpt-5.1-codex-mini",
                                       "gpt-5-mini", "gpt-4.1",
                                       "gemini-3-pro-preview")
    
  • Grafana CloudのPrometheusの設定

    環境変数名 値 備考
    INSTANCE_ID XXXXXXX Your Grafana Cloud Prometheus username.
    PROM_URL https://prometheus-prod-XX-prod-ap-southeast-1.grafana.net/api/prom PrometheusのエンドポイントURL
    API_KEY glc_XXXXXXXXXXXXXXXXXXXXXX Prometheusの読み取り権限を付与したAPIトークン
  • 異常検知の閾値

    環境変数名 値 備考
    THRESHOLD_CPU_USAGE 80 CPU使用率の閾値
    THRESHOLD_MEMORY_USAGE 85 メモリ使用率の閾値
    THRESHOLD_SWAP_USAGE 50 Swap使用率の閾値
    THRESHOLD_DISK_USAGE 90 ディスク使用率の閾値
    THRESHOLD_IOWAIT 20 I/O待ち割合の閾値
    THRESHOLD_LOAD1_PER_CPU 2.0 1分平均ロードを CPU 数で割った値の閾値
    THRESHOLD_NETWORK_ERRORS_PER_SEC 10 ネットワークエラー発生数の閾値
    THRESHOLD_TCP_RETRANS_PER_SEC 50 TCP再送発生数の閾値

.env ファイルの記載例です。

# GitHub Copilot設定
COPILOT_MODEL=gpt-5-mini

# Grafana Cloud Prometheus設定
INSTANCE_ID=XXXXXXX
PROM_URL=https://prometheus-prod-XX-prod-ap-southeast-1.grafana.net/api/prom
API_KEY=glc_XXXXXXXXXXXXXXXXXXXXXX

# 異常検知の閾値(必要に応じて調整)
THRESHOLD_CPU_USAGE=80
THRESHOLD_MEMORY_USAGE=85
THRESHOLD_SWAP_USAGE=50
THRESHOLD_DISK_USAGE=90
THRESHOLD_IOWAIT=20
THRESHOLD_LOAD1_PER_CPU=2.0
THRESHOLD_NETWORK_ERRORS_PER_SEC=10
THRESHOLD_TCP_RETRANS_PER_SEC=50

ソースコード

ソースコードの全体はGitHubをご参照ください。
https://github.com/jay34986/copilot_metrics_check

以下では、GitHub Copilot SDKに関連する部分について抜粋します。

main.py では llm_analyzer.py の analyze_metrics_sync() を呼び出します。

main.py 180-186行目
    # 4. LLM解析
    logger.info("\n🤖 LLMで解析中...")
    try:
        llm_result = analyze_metrics_sync(summary, anomaly_result, detailed)

        logger.info("\n%s", "=" * 60)
        logger.info("LLM解析結果")

analyze_metrics_sync() では、 LLMAnalyzer() を初期化し、 detailed の有無で analyze_detailed() か analyze_summary() を実行します。
以下では、 analyze_summary() の例で記載します。

llm_analyzer.py 269-279行目
            async with LLMAnalyzer() as analyzer:
                if detailed:
                    return await analyzer.analyze_detailed(
                        summary,
                        detailed,
                        anomaly_result,
                    )
                return await analyzer.analyze_summary(
                    summary,
                    anomaly_result,
                )

LLMAnalyzer() の初期化では、 .env に記載したGitHub Copilotの基盤モデルを設定します。

llm_analyzer.py 42-45行目
    def __init__(self, model: str = COPILOT_MODEL) -> None:
        """Initialize the LLMAnalyzer with a model."""
        self.model = model
        self.client = None

async with LLMAnalyzer() で async で起動しているので、 LLMAnalyzer.__aenter__ が実行されGitHub Copilot Clientとの接続が確立されます。

llm_analyzer.py 47-51行目
    async def __aenter__(self) -> "LLMAnalyzer":
        """非同期コンテキストマネージャのエントリ."""
        self.client = CopilotClient()
        await self.client.start()
        return self

その後、 analyze_summary() で _build_summary_prompt() を実行してプロンプトを作り、 プロンプトを引数に _execute_analysis() を実行してGitHub Copilotで解析します。

llm_analyzer.py 63-79行目
    async def analyze_summary(
        self,
        summary: dict[str, Any],
        anomaly_result: dict[str, Any],
    ) -> str:
        """サマリメトリクスと異常検知結果をLLMで解析.

        Args:
            summary: メトリクスサマリ
            anomaly_result: 異常検知結果

        Returns:
            LLMの解析結果(テキスト)

        """
        prompt = self._build_summary_prompt(summary, anomaly_result)
        return await self._execute_analysis(prompt)

_execute_analysis() では、 self.client.create_session() で基盤モデルを指定してセッションを作成します。
完全な結果を一度に取得するため、 streaming は False としています。

llm_analyzer.py 200-205行目
        session = await self.client.create_session(
            {
                "model": self.model,
                "streaming": False,  # 完全な結果を一度に取得
            },
        )

session.on() で SessionEvent を受け取るコールバックを登録し、 session.send() でGitHub Copilotにプロンプトを送信し解析してもらいます。

llm_analyzer.py 226-227行目
            session.on(on_event)
            await session.send({"prompt": prompt})

_execute_analysis() は finally で session.destroy() を実行して、セッションを解放します。

llm_analyzer.py 242-243行目
        finally:
            await session.destroy()

analyze_summary() が終了し、最後に LLMAnalyzer.__aexit__ が実行されてGitHub Copilot Clientが終了されます。

llm_analyzer.py 53-61行目
    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        """非同期コンテキストマネージャの終了."""
        if self.client:
            await self.client.stop()

また、安定性向上のため、内部エラーのハンドリングを実装しています。

llm_analyzer.py 16-17行目
def _custom_exception_handler(_: asyncio.AbstractEventLoop, context: dict) -> None:
    """asyncioのカスタム例外ハンドラー - copilotライブラリ内のAssertionErrorを抑制."""

実行

通常時の実行(python main.py)では以下の出力となりました。

python main.py
設定の検証が完了しました

============================================================
メトリクス監視・異常検知スクリプト
実行時刻: 2026-01-21 06:18:34
============================================================

📊 サマリメトリクスを取得中...

============================================================
メトリクスサマリ
============================================================

【観測健全性】
  up: 1.0
  scrape_duration: 0.205s

【CPU】
  使用率: 6.6%
  iowait: 0.4%
  load1: 0.12
  load5: 0.16
  load15: 0.25

【メモリ】
  使用率: 33.9%
  swap: 2.9%

【ディスクI/O】
  読み込み: 0.0 MB/s
  書き込み: 0.0 MB/s

【ファイルシステム】
  1. /mnt/array1: 49.8%
  2. /: 39.0%
  3. /: 39.0%

【ネットワーク】
  受信: 0.0 MB/s
  送信: 0.0 MB/s
  エラー: 0.0/s

【TCP】
  確立済み接続: 3.0
  再送: 0.0/s
============================================================

🔍 異常検知を実行中...

✅ 異常は検知されませんでした

🤖 LLMで解析中...

============================================================
LLM解析結果
============================================================
### 総合状態
現在は正常です。CPU・ロード・メモリ・ネットワークともに余裕があり、異常検知は報告されていません(重要度: normal)。

### 推奨アクション
- 継続的な監視を維持し、主要指標(CPU負荷、メモリ・スワップ、ディスク使用率、I/O)に閾値アラートを設定してください(例: FS使用率80%)。
============================================================

結果を保存しました: output/metrics_20260121_061834.json
✅ 処理が完了しました

python main.py --detailed で詳細メトリクスまで取ると、以下のように細かく見てくれました。

詳細メトリクス取得時の結果
python main.py --detailed
設定の検証が完了しました

============================================================
メトリクス監視・異常検知スクリプト
実行時刻: 2026-01-21 06:19:40
============================================================

📊 サマリメトリクスを取得中...

============================================================
メトリクスサマリ
============================================================

【観測健全性】
  up: 1.0
  scrape_duration: 0.194s

【CPU】
  使用率: 6.7%
  iowait: 0.4%
  load1: 0.17
  load5: 0.16
  load15: 0.24

【メモリ】
  使用率: 33.9%
  swap: 2.9%

【ディスクI/O】
  読み込み: 0.0 MB/s
  書き込み: 0.0 MB/s

【ファイルシステム】
  1. /mnt/array1: 49.8%
  2. /: 39.0%
  3. /: 39.0%

【ネットワーク】
  受信: 0.0 MB/s
  送信: 0.0 MB/s
  エラー: 0.0/s

【TCP】
  確立済み接続: 3.0
  再送: 0.0/s
============================================================

🔍 異常検知を実行中...

✅ 異常は検知されませんでした

📈 詳細メトリクスを取得中...
✓ 29個の詳細メトリクスを取得しました

🤖 LLMで解析中...

============================================================
LLM解析結果
============================================================
### 根本原因の仮説
1. 正常(最尤): リソース利用は総じて低く、現在の状態に致命的な異常は検知されていない — 軽微なローカル(loopback)通信と RAID ミラー/バックエンド書き込みによる継続的なディスク書き込みが観測される(sda/sdb/md1 書き込み)。  
2. 過去または断続的なメモリ圧迫の痕跡: スワップ使用率が約2.9%でゼロではないため、断続的にメモリ不足が発生して一部プロセスがスワップを使っている可能性がある(だが現在のメモリ使用率は低め)。  
3. 容量増加リスク(中長期): /mnt/array1 が約50% 使用で、そこにログやデータが集中している場合は急成長で将来的にディスク不足になる恐れがある。

### 影響範囲の推定
- 現在はシステム負荷・レスポンス低下の顕著な影響は見られない(CPU/ロード/iowait/IO 利用率いずれも低水準)。  
- ただしスワップ使用や継続的な書き込みが増加すると、I/O レイテンシやレスポンス悪化、最悪 OOM・サービス障害につながる可能性がある。  
- ループバック通信が主流なので影響は主にローカルのサービス間連携(エージェント、ローカルプロキシ等)に限定される見込み。

### 推奨する調査・対処手順(優先度順、具体コマンド含む)
1. 現状プロセスとI/O発生日を特定(高優先)  
   - top/htop で瞬間的なCPU/メモリ負荷を確認: sudo top -o %MEM  
   - I/O を多く発生させているプロセス確認: sudo iotop -aoP もしくは sudo pidstat -d 1 5  
2. どのプロセスがスワップを使っているか確認(中優先)  
   - スワップ量プロセス別一覧: sudo grep VmSwap /proc/*/status 2>/dev/null | sort -k2 -n -r | head -n 20  
   - もしくは smem があれば: sudo smem -k | sort -nr -k 5 | head  
3. ディスク利用と大きいファイル/ディレクトリの確認(中優先)  
   - マウント毎の利用: df -hT / /mnt/array1  
   - /mnt/array1 内の大容量上位: sudo du -sh /mnt/array1/* 2>/dev/null | sort -hr | head -n 20  
   - RAID 状況: cat /proc/mdstat && sudo mdadm --detail /dev/md1 (必要に応じて)  
4. ローカル通信の内容確認(低〜中優先)  
   - 接続一覧: ss -tunap | egrep 'ESTAB|LISTEN'  
   - ループバックのトラフィック発生元プロセス特定(短時間キャプチャ): sudo tcpdump -i lo -n -c 200 -w /tmp/lo.pcap そして tshark/wireshark で解析、または sudo ss -p | grep -E '127.0.0.1|::1'  
5. ログとカーネルメッセージ確認(低優先)  
   - dmesg / var/log/syslog / var/log/messages に I/O エラーや OOM イベントがないか確認: sudo dmesg --ctime | tail -n 200; sudo journalctl -k --since "1 hour ago"  

(対応例)一時的にスワップを解放する場合: sudo swapoff -a && sudo swapon -a(ただしシステムへの影響を考慮して実行)

### 予防策
1. モニタリングとアラート強化: /mnt/array1 のディスク使用率、スワップ使用率、disk_io_util、iowait、iotopでの上位プロセスに閾値アラートを設定して早期検知する。  
2. スワップとメモリ運用方針の見直し: 必要なら swappiness を調整(sysctl vm.swappiness=10 等)および定期的なメモリリーク検査・ログローテーションや古いデータのアーカイブを実施してディスク増大を抑制する。

必要なら、上記のコマンド実行結果を貼っていただければさらに原因の絞り込みと優先対応を指示します。
============================================================

結果を保存しました: output/metrics_20260121_061940.json
✅ 処理が完了しました

終わりに

本記事では取得したメトリクスをGitHub Copilotに解析してもらいました。
これを cron などで自動実行すれば定期的にメトリクスをGitHub Copilotに自動で解析してもらえます。
また、その解析結果を通知するように作ることで、定常的な監視の負荷を下げられそうです。

GitHub Copilotでの解析結果については出力形式が揺れて安定しないところがあるので、プロンプトの検討が必要です。
また、使う基盤モデルでも生成結果は変わってくるはずなので、このような使い方をするときはプロンプトのチューニングが必要なことを意識しておく必要があります。

Discussion