AI DevelopmentSep 8, 202610 分 READ

AGENTS.md vs. skills: コーディングエージェントを導く方法

Jacob Schmitt

シニア テクニカル コンテンツ マーケティング マネージャー

コーディングエージェントを導入するチームは早い段階で必ず同じ問いにぶつかります。エージェントに、自分たちのコードベースが実際にどう動くのかを伝える指示をどこに置くべきか、という問いです。今、この議論を支配している答えは2つあります。一つはAGENTS.md、リポジトリのルートに置くただのMarkdownファイルです。もう一つはskills、エージェントが必要に応じて読み込む、パッケージ化された指示セットです。

この議論の多くは、これを単なるフォーマットの選択として扱っています。しかし実際はそうではありません。どのフォーマットを選ぶかは、もっと難しい問いの先にある話です。エージェントに指示を与えることで、その振る舞いは本当に変わるのか、そしてそれをどうやって確かめるのか、という問いです。「なんとなくうまくいっている気がする」以上の根拠でこれに答えられないなら、あなたが選んでいるのは戦略ではなく、単なるファイルの拡張子でしかありません。

AGENTS.md vs. skills: 端的な答え

AGENTS.mdは、慣例としてリポジトリのルートに置かれる単一のMarkdownファイルで、コーディングエージェントがセッション開始時に読み込みます。これはOpenAIやGoogleなどが後押しするオープンなフォーマットで、OpenAIのCodexドキュメントでは、エージェントが作業を始める前に読み込むカスタム指示を置く場所として扱われています。ここには常に有効なコンテキストが入ります。ビルドの実行方法、優先すべきコマンド、プロジェクトの規約、そして新しいコントリビューターが初日に教えてもらう必要があるような内容です。毎回必ず読み込まれるため、シンプルで予測可能であり、対応力のあるエージェントならどれでも読み込むことができます。

一方、skillsは、AnthropicがClaude向けに導入したアプローチで、モジュール式になっています。各skillは、指示(場合によってはスクリプトや参考資料も含む)が自己完結した形でまとめられたバンドルであり、エージェントはタスクが必要とする場合にのみそれを取り込みます。Anthropicの設計では、エージェントは最初は各skillの名前と説明だけを目にし、タスクに合致したときに初めて完全な指示をオンデマンドで読み込みます。無制限に肥大化する1つのファイルの代わりに、目の前の作業に応じてエージェントが選び出せるライブラリが手に入るのです。

どちらを使うべきか、端的に言うと:

  • 指示が小さく、安定していて、ほぼすべてのタスクに当てはまる場合はAGENTS.mdを使いましょう。ビルドやテストのコマンド、リポジトリの構成、独自のコーディングスタイルなどです。これは最低限の基盤であり、ほとんどすべてのプロジェクトが恩恵を受けられ、書くのに数分しかかかりません。

  • 指示が大規模で、状況依存的、あるいは手順的な場合はskillsを使いましょう。複数ステップからなるリリースチェックリスト、フレームワーク固有のマイグレーション、特定のファイルにしか関係しないレビュー基準などです。こうした内容をすべてのタスクで毎回読み込むと、コンテキストを無駄にし、本当に当てはまる指示が薄まってしまいます。

実際には、ほとんどのチームが両方を併用することになります。常に当てはまる事実のためのシンプルなAGENTS.mdと、専門的な手順のためのskillsです。両者の選択は、範囲と読み込みのタイミングに帰結します。

しかし、この整理の仕方が静かに前提としていることに注意してください。それは、指示を書き記すことと、エージェントがそれに従うことが同じである、という前提です。実際には、そうではありません。

evalが示す、AGENTS.md対skillsの実態

チームが目視ではなく実際にエージェントの振る舞いを計測すると、いくつかのパターンが繰り返し現れます。

第一に、指示には「遵守曲線(compliance curve)」があります。テストでは毎回ルールに従っていたエージェントも、context windowが埋まってくると、そのルールに従う頻度は大きく下がります。あなたには曖昧さのないルールに見えても、モデルがより重視する、より新しい、あるいはより具体的な指示に負けてしまうことがあります。「AGENTS.mdに書いてあるから」は、エージェントの振る舞いを保証するものではありません。

