スキルはコードではなく「呼び出し契約」—エージェントの作業に対するレビュー権限を保つ方法

Dev.to / 2026/4/19

💬 オピニオンDeveloper Stack & InfrastructureIdeas & Deep AnalysisTools & Practical Usage

要点

  • この記事では、AIエージェントの「スキル」をコードではなく呼び出し契約(例:SKILL.md)として扱うべきだと主張し、レビューでは生成された実装の細部ごとではなく契約レベルで振る舞いを確認するべきだと述べています。
  • 実例として(claude-code-mcp-qa-automation)、16個のスキルを純粋なMarkdownで管理し、基盤となるPython実装は差し替えやリファクタリングを行っても契約の再レビューが不要になる構成を説明しています。
  • SKILL.mdの4つのセクション(frontmatter、inputs、delegated work、failure modes)を整理し、期待する入力、ふるまい、エラー処理をレビュー可能な形で定義することの重要性を強調しています。
  • 著者は、スケールの曲線が異なる2つのモデルを対比し、コードをスキルとする方式は変更のたびに人間がコードを読み直す必要があるためスケールしにくく、一方でMarkdownをスキルとする方式は契約が安定しているためエージェントによる再生成があってもレビュー負荷が増えにくいとしています。
  • 全体として、人間がエージェントの振る舞いに対するレビュー権限を保ちつつ、複数のエージェントが同じスキル面を効率的に扱えることを目指しています。

スキルがコードなら、コードをレビューします。スキルがマークダウンの契約(コントラクト)なら、契約をレビューします――そしてその下で、実装はエージェントによって再タイピングされます。同じ表面(サーフェス)に対して複数のエージェントが作業するのをスケールさせることができます。もう一方は、実装を人間が再読込する必要が生じた瞬間に崩れます。

これは、claude-code-mcp-qa-automation で私が実行しているパターンです。16個のスキルが、純粋なマークダウンファイルとして存在します。下にあるPythonの実装は差し替え可能です――書き換え、リファクタ、置換しても、スキルの表面に触れる必要はありません。レビュー権限が本来あるべき場所に留まるのはここです。すべての生成された1行にではなく、契約(コントラクト)に対してです。

SKILL.md が実際に含むもの

スキルファイルはドキュメントではありません。呼び出し(インボケーション)の契約です。4つのセクションがあり、それぞれ特定の目的を持っています。

フロントマター(Frontmatter)。 名前、説明、1〜2個のタグ。これは、エージェントローダがスキルを選択してインデックスするために使うものです。説明はシステムに読まれ、人間には読まれません――文章としての品質ではなく、マッチ品質が最適化されています。

入力(Inputs)。 スキルが受け取ることを期待するものです。型、形(シェイプ)、必須フィールド、入力が欠けていた場合の失敗条件。呼び出し側が不正形式の入力を送った場合のスキルの挙動は、ここで決まります。実装の中ではありません。

委譲される作業(Work delegated)。 スキルが行うこと。どのようにではありません。契約は、レビュー担当者が理解する必要がある粒度でメカニズムを名付けます:「Jiraからそのスプリントのチケットを取得し、ステータス遷移をトレンドのストアに集約し、(チケット、日)ごとに1行を書き込む」。実装の詳細――どのPythonライブラリか、どのHTTPクライアントか、どのページネーション戦略か――はコードにあり、契約にはありません。

区別された失敗モード(Failure modes distinguished)。 スキルが別々に表面化する具体的な失敗タイプ:一時的なJiraエラー、認証失敗、スキーマ不一致、レート制限によるリトライの使い切り。各々に専用のハンドラと、独自のログの形があります。

レビュー担当者はその4つのセクションを読みます。それが意図した挙動と一致していれば、スキルはレビューに合格します。その下の実装は、チームが必要とする任意の頻度で差し替えられます――エージェントが書き換えているなら日次でも――契約が変わっていないため、別のレビューサイクルを引き起こしません。

なぜこれがスケールし、コードスキルはしないのか

2つのパターン、2つのスケーリングカーブ。

コードをスキルとして扱う。 各スキルはPythonモジュール、または一連の関数です。レビュー担当者はコードを読まないと挙動を理解できません。新しいスキルを追加するには、より多くのコードを書くこと、テストすること、レビューすることが必要です。実装の差し替えは、レビューの書き換えを意味します。スケーリングのボトルネックは人間のレビュー能力です。

マークダウンをスキルとして扱う。 各スキルは契約(コントラクト)です。レビュー担当者は契約を読みます。新しいスキルを追加するには、契約を書くことが必要です。実装は、その契約の下で、どのエージェントまたはエンジニアがその特定のスタックで最も速いかによって生成されます。実装の差し替えは、同じ契約の下で再生成することを意味します。

2つ目のパターンは、エージェントによる再タイピングに耐えます。実装を再生成するLLMは、レビュー担当者が気づかない形で契約を変えることはできません。コードスキルとして再生成するLLMは、表面を静かに変更し得て、レビュー担当者は差分(diff)でそれを見つける必要があります――これは、エージェント工学の分野が「盲目的にdiffを受け入れる失敗モード」と名付けているものです。

契約の下でのサブエージェントのオーケストレーション

QAオートメーションのパイプラインには、作業をサブエージェントへ分散(ファンアウト)するコーディネータがいます。サブエージェントはボード単位で動きます(Jiraボードごとに1つ、並列)。コーディネータはそれらの出力を統一されたレポートに集約します。

本番では、コーディネータは Claude Code の Agent ツールを使うでしょう。参照実装では、コーディネータは構造的に同等なスタンドインとして ThreadPoolExecutor を使います。同じファンアウトの形、同じ集約のセマンティクス、同じ決定的な出力――ただし参照では、ライブなClaude呼び出しを要求せずに動くため、どのCIランナーでもエンドツーエンドでレビュー可能です。

