← ブログに戻る

AIパイプラインが壊れる9つの理由 -- 全部AIの外側だった

harness-engineeringclaude-codeai-agentautomationdevops

AIの自動パイプラインを6回テストして、9個のバグを見つけた。

モデルが原因だったものは、1つもなかった。

壊れていたのは全てハーネス — モデルの周りの環境だった。この記事では9個のバグの中身と、その修正方法を全て公開する。

作ったもの

Claude Codeを使って、記事の自動生成パイプラインを構築した。3つの独立したAIセッションが順番に連鎖する仕組みだ。

  1. Observer — トレンド・競合記事・パフォーマンスデータを調査
  2. Strategist — テーマを選び、切り口を決め、アウトラインを作成
  3. Marketer — 記事本文を執筆、品質チェック、公開予約まで実行

各フェーズは独立したClaudeセッション。Observerの出力がStrategistの入力になり、Strategistの出力がMarketerの入力になる。品質チェックに引っかからない限り、人間の介入は不要 — そういう設計だった。

このアーキテクチャ図を紙に描いた時点では、私は天才だと思っていた。

# 目標のアーキテクチャ
observer:
  schedule: "0 7 * * 1"  # 毎週月曜 7:00
strategist:
  after: observer          # Observer完了後に起動
marketer:
  after: strategist        # Strategist完了後に起動

きれいに見える。現実はもっと泥臭かった。

パイプライン設計: Before vs After

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シェルエスケープ、ジョブ管理

9つのバグ: 全てAIの外側

これは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章で解説しています。