第二に、指示をどこに置き、どう言い回すかが数値を左右します。同じルールでも、「コミットを提案する前にyarn lintを実行する」のような具体的でテスト可能な指示にした場合、「コードをきれいに保つ」のような曖昧な願望として書くよりも、はるかに確実に機能します。ルールをどこに置くかも重要ですが、必ずしも予想通りの方向に働くわけではありません。オンデマンドでの読み込みというskillsの前提そのものは、必要になった瞬間に該当する指示を浮かび上がらせることができます。しかし同時に、エージェントが読み飛ばせるステップ、つまり「そもそもそのskillを読み込むかどうかを判断する」というステップも増えます。Vercelが両者を直接比較するevalを実施したところ、常に読み込まれるAGENTS.mdはタスクセットで100%のパス率に達した一方、skills版は79%が上限でした。これは、エージェントがskillの読み込みをそもそも選ばなかったことが一因です。ただし、その後の議論では重要な指摘がありました。この結果はVercelのタスク、モデル、セットアップに固有のものだということです。結果は状況によって変わり得るからこそ、他人の結論を鵜呑みにするのではなく、自分自身で計測する必要があるのです。

第三に、指示を増やすほど結果が線形に良くなるわけではありません。ある一定の量を超えると、1つのファイルにルールを追加することが、すでにそこにあるルールへの遵守度を下げてしまいます。エージェントが使える注意力(attention)の予算には限りがあるからです。AGENTS.mdを追記専用の置き場所として扱っているチームは、ファイルが肥大化するにつれて、以前は機能していたルールも含めて全体的な遵守度が下がっていく傾向が見られます。

これらはいずれも、議論だけで決着がつくものではありません。実証的な事象であり、モデルやコードベースによって変わり、基盤となるモデルをアップグレードするたびに結果も変わります。自分たちの設定がどう振る舞うかを知る唯一の方法は、実際にテストすることです。

再現可能なevalループでエージェント設定をテストする方法

エージェント設定のためのevalループは、聞こえるほど複雑ではありません。研究用の大掛かりなハーネスは必要ありません。必要なのは、固定されたタスクのセット、それに対してエージェントを実行する仕組み、そして望んでいた振る舞いが実際に起きたかどうかを教えてくれるチェックの3つです。

実用的なループは、次の4つの要素からなります。

  1. Fixtures。 既知の開始状態(チェックアウトされたコミット、特定のプロンプト、定義済みのリポジトリ構成)に固定された、代表的なタスクの小さなセットです。ポイントは、すべての実行が同じ地点から始まることです。そうすることで、出力の変化が入力の変化ではなく、設定の変化を反映するようになります。
  2. 検証すべき振る舞い。 観察可能なものを選びます。エージェントはコミット前にテストコマンドを実行したか。新しいファイルを、規約で指定したディレクトリに置いたか。避けるように指示した非推奨のAPIを避けたか。「良いコードを書く」のような曖昧な目標はここでは機能しません。テスト可能な目標であれば機能します。
  3. runner。 各fixtureをエージェントに与え、エージェントが行ったこと、つまり変更したファイル、実行したコマンド、最終的なdiffを記録するスクリプトです。
  4. checker。 記録された出力を検査し、合格か不合格かを返すコードです。禁止されたAPIをgrepで探す、期待されるファイルが存在するかを確認する、lintのステップが実行されたことをassertする、といった具合です。これは、アプリケーションの振る舞いではなくエージェントの振る舞いに向けられた、ごく普通のテストassertionにすぎません。

これらが揃えば、あとはテストを書いたことがある人なら馴染みのあるワークフローです。設定を変更し(ルールをAGENTS.mdからskillに移す、指示の言い回しを変える、肥大化したファイルを分割するなど)、fixturesを実行し、前後のパス率を比較します。エージェントの出力には実行ごとにある程度のばらつきがあるため、各fixtureを複数回実行し、単発のpass/failではなくパス率を追跡してください。

以下は、コミット前にlintを実行するというルールを例にした、checkerの実装例です。

def check_ran_lint_before_commit(run):
    commands = run["commands"]  # ordered list of shell commands the agent ran
    try:
        lint_index = next(i for i, c in enumerate(commands) if "lint" in c)
        commit_index = next(i for i, c in enumerate(commands) if c.startswith("git commit"))
    except StopIteration:
        return False  # one of the two commands never ran
    return lint_index < commit_index  # lint must come before the commit

def pass_rate(runs):
    passed = sum(1 for r in runs if check_ran_lint_before_commit(r))
    return passed / len(runs)

このassertion自体は単純です。価値があるのは、設定変更のたびに、そしてモデルのアップグレードのたびに、これを一貫して実行することにあります。それによって、「自分たちのエージェント向けガイダンスは今も機能しているか」という問いが、意見ではなく計測可能な数値になるのです。

デリバリーパイプラインでエージェントを導く

エージェントの振る舞いを計測可能にする理由は、他の何かを計測可能にする理由と同じです。それを基準にしてゲートを設けられるようにするためです。チェックがコードになった以上、それは他のチェックが既に置かれている場所、つまりCIパイプラインに置くべきものです。

