1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeのスキル設計で最も大事なこと — 決定論的な処理はシェルに逃がせ

1
Posted at

スキルを作れば作るほど、壊れるスキルが増えた

Claude Codeのスキルを10個ほど作ってきた。記事投稿、壁打ち、ツイート生成、情報収集、画像生成。最初は「スキルさえ作ればAIが全部やってくれる」と思っていた。

現実は違った。同じスキルを実行しても、毎回微妙に違う結果が返ってくる。フロントマターの変換で余計なフィールドが入ったり、ファイルの保存先が変わったり。AIが「良かれと思って」やった判断が、再現性を壊す。

この問題に何度もハマった末にたどり着いた原則がある。

「一つのスキルの中で、決定論的な処理と非決定論的な処理を見極めろ。決定論的な部分はシェルスクリプトに逃がせ。」

決定論的 / 非決定論的とは何か

ここでの定義はシンプルだ。

  • 決定論的: 同じ入力に対して、毎回同じ出力を返すべき処理
  • 非決定論的: 文脈や状況によって判断が変わる処理

例を挙げる。

処理 性質 理由
Zennのフロントマターをqiita形式に変換する 決定論的 topics: [A, B]tags: [{name: A}, {name: B}] は機械的な変換
記事のタイトルを考える 非決定論的 読者層・文脈・トーンによって最適解が変わる
published: trueignorePublish: false の変換 決定論的 真偽値の反転。判断の余地がない
記事の構成を決める 非決定論的 テーマの深さ、読者の前提知識に依存する

この区別は直感的にわかるはずだ。問題は、スキルを作るときにこの2つを混ぜてしまうことにある。

混ぜるとなぜ壊れるか

自分が作った「記事投稿スキル(article-publish)」を例にする。

このスキルの仕事は、Zenn形式で書いた記事をQiita形式に変換して公開することだ。やりたいことを分解するとこうなる:

  1. Zennのフロントマター(topics, published, emoji)をQiitaのフロントマター(tags, ignorePublish)に変換する
  2. 変換したファイルを public/ に配置する
  3. git pushでGitHub Actionsを発火させる

1〜3はすべて決定論的だ。入力が同じなら出力は常に同じであるべきだし、途中でAIが「このタグ構成より、こっちの方がいいかも」と判断する余地はない。

では、これをSKILL.mdにテキストで書いたらどうなるか。

## 変換ルール
- topics配列をtags形式に変換してください
- publishedの値を反転してignorePublishに設定してください
- タグは最大5個、初心者タグを含めてください

AIはこの指示を「だいたい」守る。だが「だいたい」では困る。タグの順序が変わる。クォートの有無が揺れる。余計な属性が追加される。毎回同じ結果を出すべき処理をAIの解釈に委ねているのが問題だ。

シェルに逃がすとどうなるか

同じ処理をシェルスクリプトにするとこうなる(実際のコードを簡略化したもの):

# topics配列 → Qiita tags形式に変換(最大5タグに制限)
TOPICS=$(echo "$FRONTMATTER" | grep '^topics:' | sed 's/^topics: *//')
CLEANED=$(echo "$TOPICS" | tr -d '[]"' | tr ',' '\n' | sed 's/^ *//;s/ *$//')

while IFS= read -r tag; do
  [[ -z "$tag" ]] && continue
  TAG_LIST+=("$tag")
done <<< "$CLEANED"

# published → ignorePublish(反転)
if [[ "$PUBLISHED" == "true" ]]; then
  IGNORE_PUBLISH="false"
else
  IGNORE_PUBLISH="true"
fi

このスクリプトは何度実行しても同じ結果を返す。AIの気分に左右されない。テストもできる。デバッグも楽だ。

決定論的な処理をシェルに逃がした瞬間、スキルの信頼性が跳ね上がる。

では、スキル側(SKILL.md)には何を書くのか

シェルに逃がせない処理がある。それが非決定論的な部分だ。

article-publishスキルのSKILL.mdには、こういうことが書いてある:

  • 投稿時間の戦略: 「平日朝8:00が最適。月曜7〜8時がいいね獲得率が高い」— これはデータに基づく判断指針であり、状況に応じて変わりうる
  • 画像の使い分け: 「Mermaidで済むものはMermaidで書く。図解が必要ならimage-gen-codeを使う」— 何が適切かは記事の内容次第
  • トーン: 「フラットな語り口。先輩エンジニアが話す感じ」— AIが文脈に応じて調整すべき領域

これらは「毎回同じ結果を出す」必要がない。むしろ文脈に応じた判断をAIに任せた方がいい。SKILL.mdは、その判断のためのリファレンス(参照情報)を提供する場所だ。

公式ドキュメントも同じことを言っている

Anthropicの公式スキル設計ガイドでは、「自由度の設定(degrees of freedom)」という概念でこの考え方を体系化している。

自由度 用途 具体例
低い(シェルスクリプト) 操作が壊れやすく、一貫性が重要 データベースマイグレーション、フォーマット変換
中程度(パラメータ付きスクリプト) パターンはあるが設定で挙動が変わる レポート生成、テンプレート適用
高い(テキスト指示) 複数のアプローチが有効で、文脈判断が必要 コードレビュー、構成の提案

さらに公式は明確にこう述べている:

"Prefer scripts for deterministic operations"(決定論的な操作にはスクリプトを優先せよ)

これは「シェルでできることはシェルでやれ」と同義だ。公式がスキルのベストプラクティスとして、決定論的な処理をスクリプトに分離することを推奨している。

また、公式は自由度の概念をわかりやすい比喩で説明している:

  • 崖に挟まれた狭い橋: 安全な道は一つしかない。具体的なガードレールと正確な指示を出す(低い自由度)
  • 障害物のない広い野原: 多くの道が成功に繋がる。大まかな方向を示してAIに任せる(高い自由度)

まとめ: スキル設計の判断フロー

スキルの中で「これをどう実装するか」迷ったとき、この判断フローが使える:

この処理は、同じ入力に対して毎回同じ出力を返すべきか?
  │
  ├─ YES → シェルスクリプトに書く
  │         理由: 再現性が保証される。テスト可能。デバッグしやすい
  │
  └─ NO  → SKILL.md / リファレンスに書く
            理由: 文脈依存の判断はAIの得意分野。参照情報を充実させる

これだけだ。シンプルだが、これを意識するだけでスキルの信頼性は大きく変わる。

シェルに逃がすもう一つの利点は、テストの粒度が変わることだ。スキル全体をE2Eで動かして確認するのは重い。だがシェルスクリプトなら、そのスクリプト単体で入力を与えて出力を検証できる。バグが出たときも「スキルの指示が悪いのか、スクリプトのロジックが悪いのか」を切り分けられる。決定論的な処理をシェルに分離することは、テスタビリティの確保でもある。

シェルでできることは、シェルでやれ。AIには、AIにしかできない判断を任せろ。

参考文献

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?