AIパイプラインが壊れる9つの理由 -- 全部AIの外側だった
AIの自動パイプラインを6回テストして、9個のバグを見つけた。
モデルが原因だったものは、1つもなかった。
壊れていたのは全てハーネス — モデルの周りの環境だった。この記事では9個のバグの中身と、その修正方法を全て公開する。
作ったもの
Claude Codeを使って、記事の自動生成パイプラインを構築した。3つの独立したAIセッションが順番に連鎖する仕組みだ。
- Observer — トレンド・競合記事・パフォーマンスデータを調査
- Strategist — テーマを選び、切り口を決め、アウトラインを作成
- Marketer — 記事本文を執筆、品質チェック、公開予約まで実行
各フェーズは独立したClaudeセッション。Observerの出力がStrategistの入力になり、Strategistの出力がMarketerの入力になる。品質チェックに引っかからない限り、人間の介入は不要 — そういう設計だった。
このアーキテクチャ図を紙に描いた時点では、私は天才だと思っていた。
# 目標のアーキテクチャ
observer:
schedule: "0 7 * * 1" # 毎週月曜 7:00
strategist:
after: observer # Observer完了後に起動
marketer:
after: strategist # Strategist完了後に起動
きれいに見える。現実はもっと泥臭かった。

9つのバグ
6回のテストで見つけた全バグを整理する。4つのカテゴリに分類できた。
実行制御系(2個)
バグ1: 並列実行の競合
初期バージョンでは3つのcronジョブを同じ時刻に設定していた。Observerの出力をStrategistがまだ読んでいる最中に、Marketerが起動した。入力なしで。全員が同時に喋り出す会議と同じだ。誰も聞いていない。
# Before: 全部同時に発火
observer: "0 7 * * 1"
strategist: "0 7 * * 1"
marketer: "0 7 * * 1"
修正: 時間ベースのスケジュールからafter依存によるイベント駆動チェーンに変更した。
バグ2: 時間ずらしでも競合
時間をずらしても(7:00, 7:30, 8:00)、Strategistが30分以上かかることがある。設計レベルの競合状態だった。
根本的な修正はバグ1と同じ。時計で管理するな、完了で管理しろ。
データ整合性系(3個)
バグ3: テーマの重複
除外リストがないと、パイプラインが毎回同じテーマを選んでしまう。Observerが「LLMO」がトレンドだと判断し、何度でも「LLMO」を選び続けた。
# 修正: テーマ選定前に除外リストを注入
existing = list_existing_articles()
prompt = f"""
テーマを選べ。以下は既に公開済みなので選ぶな:
{existing}
"""
バグ4: カレンダーの二重登録
パイプラインが既存エントリの確認なしにカレンダーに登録していた。2回実行すると、同じ予定が2つ入る。
修正: 登録前に同名エントリを削除。
バグ5: 公開日の衝突
自動スケジューラーが、既に記事が予約されている日を選んでしまう。同じ日に2本、翌日は0本。
# 修正: 空き日を先に算出
available = get_available_publish_dates(
start=today,
count=batch_size,
existing=get_scheduled_dates()
)
品質保証系(2個)
バグ6: 品質チェックの自己申告
AIが自分の成果物を自分でチェックしていた。「この記事は良いですか?」「はい、素晴らしいです。」宿題を自分で採点して100点をつける小学生と同じ仕組みを、私は真面目に設計していた。
修正: 品質チェックを別のClaudeセッションで実行する。執筆セッションの記憶を持たない、独立したレビュアーに任せた。
バグ7: Witチェックの未実装
AI Slop語彙のチェックはあったが、Wit(自嘲・メタファー・大言壮語の直後に入れるツッコミ)のチェックがなかった。文法的に正しいが、読んでいて退屈な文章が通過していた。
修正: Phase 4にWitチェックを追加。最低2箇所のWit要素を確認する工程を入れた。
インフラ系(2個)
バグ8: bash構文エラー
プロンプトテンプレートに<devto_id>というプレースホルダーがあった。bashが<を入力リダイレクトと解釈し、コマンドが壊れた。エラーも出ない。
# Before: bashが<devto_id>をリダイレクトと解釈
echo "Update article <devto_id> to published"
# After: エスケープまたはクォート
echo "Update article DEVTO_ID_PLACEHOLDER to published"
バグ9: atジョブの二重登録
atコマンドで公開予約をしていたが、同じ記事IDのジョブが既にあるか確認していなかった。再実行すると、同じ記事が2回公開される。
修正: 新しいジョブを登録する前に、同じIDのジョブを削除。
パターン
9個のバグを振り返ると、モデルが悪い文章を生成した事例は1つもない。モデルは問題なかった。壊れていたのはモデルの周りだ。
| カテゴリ | 件数 | 例 |
|---|---|---|
| 実行制御 | 2 | 並列セッション、競合状態 |
| データ整合性 | 3 | 重複、衝突、除外リスト欠如 |
| 品質保証 | 2 | 自己採点、チェック漏れ |
| インフラ | 2 | シェルエスケープ、ジョブ管理 |

これはAIエンジニアリングの3層モデルにきれいに対応する。
- Prompt Engineering: モデルへの指示を最適化する
- Context Engineering: モデルに送る情報全体を最適化する(RAG、ツール、メモリ)
- Harness Engineering: モデルが動作する環境全体を最適化する
9個全てがハーネスのバグだった。Y Combinatorの調査でも、AIエージェントプロジェクトの40%が失敗しており、共通の原因はモデルの質ではなくハーネスの不在だという。
修正: イベント駆動チェーンへの移行
最もインパクトが大きかった変更は、時間ベースのcronからイベント駆動の依存チェーンへの移行だ。
# 最終アーキテクチャ
observer:
schedule: "0 7 * * 1"
strategist:
after: observer
marketer:
after: strategist
各フェーズは出力を所定の場所に書き込む。次のフェーズは前のフェーズが正常完了した場合のみ起動。途中で失敗したらチェーンが止まる。下流への汚染はない。
9個の修正を全て反映した7回目のテストでは、5本の記事を一括生成し、衝突しない日程に自動スケジュールし、それぞれ独立した品質チェックを通過させることに成功した。
学び
AIエージェントの品質は、AIの外側で決まる。
モデルはシェフ。コンテキストは食材。ハーネスはキッチン。
キッチンが壊れていたら — コンロが同時に暴発し、食材が混ざり、誰も味見をしない状態では — シェフの腕は関係ない。
私はプロンプトの最適化に3時間かけた。キッチンの点検に使ったのは0分だった。自分がバグ10個目だ。
プロンプトを最適化する前に、キッチンを点検しよう。
さらに深掘りしたい方へ
本記事はその一面に過ぎません。OpenAI・Anthropic・LangChain・Martin Fowler・学術の5つの解釈を1冊に統合した体系書 ハーネス・エンジニアリング — AIを”使う”から”操る”へ で、ハーネスとは何か、どう設計し、どう運用するかを19章で解説しています。
この記事は役に立ちましたか?