ここで実行する価値があるのは、明確に異なる2つのことです。

第一は、エージェント設定をテスト対象のアーティファクトとして扱うことです。AGENTS.mdとskillsは、プロダクションの作業を形作る入力であり、時間とともに変化していきます。誰かがそれらを編集したとき、あるいは新しいモデルバージョンに切り替えたときには、CI上でevalスイートを実行し、ベースラインとパス率を比較してください。ある重要なルールの遵守率を100%から40%まで下げてしまうプルリクエストは、単体テストを壊すプルリクエストと同じように失敗として扱われるべきです。これによって、そうしなければ数週間後にエージェントが誤った成果物を出すまで見えない、静かなリグレッションを検知できます。

第二は、エージェントが生成した成果物を検証することです。指示を与えることで悪い変更が起きる確率は減りますが、ゼロにはなりません。エージェントが生成したものは何であれ、人間による変更が通るのと同じチェック、つまりビルド、lint、単体テスト、統合テストを通す必要があります。これこそCircleCIが得意とする仕事です。CircleCIはあらゆるコミットに対してテストスイートを実行するため、エージェントは直近のテスト実行結果の分だけ信頼できるということになります。数分で明確なpass/failの信号を返すパイプラインがあれば、エージェントは安全に素早く反復できます。なぜなら、あらゆる反復が顧客に届く前に検証されるからです。

この検証の一部は、さらに早い段階に移すこともできます。Chunkを使えば、同種のチェックをエージェントのinner loop内でpre-commit hookとして実行できるため、基本的な失敗はマシンの外に出る前に捕まえられ、パイプラインには本番投入の準備が整ったコードだけが届くことになります。

こうして、AGENTS.md対skillsという問いは自然に解決します。指示を書き、その効果を再現可能なevalで計測し、設定とエージェントの出力をCIでゲートし、その結果をエージェントにフィードバックする。このループが回っていれば、当てるのではなく、あるルールについてどちらのフォーマットがより高い遵守率を得られるかを実際に見ることができます。

フォーマットはフィードバックループの先にある

基本的な結論はこうです。小さく常に当てはまる事実にはAGENTS.mdを、大規模で状況依存的な手順にはskillsを選ぶ。しかし、コーディングエージェントを導くという観点で言えば、これはあなたが下す判断の中で、もっとも重要度の低いものの一つです。

もっと重要なのは、自分たちの指示が実際に機能していると、根拠を持って言えるかどうかです。検証されていないエージェントの出力は、マシンの速度で動き回る負債です。まずフィードバックループを構築し、それをパイプラインで実行してください。そうすれば、フォーマットの問題は、データに基づいて判断できる問いになります。

最も効果的なフィードバックループを構築するには、エージェントがテスト結果に直接アクセスできる必要があります。そのために私たちはCircleCI CLIをゼロから作り直し、エージェントフレンドリーなものにしました。データを返すすべてのコマンドで予測可能なJSONを返し、exit codeを安定させ、組み込みのMCPサーバーを用意することで、エージェントが推測に頼らずにrunをトリガーし、jobを調べ、テスト結果を読み取れるようにしています。コーディングエージェントをCircleCI CLIに向けて、ループを自ら閉じさせましょう。evalを実行し、パス率を読み取り、あらゆるコミットについて、本番環境に届く前に自分自身の変更をゲートするのです。

よくある質問

AGENTS.mdとskills、どちらを選ぶべきですか?

ビルドコマンドやリポジトリの規約のように、ほぼすべてのタスクに当てはまる小さく常に正しいガイダンスにはAGENTS.mdを使いましょう。特定のタスクにしか関係しない、大規模あるいは状況依存的な手順にはskillsを使いましょう。ほとんどのチームは両方を併用し、議論するのではなく、あるルールについてどちらのフォーマットがより高い遵守率を得られるかを計測しています。

エージェントの設定が実際に機能しているかどうかは、どうやってテストすればよいですか?

小さなevalループを構築しましょう。固定されたタスクのセット、エージェントが行ったことを記録するrunner、望んでいた振る舞いをassertするcheckerです。各タスクを複数回実行してパス率を追跡し、設定変更の前後でパス率を比較することで、その変更が何かを動かしたかどうかを確認できます。

モデルをアップグレードしたときに、エージェント設定がリグレッションを起こさないようにするには、どうすればよいですか?

設定をテスト対象のアーティファクトとして扱いましょう。AGENTS.md、skills、モデルバージョンのいずれかに変更が入るたびにCIでevalスイートを実行し、重要なルールのパス率がベースラインを下回った場合は、単体テストが壊れたときと同様にビルドを失敗させてください。