スキルやインストラクション(指示ファイル)をちゃんと使っていますか。
AI エージェントのできることはこの1年でかなり増えました。一方で、AI は環境構築やプロジェクト固有の作法が相変わらず苦手です。というより、こればかりは教えないと無理です。素の設定のまま AI に仕事をさせると、せっかく正解を見つけたり教えたりしても、時間が経つと忘れてしまいます。また、同じ試行錯誤の教え直しに、トークンと時間を使ってしまうのが問題だと感じています。インストラクション・スキル・フックを設定すれば、この無駄を省けます。
本記事では、python 開発向けのスキル・インストラクション・フックをどう管理しているかを紹介します。具体的には、1つのリポジトリに集約した構成、規約・手順・決定論的処理という書き分け基準とその作り方、どのディレクトリで作業しても有効にする設定方法の3点です。ツールは GitHub Copilot を前提にしています。
管理構成:スキルなどを1つのリポジトリに集約
私はどのライブラリの開発でも多くの AI 設定を使いまわしたいため、スキル・インストラクション・フックは1つのリポジトリにまとめています。
あえてユーザーフォルダには集約していません。GitHub Copilot の場合、インストラクションもスキルもフックも、ユーザーフォルダ(~/.copilot/)に置けば全セッションで効きます。それでも集約しないのは、変更履歴を追いたい、ユーザーフォルダには誰にも共有しない個人向けスキルを置いておきたい、社内で配布する可能性もある、という3つの理由からです。
リポジトリ内に instructions・skills・hooks を次のように配置しています。
# スキルなどは一例です。
skills/
├── .github/
│ ├── instructions/
│ │ ├── python-writing.instructions.md # applyTo: **/*.py
│ │ ├── test-writing.instructions.md # applyTo: **/test_*.py
│ │ └── ...
│ └── skills/
│ ├── push/
│ ├── squash-commits/
│ ├── run-ruff/
│ ├── create-changelog/
│ ├── post-slack/
│ └── ...
└── hooks/
├── guard.json # preToolUse: master への直接プッシュを拒否
├── lint.json # agentStop: ruff を実行
├── scripts/ # 上の JSON から呼ぶシェルスクリプト
└── ...
書き分け基準:規約・手順・決定論的処理
規約は instructions、手順は skills、決定論的に固定したい処理は hooks、という基準で書き分けています。
-
守ってほしい規約は
instructionsに書いています。 「os.pathよりpathlibを優先する」のような、手順ではなく書き方の話です。 -
手順を踏んで実行したい知識は
skillsに書いています。 「squash-commits」や「create-changelog」のような、手順どおりに進めてほしい作業の話です。 -
発火条件を固定でき決定論的に処理したいものは
hooksに書いています。 「master への直接プッシュを拒否する」のような、強制の話です。
作り方:Agent Skills 準拠とスクリプト化
スキルとフックの作り方は、「標準仕様への準拠」と「決定論的な部分のスクリプト化」の2本立てです。
スキルを実装するにあたっては、Agent Skillsを参考にしました。Agent Skillsには、Anthropic が提唱したスキルの仕様が Open Standard として記述されています。スキルを量産する前に、標準的な書き方に準拠させるため、Agent Skills準拠の「スキルを作るスキル」を作りました。「スキルを作るスキル」がなくても、AI はそれっぽいものを作ります。ただ Agent Skills が推奨している内容と比べると、全然できていないことがわかります。description がほとんど書かれていなくていつ呼ばれるのか分からない、テンプレートなど出力する形式が明示されていない、など問題がたくさんあります。
決定論的な部分はシェルになるべく書くようにしています。 エージェントスキルはどこまでいっても確率論的にそうなるだけで、文脈によって守られないということが普通に起きます。なので、確実に同じ結果を得られる・得たいものに関してはスクリプトにしておき、スクリプトのパスをエージェントスキルに記載しておきます。
フックも、決定論的に実行したいものはスクリプトにするのと同じ思想で設定しています。例えば「作業終わりに ruff でリントするように」と指示をしていても、守られないことがあります。GitHub Copilot にはフック機能があるので、これを活用して、AI に頼らず確実に実行される環境を整えると良いです。
GitHub Copilot では、セッションの開始・終了からツール実行の前後、権限確認、エラー発生時まで、ライフサイクル全体にフックを差し込めます。
-
sessionStart/sessionEnd:セッションの開始・終了 -
userPromptSubmitted:プロンプトを送信したとき -
preToolUse/postToolUse:ツール実行の前後 -
permissionRequest:権限の確認が出たとき -
preCompact:コンテキストの圧縮前 -
agentStop/subagentStop:エージェントがターンを終えたとき -
notification/errorOccurred:通知・エラー発生時
AI 向けのフックを書く他に、git hook など AI 以前の既存の仕組みも活用しています。
全セッションで参照させるための設定
実体はリポジトリ側に置いたまま、どのディレクトリでセッションを開いても有効にする方法は、インストラクション・スキル・フックで全て違いました。そのため、個別に設定をしました。
インストラクションは環境変数で設定しました。 COPILOT_CUSTOM_INSTRUCTIONS_DIRS にディレクトリを指定しておくことで、その配下の .github/instructions/**/*.instructions.md を使いまわせるようになります。
スキルはコマンドで設定しました。 copilot skill add <ディレクトリ>(セッション中なら /skills add)で、ユーザーフォルダやセッションのディレクトリ以外の場所をスキルの探索先として追加できます。
フックはジャンクションで設定しました。 コマンドも環境変数も見当たらなかったため、ユーザーレベルの ~/.copilot/hooks/ にジャンクションを張って、リポジトリ内のフックを参照させています。
おわりに
AI がコメントを書きすぎるなど、まだ期待した通りの挙動をしないことがあります。構成を見直す余地は大いにあると思います。まとまった知見が溜まれば、また記事にするかもしれません。
この課題に対しては、インストラクションの内容の殆どを pylint のカスタムプラグインに移せないか、最近検討しています。ルールをリンターに寄せられれば、インストラクション自体を簡素化できるはずです。試したいことが多すぎて、一人では検証しきれず大変です。すでに知見のある方がいましたら、より良い方法をぜひ教えていただけますと嬉しいです。