2026 年の春、私は個人で 4 サイトの AI 技術ブログを Claude Code の Skill で自動生成しています。1 サイトあたり 1 日 4 本、合計で 16 本前後が毎日 push される運用です。最初の数週間は順調でしたが、ある時期から導入文がどこか見覚えのある書き出しに収束してくる現象が増えてきました。SKILL.md にはテンプレ的なフレーズを禁止する旨を書き連ねてあるはずなのに、です。
原因を追って気づいたのは、自然言語のルールだけでは抑えきれない領域があるという事実でした。同じ Skill を毎日 16 回も走らせていると、その日の文脈や記事テーマの偶然で、何回かに一度はテンプレ語句が混じります。1 本ずつなら気にならない量でも、1 ヶ月でサイト全体に積もっていきます。私の場合は、これがじわじわサイト評価を下げる結果になりました。
そこで採ったのが、SKILL.md の最終ステップに grep ベースの自動チェックを組み込むという、ごくローテクな手段です。プロンプトの指示は「お願い」でしかないけれど、bash の grep は「見つかったら exit 1」と物理的に押し戻してくれます。今回は、その仕組みと運用してみての所感を共有します。
なぜプロンプトだけの指示では抑えきれないのか
Claude Code の Skill は、SKILL.md に書いた手順とルールを読んで実行します。「禁止」と書けば多くの場合は守ってくれますが、決定論的ではありません。LLM の出力には常にゆらぎがあるため、100 本生成すれば数本はすり抜けてきます。
私の運用で観測したパターンは次のようなものでした。
- 日本語の導入テンプレを避けても、英語版で
Let's dive into ...が混入する - 「網羅」系の禁止語を消したつもりが、別の言い換えがタイトルに紛れる
- 締めの定型句を消しても、「〜できるようになるはずです」のような言い換えがじわじわ復活する
1 本ずつ目視レビューできれば修正できますが、1 日 16 本のペースでは現実的ではありません。だからこそ「人間のレビュー」ではなく「機械的な検出」を Skill 内部に組み込む必要が出てきました。
SKILL.md に grep ガードを差し込む最小例
SKILL.md の Step 4(品質チェック)に追加しているガードのうち、もっとも効いている部分を抜粋します。検出語句は環境変数にまとめておくと、後から追加・削除しやすくなります。
# Step 4 の最後に挿入する禁止語句チェック
NEW_FILE="$WORK/content/articles/ja/${CATEGORY}/${SLUG}.mdx"
VIOLATIONS=0
# (1) タイトル禁止パターン
TITLE_BAD='完全ガイド|徹底解|決定版|[0-9]+選'
if grep -qE "^title:.*(${TITLE_BAD})" "$NEW_FILE"; then
echo "FAIL: title contains forbidden pattern"
VIOLATIONS=$((VIOLATIONS+1))
fi
# (2) 導入文 600 文字以内の禁止フレーズ
HEAD=$(awk 'BEGIN{fm=0} /^---$/{fm++; next} fm==2{print}' "$NEW_FILE" | head -c 600)
INTRO_BAD=("最近" "における重要性")
for phrase in "${INTRO_BAD[@]}"; do
if echo "$HEAD" | grep -qF "$phrase"; then
echo "FAIL: intro contains \"$phrase\""
VIOLATIONS=$((VIOLATIONS+1))
fi
done
if [ "$VIOLATIONS" -gt 0 ]; then
echo "Stopped: ${VIOLATIONS} violation(s). Fix and re-check."
exit 1
fi
echo "Guard passed."実行すると、たとえば違反があった場合は次のような出力になります。
FAIL: title contains forbidden pattern
FAIL: intro contains "最近"
Stopped: 2 violation(s). Fix and re-check.
ポイントは exit 1 で Skill 全体を停止させていることです。Claude Code はこの終了コードを見て「次の Step に進めない」と判断し、自分で記事を書き直そうとします。
違反検出時に「自己修正ループ」を Skill に書く
ただ exit するだけでは、Claude Code は単に止まってしまいます。そこで SKILL.md 側に、違反検出時の挙動も明示しておきます。私が使っている書き方の一例です。
### 違反検出時の自動リカバリ
grep チェックが exit 1 を返した場合、以下を順に実行する:
1. 検出された違反パターンを変数に保存し、`cat` で MDX を読み込む
2. 該当箇所を sed で書き換えるか、MDX 全体を再生成する
3. 同じ grep チェックを再度実行する
4. 3 回以上違反が続く場合のみ、ログに `Status: FAILED` を記録して終了「3 回まで」と上限を設けているのは、無限ループを避けるためです。実運用では、ほとんどの違反は 1 回の書き直しで解消されます。
1 週間運用してみての所感
このガードを入れる前と後で、目視で気づいた変化は次のようなものでした。
- タイトルから「完全」「網羅」系の語が消えた(grep で機械的に弾けるため)
- 導入文の冒頭がテンプレ的な語り出しに収束する頻度がほぼゼロになった
- 一方で、締め部分の「〜できるようになるはずです」のような言い回しは、grep だけでは完全には消えなかった
最後の点が示唆的でした。grep は「文字列の完全一致」しか見られないため、AI が表現を少し変えるとすり抜けます。それでも、明らかに頻出していたフレーズを 90% 以上抑えられるだけで、サイト全体の印象はかなり変わります。
そして大事なのは、ガードを増やしすぎないことです。チェック項目が多くなりすぎると、生成自体が通らなくなり、自己修正ループが 3 回上限に達して FAILED で終わる頻度が上がります。私の場合は、まず 5 項目だけに絞って、運用しながら追加するペースを保っています。
16 歳のときに身につけた習慣との接点
少し脱線しますが、私は 1997 年、16 歳のときに独学でプログラミングを始めました。当時のインターネットは、国境を越えて見ず知らずの人と一緒に何かを作れる場所でした。人がやっていることを見て真似ながら、何が良いコードで何が悪いコードかを少しずつ学びました。
そのときから自然と身についた感覚として、「ルールを口で説明するより、機械にチェックさせた方が早い」というものがあります。CI が静的解析を回すのも、Lint が走るのも、人間に頼らない仕組みを作る発想です。Skill の grep ガードも、結局はその延長線上にあるのだと思います。
両祖父が宮大工だったこともあり、「手を動かして作るものは、後から自分で見直せる形にしておく」という感覚も大事にしています。Skill に grep ガードを書くというのは、未来の自分(あるいは Claude Code の次回実行)が、自分の出力をもう一度検査できる状態を残すことでもあります。
次に試してほしいこと
今日の終わりに、自分の SKILL.md に grep チェックを 1 つだけ追加してみてください。最初は「タイトルに頻出する陳腐な語が入っていないか」だけで十分です。1 行の grep -qE と exit 1 で、そのあとの 1 ヶ月の出力品質が変わってきます。
完璧な禁止リストを最初から作る必要はありません。1 つずつ追加して、自分のプロジェクトで実際に出てきたパターンに合わせて育てていくのが、結果的にいちばん長持ちする運用になります。
実装の参考になれば嬉しいです。お読みいただきありがとうございました。