メインコンテンツへスキップ
← 記事一覧に戻る
·運営·8 min read

LLMの自動化は指示文でなく決定論的ゲートで守る — content-executor Step5 実装記録

Claude CodeautomationroutineCIvalidate個人開発
LLMの自動化は指示文でなく決定論的ゲートで守る — content-executor Step5 実装記録

「指示文を直したのに、また同じエラーが出た」——そう思ったことがあるなら、この話は自分ごとになるはずです。

結論

LLMへのテキスト指示は、フォーマット制約を守る保証にならない。守らせたい制約は、push前の決定論的ゲート(シェルスクリプト)で機械的に止める。

これが、2026年6月のQiita/Zenn CI再発事故から私が出した答えです。

この記事の前提

  • スタック: masatoman.net(Next.js + Supabase) + Qiita/Zenn 外部配信
  • 運用: content-executorというクラウドルーチンで記事を自動生成→各媒体にadapt→push
  • 事故日: 2026-06-19頃(Qiita/Zenn CIが連続失敗)
  • 「実装して成功した話」ではなく「再発事故を受けてどう設計変更したかの判断記録」です

読者のよくある詰まり

  • LLMに「このフォーマットで出力して」とプロンプトに書いたのに、たまに崩れる
  • 崩れるたびにプロンプトを修正するが、また別の場所で崩れる
  • 「指示を徹底すれば防げる」と思い込んで、同じ作業を繰り返している

これはプロンプトの問題ではありません。LLMの構造的な問題です。

実際に起きたこと

Qiita/Zenn CIが連続失敗した。原因はfrontmatterのフィールド欠落とpublished_atの混入。

2026-05-17の事故を受けてCIに検証ガードを入れた。そのガードが「設計通りに」働いてpushを止めていた——つまり再発した。

再発の真因はこうだ。

content-executorがQiita/Zenn向けのadapted記事を生成する際、LLMはfrontmatterテンプレートを「知っている」が「確実に守る」わけではない。 フィールドを一部省略したり、過去に見たフォーマットを混入させたりする。テキスト指示でテンプレートを渡していたが、LLMはそれを「参考情報」として扱う。

もうで1つの発見があった。「生成側を直そう」としてローカルのSKILL.mdを修正したが、何も変わらなかった。実際に記事を生成しているのはクラウド上で稼働しているcontent-executorルーチンであり、ローカルのSKILL.mdは参照されていなかった。authoritative な設定はクラウド側のスケジュール設定にある。ローカルで何を直しても、クラウドルーチンが読むファイルを変えなければ無意味だった。

原因分析

問題の構造を整理すると:

何をしているか限界
テキスト指示(プロンプト)LLMに「このフォーマットで」と伝えるLLMは守る「努力」をするが保証はない
CIガード(push後)壊れた記事を検出して止めるpush後なので記事は生成済み。失敗ログが積み上がる
push前ゲート(今回の解決策)生成直後・push前に機械的に検証壊れた状態でpushされることを原理的に防ぐ

LLMを使う自動化において、「指示文を厚くする」アプローチは限界がある。LLMの出力を信頼するのではなく、出力の境界に決定論的な検証を置くのが正しい設計だ。

実装した判断:Step5にゲートを埋め込む

解決策は「content-executorのStep5(外部配信adapted生成後)にvalidate-frontmatter.shを必ず通す」だった。

Step5の処理順(変更後):
1. Qiita向けadapted記事を生成(LLM)
2. cd Qiita && git pull --rebase
3. bash scripts/validate-frontmatter.sh public/{slug}.md  ← ここが新設ゲート
   → NGなら自動補完を試みてexit0まで繰り返す
   → 通らなければpushをスキップ(記事は生成されるが配信されない)
4. exit0確認後にpush

validate-frontmatter.shはシェルスクリプトで、フィールドの存在・型・禁止フィールドを機械的にチェックする。LLMの気分に左右されない。通れば進む、通らなければ止まる——それだけだ。

重要な設計原則: ゲートは「LLMが正しく出力したかの確認」ではなく「機械が壊れた状態を通さない境界」として機能させる。LLMを信頼しないのではなく、LLMの出力に依存する部分を最小化する。

判断軸:どの制約を「ゲート」にするか

すべての制約をゲートにするのは過剩だ。以下の基準で判断する:

ゲートにすべき制約:

  • フォーマット・スキーマの制約(frontmatterのフィールド存在・型)
  • 下流システムが依存する制約(CIが読む、DBが期待するスキーマ)
  • 崩れたときに自動修復が難しい制約(publishされた記事のURL/slugの変更)

テキスト指示で十分な制約:

  • 内容・文体・トーン(ある程度崩れても手動修正できる)
  • 創造的判断を伴うもの(ゲートで測定できない)

判断の問い:「これが崩れたとき、機械的に検出して止められるか?」Yesならゲートにする。

ローカルとクラウドの乖離問題

もうで1つの教訓として記録する。

自動化を設計するとき、「どのファイルが実際に参照されているか」を必ず確認する。私の場合:

  • ローカル: ~/home/work/claude-code/SKILL.md(手で編集できる)
  • クラウドルーチン: スケジュール実行時にロードされる設定(別の場所にある)

ローカルのSKILL.mdを完璧に書いても、クラウドルーチンがそれを読まなければ意味がない。「どこが本当のauthoritativeか」を把握せずに修正しても、デバッグに時間を湶かすだけだ。

今日やること(3つ以内)

  1. 自分のLLM自動化パイプラインで「フォーマット崩れが起きたら下流が壊れる」箇所を1つ特定する。 その箇所のpush前に検証スクリプトを入れることを検計する。

  2. 「テキスト指示を直す」を繰り返している制約がないか確認する。 同じ崩れが2回以上起きたなら、指示ではなくゲートで対処する番だ。

  3. クラウドで動いている自動化がある場合、「実際に参照されているファイル」を1つ確認する。 ローカルのコピーと乖離していないか。


masatoman のメルマガ — 毎週月曜の朝に手紙を 1 通

masatoman.net の今週の記事 1 本を、読者目線で深掘りした手紙が毎週月曜 9:00 に届きます。「これ自分のことだ」が見つかる予告編。登録特典に「個人開発の収益化チェックリスト 15 項目」。

masatoman のメルマガ — 毎週月曜の朝に 1 通

masatoman.net で今週公開した記事の中から 1 本を、読者目線で深掘りした手紙が届きます。「自分も同じことやってる」「ここで詰まってた」が見つかる予告編。

記事が役に立ったら気軽にどうぞ。

この記事が役に立ったらシェア