コーディネータ用のスキルファイルは契約を名前で定義します:「入力はボードのリスト、出力は各ボードのレポート1つに加えてロールアップ1つ。サブエージェントのいずれかが失敗しても、全体のファンアウトは失敗にならない」。実装(ThreadPoolExecutorまたはAgentツール)は、契約が変わらない限り変えてよい詳細です。

フラグによるゲート、設定駆動の実行

オン/オフがあり得るあらゆる挙動は config/flags.yaml にあり、グローバルとボード単位のオーバーライドを備えています。Python内にインラインの if FEATURE_FOO: トグルは置きません。

flags:
  global:
    enable_trending: true
    slack_digest: true
    include_closed_tickets: false
  boards:
    ENG:
      include_closed_tickets: true
    OPS:
      slack_digest: false

回帰のデバッグは、コードを手探りで掘ることから始めるのではなく、フラグを切り替えて再実行することから始まります。バグ報告が来ます:「火曜日からOPSのダイジェストが欠けています」。最初の確認は config/flags.yaml です――フラグは切り替えられましたか?はい、それなら原因です。いいえなら、バグはコードパス側にあります。つまり2つ目の確認になります。

この切り分けが、パイプラインを監査可能にする理由です。設定(config)はソース管理されています。フラグの切り替えはすべてコミットです。「先週金曜のレポートを作ったもの」と「今日のレポートを作ったもの」との差分は、常に見えています。

決定的で、レビュー可能な出力

レポートそのものは、インラインCSSを含む単一のHTMLファイルで、JavaScriptはゼロ、外部参照もありません。この制約は見た目のためではありません。まったく同じフラグのもとで、オフラインでレビューでき、アーカイブでき、バイト単位で差分化(diff可能)できるようにするのがこの制約です。

./pipeline.py --config=flags.yaml --board=ENG > run1.html
./pipeline.py --config=flags.yaml --board=ENG > run2.html
sha256sum run1.html run2.html
# should print identical hashes

2つのハッシュが異なるなら、パイプラインのどこかに決定性のない要素があり、それを見つける必要があります。決定性が崩れる原因の多くは偶発的なものです。古いPythonバージョンでのdictの反復順序、出力内のタイムスタンプ、ページネーションでのランダムサンプリングなど。それぞれに修正方法があります。この制約は、決定性のなさを隠したままにすることを許さず、修正を強制します。

決定性(determinism)があるからこそ、出力はコンプライアンス品質の成果物になります。再現できないレポートは、利害関係者が信頼できないレポートです。再現できるレポートとは、将来の任意の時点でソースから再生成できるものであり、これが「レビュー可能」の実際の定義です。

これは一般に Claude-Code のスキルとどうつながるのか

上記のスキルパターンはQA-automationの表面に固有ですが、仕組み自体は一般化します。Claude Codeにおけるスキルとは、エージェントローダーが選択する、名前が付いて参照可能な契約(コン​​トラクト)です。最適なスキルファイルとは、査読者が1分以内に読めること、そしてエージェントが曖昧さなくインスタンス化できることを満たすものです。

スキルが良いかどうかは、次の3つの特性で決まります。

1. 引き金が厳密である(Invocation-tight)。 スキルの説明は、ローダーに対して「いつ発火し、いつ発火しないか」を正確に伝えます。説明が緩いと、間違ったタイミングで間違ったスキルが発火してしまい、これはスキル関連のバグとして最もよくあるものです。

2. 実装に依存しない(Implementation-free)。 スキルの契約は、Pythonモジュール名、特定のライブラリ、内部ファイルパスなどを名指ししません。そうした詳細は実装側が所有するものです。実装の具体に言及しているスキルは、契約を書き換えずに再実装できないスキルです。

3. 失敗時の挙動が明示されている(Failure-mode-explicit)。 契約は、成功パスとは別に、そのスキルが提示する失敗モードを名前で挙げます。契約の中に失敗モードが見えないレビュー担当者は、成功パスがどれほど読みやすく整っていても、そのスキルが不完全だと判断できます。

この3つすべてを満たすスキルは、エージェントをまたいで、実装をまたいで、そして時間をまたいでスケールします。3つ未満しか満たさないスキルは、すぐにコードがスキルになる(code-as-skill)状態へ劣化します。そうなると、レビューのボトルネックが再び現れ、マークダウン層が存在している価値が失われます。

What you do with this pattern

規模の大小を問わず、Claude Codeの構築に取り組んでいるなら、skillsディレクトリは最も投資対効果が高い領域です。複利で効いてくる2つの手:

  • 既存のスキルを1つ監査する。上記の3つの特性について確認します。どれかの特性が失敗しているなら、次のスキルを追加する前に契約を書き換えてください。1つの書き換え済み契約は、継承されたドリフトを持つ3つの新しいスキルに相当します。
  • 設定を振る舞いから分離する。 すべてのトグル、すべての閾値、そして環境固有の値を、コードから取り出してフラグファイルへ移動します。次の回帰(レグレッション)捜索は、50分ではなく5分で終わります。

この2つの手は、あなたがその上に築くエージェント・エコシステム全体で複利的に効いてきます。QA-automationリポジトリは、このパターンの具体的な一形態です。しかし形は一般化でき、レビュー権限が希少なリソースであるあらゆるClaude Codeの表面に広げられます。

Aman Bhandari。AIエンジニアリングの研究ラボのオペレーターで、コーチングパートナーとしてClaude Opusを運用し、さらに実際のスプリント業務に対して出荷するQA-automation表面にも対応しています。公開成果物:claude-code-agent-skills-framework および claude-code-mcp-qa-automationgithub.com/aman-bhandari