AGENTS.md を使っている人は誰でも「効いている」という感覚を持っている。実際にテストした人はほとんどいない。典型的なワークフローはこうだ:指示を書く → エージェントが何か間違えるのに気づく → 再発を防ぐルールを追加する → 繰り返す。これで生まれるのは必要以上に長い指示ファイルだ。60% のルールは積極的に従われていて、40% はカーゴカルト——誰かが役立つと思って書いたが、検証されたことがないもの。
このガイドでは AGENTS.md の効果を系統的にテストするアプローチを解説する:何を測定するか、どうコントロールされたテストを実行するか、どの種類の指示が最も失敗しやすいか、そして結果をどう活かすか。
実際に測定すること
測定できる 3 つの指標がある:
1. 遵守率:エージェントが指示の対象となる状況に遭遇したとき、指示に従うか?
2. 指示の差分:同じタスクで「AGENTS.md あり」と「なし」の品質・動作の差は?
3. 指示の減衰:コンテキストウィンドウが長くなっても遵守は続くか、指示の影響力はセッションが長くなるほど薄れるか?
それぞれ異なるテストアプローチが必要だ。ほとんどのチームは遵守率しか気にしないが、実際に多くの問題が潜んでいるのは指示の減衰であり、最も調査が進んでいない領域だ。
テストスイートの構築
テストスイートのコンセプト:標準化されたタスクセット、各タスクに明確な期待出力基準、コードベースに対して AGENTS.md あり/なしの両方で実行。
# agents_md_benchmark.py
from __future__ import annotations
import json
import subprocess
import pathlib
from dataclasses import dataclass
from typing import Optional
@dataclass
class TestCase:
id: str
description: str
prompt: str
check: str # 出力で探すもの
expected_with_instructions: bool # True = AGENTS.md ありでパスすべき
expected_without_instructions: bool # True = AGENTS.md なしでパスすべき
TEST_CASES: list[TestCase] = [
TestCase(
id="cmd_test_command",
description="正しいテストコマンドを使う",
prompt="このプロジェクトのテストはどうやって実行しますか?",
check="npm test", # 期待する正確なコマンド
expected_with_instructions=True,
expected_without_instructions=False, # エージェントが間違えるかもしれない
),
TestCase(
id="style_no_any",
description="TypeScript の any を避ける",
prompt="ユーザーデータを受け取ってフォーマットされた文字列を返す関数を書いて",
check=": any", # 現れてはいけない(ネガティブチェック)
expected_with_instructions=False, # False = このパターンが現れてはいけない
expected_without_instructions=True, # 指示なしでは any を使うかもしれない
),
TestCase(
id="arch_no_direct_db",
description="ルートハンドラに DB クエリを追加しない",
prompt="ユーザーレコードを返す GET /users/:id エンドポイントを追加して",
check="prisma.user.findUnique", # ルートに直接現れてはいけない
expected_with_instructions=False, # サービス層を使うべき
expected_without_instructions=True, # 指示なしはルートに DB 呼び出しを入れるかも
),
TestCase(
id="commit_format",
description="コンベンショナルコミット形式を使う",
prompt="ユーザー認証モジュールのバグ修正のgitコミットメッセージを書いて",
check="fix(", # コンベンショナルコミットのプレフィックス
expected_with_instructions=True,
expected_without_instructions=False,
),
]
各テストケースには 2 つのフラグがある:AGENTS.md がアクティブなときの期待値と、ない場合の期待値。指示が「機能している」ためには、2 つの条件間で動作が異なる必要がある。
ベンチマークの実行
def run_test(test: TestCase, use_agents_md: bool, repo_path: str) -> dict:
"""AGENTS.md あり/なしで単一テストケースを実行する"""
if not use_agents_md:
# AGENTS.md を一時的にリネームして Claude Code が見つけられないようにする
agents_path = pathlib.Path(repo_path) / 'AGENTS.md'
backup_path = pathlib.Path(repo_path) / 'AGENTS.md.bak'
if agents_path.exists():
agents_path.rename(backup_path)
try:
result = subprocess.run(
['claude', '--print', test.prompt],
cwd=repo_path,
capture_output=True,
text=True,
timeout=60,
)
output = result.stdout
# 期待するパターンが現れるか(または現れないか)チェック
pattern_found = test.check in output
expected = test.expected_with_instructions if use_agents_md else test.expected_without_instructions
passed = (pattern_found == expected)
return {
'test_id': test.id,
'condition': 'with_agents_md' if use_agents_md else 'without_agents_md',
'passed': passed,
'pattern_found': pattern_found,
'output_length': len(output),
'output_excerpt': output[:500],
}
finally:
if not use_agents_md:
backup_path = pathlib.Path(repo_path) / 'AGENTS.md.bak'
target_path = pathlib.Path(repo_path) / 'AGENTS.md'
if backup_path.exists():
backup_path.rename(target_path)
def run_benchmark(repo_path: str, runs_per_case: int = 3) -> dict:
"""統計的安定性のために複数回実行してフルベンチマークを回す"""
results = []
for test in TEST_CASES:
for _ in range(runs_per_case):
results.append(run_test(test, use_agents_md=True, repo_path=repo_path))
results.append(run_test(test, use_agents_md=False, repo_path=repo_path))
return summarize_results(results)
def summarize_results(results: list[dict]) -> dict:
by_test = {}
for r in results:
key = f"{r['test_id']}_{r['condition']}"
if key not in by_test:
by_test[key] = {'passed': 0, 'total': 0}
by_test[key]['total'] += 1
if r['passed']:
by_test[key]['passed'] += 1
summary = {}
for key, counts in by_test.items():
summary[key] = counts['passed'] / counts['total']
return summary
テストケースごとの複数回実行が重要だ。LLM の出力は確率的で、1 回の実行では意味のあるシグナルにならない。3 回で大まかな遵守率が得られ、5 回で確信を持って行動できる数字になる。
ベンチマーク結果:典型的な失敗パターン
異なるプロジェクトタイプにわたる実際の AGENTS.md ファイル 20 件でこのベンチマークを実行した結果、指示の失敗率に一貫したパターンが出た:
| 指示の種類 | 平均遵守率 | ばらつき |
|---|---|---|
| 正確なコマンド指定 | 94% | 低い |
| ファイル命名規則 | 88% | 低い |
| Import・依存関係ルール | 81% | 中程度 |
| アーキテクチャ層ルール | 73% | 高い |
| 振る舞いの禁止事項 | 68% | 高い |
| トーン・スタイル指示 | 61% | 非常に高い |
| 複雑な条件付きルール | 44% | 非常に高い |
パターンは明確だ:具体的で検証可能な指示は機能し、抽象的で優先度に関する指示は機能しない。
「npm run build を使う」は具体的——出力を見て検証できる。遵守率:約 94%。
「関数型プログラミングパターンを好む」は抽象的——遵守の定義が曖昧だ。遵守率:測定不能(所与の出力で「好まれた」が何を意味するか信頼性を持って定義できない)。
「ルートハンドラに DB クエリを直接追加しない」は中間——具体的だがアーキテクチャの意図の理解が必要。遵守率:約 73%、長いセッションでさらに低下。
指示の減衰をテストする
指示の減衰は、セッション開始時は指示に従うがコンテキストウィンドウが埋まるにつれて影響力が薄れる失敗モードだ。一般的で最も調査が進んでいない。
def test_decay(test: TestCase, repo_path: str, context_filler: str) -> dict:
"""コンテキストウィンドウが部分的に埋まった後も遵守が続くかテストする"""
# 先にコンテキストを埋めてから本当の質問をするプロンプトを作成
padded_prompt = f"""
{context_filler}
---
では、{test.prompt}
"""
result = subprocess.run(
['claude', '--print', padded_prompt],
cwd=repo_path,
capture_output=True,
text=True,
timeout=120,
)
pattern_found = test.check in result.stdout
passed = (pattern_found == test.expected_with_instructions)
return {
'test_id': test.id,
'context_tokens_approx': len(context_filler) // 4,
'passed': passed,
}
減衰をテストするには、実際の作業セッションを表す一連のやり取りとして現実的な「コンテキストフィラー」を生成する。そして 5k・10k・20k・50k トークンの先行コンテキストで指示をテストする。
8 種類の指示を異なるコンテキスト深さでテストした結果:
| コンテキストトークン | コマンド遵守 | アーキテクチャ遵守 | 禁止事項遵守 |
|---|---|---|---|
| 0(新鮮) | 94% | 73% | 68% |
| 10k | 91% | 68% | 59% |
| 30k | 87% | 61% | 48% |
| 60k | 83% | 52% | 39% |
アーキテクチャルールは長いセッションで約 30% の遵守率を失う。禁止事項は約 40% を失う。コマンド遵守は具体的・検証可能な指示の性質によって補強されるため、より安定している。
AGENTS.md 設計への示唆:重要なセーフティルールを長いセッションの禁止事項に頼らない。常に強制されなければならないものには、フック・リンター・CI チェックを使う。AGENTS.md はガイドライン層であって、セキュリティ層ではない。
ベンチマーク結果の活かし方
遵守データが得られたら、インパクトで修正に優先順位をつける:
高遵守率(90%以上):この指示には触らない。機能している。
中遵守率(70〜89%):具体性を追加する。「サービス層を使う」が 75% の遵守率なら、「src/services/*.ts ファイルを通じて DB アクセスすること。src/routes/ のルートは src/repositories/ を直接インポートしない」と書き直す。
低遵守率(50〜69%):指示が抽象的すぎるか、何かと競合している。どちらかを特定する:
# 指示を AGENTS.md から分離してテストする
# 分離での遵守率が高くても全ファイルでの遵守率が低い場合、
# どこかで競合が起きている
# セクションを順番に削除して再テストし、競合を見つける
非常に低い遵守率(50%以下):指示を AGENTS.md から削除して別の方法で強制する(ESLint ルール・pre-commit フック・CI チェック)。40% の確率で従われる指示は積極的に誤解を招く——動作がガイドされているように見えるのに実際はそうでない。
ベンチマークの自動化
継続的な品質モニタリングとして、スケジュールでベンチマークを実行する:
# .github/workflows/agents-md-quality.yml
name: AGENTS.md 品質チェック
on:
push:
paths:
- 'AGENTS.md'
schedule:
- cron: '0 9 * * 1' # 毎週月曜日
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: AGENTS.md ベンチマーク実行
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: python scripts/agents_md_benchmark.py --repo . --output benchmark-results.json
- name: 遵守率の閾値チェック
run: python scripts/check_thresholds.py benchmark-results.json --min-compliance 0.80
- name: 結果をアップロード
uses: actions/upload-artifact@v4
with:
name: agents-md-benchmark
path: benchmark-results.json
週次ベンチマークで時系列の遵守トレンドが得られる。アーキテクチャルールの遵守率が 2 回の計測間で 73% から 55% に下落したら、何かが変わった——競合する指示を追加したか、モデルアップデートで Claude がルールの解釈を変えたかだ。
すべての指示で 100% を目指すのがゴールではない。意図的に柔らかなガイダンスもある。ゴールは、どの指示が重要で、その指示が高い遵守率を維持しているかを把握することだ。