この記事は playpark Blog からの転載です。
この記事で分かること
- Claude Code Skillsでエラー出力をJSON形式に統一する理由
- exit codeのみ・ログファイル・構造化出力の比較と選択基準
- どういう状況でこの設計を採用すべきか
背景:こういう課題があった
Claude Code Skillsが増えてくると、「スクリプトが失敗しているのに正常扱いになる」問題が発生します。
AIエージェントはスクリプトの出力を解釈して次の行動を決定します。このとき、エラー情報が構造化されていないと:
- exit codeが1でも「何が」「どこで」失敗したかが分からない
- エージェントが間違った判断をしてデバッグが長引く
- 別のSkillから呼ばれる場合、上流が正確なエラー理由を取れない
「エラーは出ている、でも理由が分からない」 という状況は、人間でも時間を消費しますが、AIエージェントにとっては構造化データがないと文字通り解析不能です。
選択肢の検討
| アプローチ | メリット | デメリット |
|---|---|---|
| exit codeのみ | 実装が最もシンプル | 失敗理由が不明、エージェント側で解析できない |
| ログファイルに書く | 詳細ログを残せる | リアルタイムに取れない、ファイルパス管理が必要 |
| stderr に JSON 出力(採用) | 構造化されていてエージェントが解析可能、stdoutと分離できる | ヘルパー関数が必要 |
なぜこのアプローチを選んだか
Claude Code のエージェントは stdout をデータとして、stderr をエラー情報として区別して処理できます。JSONで出力することで、SKILL.md側が jq を使って失敗理由を取り出し、次の行動(リトライするか、別アプローチを試みるか)を判断できます。
exit codeだけだと「失敗した」という事実しか分かりません。JSON化すると「どんなエラーで」「どのexit codeで」失敗したかが一発で取れます。これがエージェント間の信頼できる通信プロトコルになります。
実装例
共通ライブラリに die_json ヘルパー関数を定義します:
die_json() {
local msg="$1"
local code="${2:-1}"
# json_str: 文字列をJSONセーフにエスケープするヘルパー
echo "{\"status\":\"error\",\"error\":$(json_str "$msg"),\"exit_code\":$code}" >&2
exit "$code"
}
入力バリデーションもこのパターンで統一します:
VALID_CATEGORIES="logic state external environment"
local valid=false
for v in $VALID_CATEGORIES; do
[[ "$HYP_CATEGORY" == "$v" ]] && valid=true && break
done
[[ "$valid" == true ]] || die_json "Invalid category: $HYP_CATEGORY. Must be: $VALID_CATEGORIES"
状態ファイルの初期化では jq -n を使い、スキーマを確定させます:
jq -n \
--arg target "$target" \
--arg scope "$scope" \
--argjson auditors "$auditors" \
'{
version: "1.0.0",
target: $target,
scope: $scope,
status: "initializing",
auditors: $auditors,
findings: []
}' > "$STATE_FILE"
version フィールドを入れる理由は、スキーマ変更時に呼び出し側が「これは旧フォーマット」と検知できるようにするためです。
まとめ:どういう場面で使うべきか
Skillが他のSkillから呼ばれる構成、あるいはAIエージェントがスクリプト出力を解釈して判断を下す構成であれば、JSON化は早い段階で入れる価値があります。
Skillが単体で完結していて人間がログを確認する運用なら、exit codeだけでも十分です。ただし、Skillが10個を超えて相互呼び出しが増えてきたタイミングが、この設計を入れるしきい値の目安です。
さらに深掘りしたい方へ
この記事ではJSON契約による型安全性の選定理由を解説しました。
【Claude Code】Skills設計パターン上級編 - 型安全性・エラーハンドリング・スキル間連携の実装 ではさらに:
- フェーズ別Auto-Retryプロトコル(8フェーズごとのリトライ戦略と「盲目的リトライ禁止」の設計判断)
- 3層コンフィグマージ(グローバル設定とプロジェクト設定を
jqでdeep mergeする実装) - Agent Team並列協調の状態管理パターン(単一ライター設計とクロスドメイン検出)
を扱っています。
playpark について
playpark LLC - 業務自動化・AI活用・Web開発