1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Anthropicの「ハネス設計」ブログ3本を読み込んで実装パターンを整理してみた

1
Posted at

Anthropicがエンジニアリングブログで公開している「ハネス設計」の記事が3本に増えたので、全部読んで実装パターンを整理してみた。正直、1本目(2024-12)の時点ではピンと来なかった概念が、3本目(2026-03)まで追うことで「なるほど、こう繋がるのか」と腹落ちした部分が多い。特にGenerator-Evaluator分離の考え方と、モデル進化に合わせてハネスを簡素化していく発想は、自分のエージェント開発にもすぐ使えそうだと感じた。


Anthropicの「ハネス設計」ブログ3本を読み込んで実装パターンを整理してみた

Anthropic 公式エンジニアリングブログ3編を読み込んで整理

原文:

参考: Building agents with the Claude Agent SDK

最終検証日: 2026-03-30


目次

  1. ハネスとは何か
  2. 基礎: エージェント アーキテクチャ パターン
  3. なぜハネスが必要なのか — 単一エージェントの限界
  4. コンテキスト管理の課題
  5. ハネス設計 第1世代: 2エージェント直列構造
  6. ハネス設計 第2世代: GAN 着想の3エージェント構造
  7. フロントエンド デザイン ハネス
  8. フルスタック コーディング ハネス
  9. コスト・パフォーマンス比較
  10. モデル進化に応じたハネス簡素化
  11. 実装ガイド: GAN着想ハネスの作り方
  12. ツール・技術スタック
  13. 設計原則と推奨事項
  14. 障害モードと対応戦略
  15. まとめ

1. ハネスとは何か

ハネス(Harness)は、AI エージェントが長時間にわたって複雑なタスクを自律的に実行できるようにするための制御構造だ。馬の鞍が馬の力を制御し方向を定めるように、エージェント ハネスはモデルの能力を制御し方向性を保つ。

ハネスが担当する役割:

役割 説明
オーケストレーション 複数のエージェントの実行順序と相互作用を管理
コンテキスト管理 コンテキスト ウィンドウ限界を超える長期タスクで状態を維持
品質保証 成果物の品質を独立的に評価し、フィードバック ループを形成
障害復旧 エージェントの障害モードを検出し、補正
状態ハンドオフ セッション間で作業状態を渡し、連続性を保証

ハネス vs ワークフロー vs エージェント

Anthropicはこの3つの概念を明確に区別している:

Augmented LLM  →  Workflow  →  Agent  →  Harness
(単一モデル+ツール)   (固定経路)   (自律判断)  (エージェント制御構造)
概念 定義 制御方式
ワークフロー LLMとツールが事前定義されたコード経路で調整されるシステム 開発者が経路を決定
エージェント LLMが自ら処理とツール使用を判断するシステム モデルが経路を決定
ハネス エージェント(群)を包む外部制御構造。複数エージェントの調整、コンテキスト管理、品質評価を担当 構造がエージェントを制御

核心的な洞察:ハネスはエージェント「上」に存在するメタ構造だ。エージェントが「何をするか」を決めるなら、ハネスは「どのエージェントが、いつ、どのコンテキストで動作するか」を決める。個人的にこの区別が一番スッキリした。「エージェントを作ればいい」と思いがちだけど、実際に長時間タスクを回すと、エージェント単体では限界がある。


2. 基礎: エージェント アーキテクチャ パターン

ハネスを設計する前に、Anthropicが定義する5つの基本的なアーキテクチャ パターンを理解する必要がある。ハネスはこれらパターンの組み合わせで構成される。

2.1 プロンプト チェーニング (Prompt Chaining)

タスクを順次ステップに分解し、各LLM呼び出しが前のステップの出力を処理する。

[LLM 1] → Gate → [LLM 2] → Gate → [LLM 3]
  • 用途: 明確に定義されたサブタスクがある場合
  • 利点: 正確性向上のために遅延時間のコストを受け入れる
  • コツ: ステップ間にプログラマティック ゲートを追加し、進捗を検証

2.2 ルーティング (Routing)

入力を分類し、特化した下流プロセスに送信する。

          ┌→ [専門家 A]
[分類器] ─┼→ [専門家 B]
          └→ [専門家 C]
  • 用途: 個別処理が必要な異なるカテゴリがある場合
  • 利点: あるタイプの最適化が他のタイプの性能を低下させない

2.3 並列化 (Parallelization)

複数のLLMプロセスを同時に実行し、結果を集約する。

          ┌→ [LLM A] ─┐
[入力] ───┼→ [LLM B] ─┼→ [集約]
          └→ [LLM C] ─┘

2つのバリエーション:

  • セクショニング(Sectioning): 独立したサブタスクを並列実行 (例: ガードレールとクエリ処理を同時に)
  • 投票(Voting): 同じタスクを複数回実行し、異なる出力を確保 (例: コード脆弱性レビュー)

2.4 オーケストレーター-ワーカー (Orchestrator-Workers)

中央のLLMが動的にタスクを分解し、ワーカーLLMに委譲し、結果を統合する。

              ┌→ [Worker 1]
[Orchestrator]┼→ [Worker 2] → [Orchestrator: 統合]
              └→ [Worker 3]
  • 用途: サブタスクを事前に予測できない複雑なタスク
  • 並列化との違い: サブタスクが入力に応じて動的に決定される
  • : マルチファイル コード修正、多元情報収集

2.5 評価者-最適化者 (Evaluator-Optimizer)

生成LLMが出力を作成し、評価LLMがフィードバックを提供する反復ループ。

[Generator] → [Evaluator] → フィードバック → [Generator] → ...
  • 用途: 明確な評価基準があり、反復改善が測定可能な価値をもたらす場合
  • 成功条件: LLMがフィードバックで実際に改善され、品質評価が信頼できる場合

実装優先順位

Anthropicの推奨される進行順序:

1. 単一LLM + 検索 + コンテキスト例の最適化
   ↓ (不十分な場合のみ)
2. ワークフロー追加 (チェーニング、ルーティング、並列化)
   ↓ (不十分な場合のみ)
3. エージェント導入 (自律的ツール使用)
   ↓ (不十分な場合のみ)
4. マルチエージェント ハネス構築

"成功とは最も洗練されたシステムを作ることではなく、必要に応じた正しいシステムを作ることだ。"


3. なぜハネスが必要なのか — 単一エージェントの限界

単一エージェントで長時間にわたり複雑なタスクを実行すると、構造的に解決するのが難しい問題が生じる。

3.1 自己評価の限界

Claudeに自分が作った成果物を評価させると、品質が落ちても**「よくやった」と褒める傾向**がある。

  • 主観的タスク(デザインなど)で特に顕著
  • ソフトウェア テストのような二項検証(合格/不合格)がない領域で深刻
  • 「このデザインは良いか?」という自己評価は信頼できない

解決策: 独立した評価エージェントを別途用意し、懐疑的にチューニングするのが、生成エージェント自身に自己批判を求めるより、はるかに扱いやすい。これは実際にClaude Codeを使っていても感じる。自分で書いたコードを自分でレビューさせると、だいたい「問題ないです」って返ってくる。

3.2 過度な野心 (Over-ambition)

エージェントがアプリケーション全体を1つのセッションで完成させようとし、コンテキスト途中で実装が途切れる。

3.3 早すぎる完了宣言 (Premature Completion)

後続セッションのエージェント インスタンスが部分的な進捗を完了した作業と勘違いし、「完了」を宣言する。

3.4 テストなしの完了マーク

エージェントがE2E検証なしに機能を「完了」とマークする。ユニットテストやcurlコマンドだけでは実際のユーザー体験を検証できない。


4. コンテキスト管理の課題

長時間実行エージェントの最も根本的な技術的制約はコンテキスト ウィンドウだ。エージェントは離散的なセッションで動作し、新しいセッションは前のメモリなしで始まる。

4.1 2つの重要な問題

問題 説明
一貫性低下 コンテキスト ウィンドウが満杯に近づくと、モデルの一貫性が低下
コンテキスト不安(Context Anxiety) モデルがコンテキスト限界に近いと判断すると、タスクを早期に終了したがる

Sonnet 4.5ではコンテキスト不安が深刻で、圧縮(compaction)だけでは不十分で、コンテキスト リセットが必須だった。

4.2 解決策

コンテキスト リセット (Reset)

ウィンドウを完全に初期化する。

  • 利点: クリーンな状態、コンテキスト不安の解消
  • 欠点: オーケストレーション複雑性増加、トークン オーバーヘッド、遅延
  • 必須条件: 次のエージェントがタスクを引き継げるよう、充分な状態情報をハンドオフ アーティファクトに含める

コンテキスト圧縮 (Compaction)

既存内容を要約して圧縮する。

  • 利点: 連続性維持、状態喪失が少ない
  • 欠点: コンテキスト不安を完全に解消できない

エージェンティック検索 (Agentic Search)

Claude Agent SDKのアプローチ。すべての情報をコンテキストに詰め込む代わりに、エージェントがgreptailなどで必要な情報のみを選択的にロードする。

"フォルダとファイル構造自体がコンテキスト エンジニアリングの一形態となる。"

サブエージェント隔離 (Subagent Isolation)

サブエージェントが隔離されたコンテキスト ウィンドウで動作し:

  • 並列化: 複数エージェントが異なるタスクを同時実行
  • コンテキスト効率: 関連結果のみをオーケストレーターに返す

4.3 状態ハンドオフ メカニズム

セッション間の連続性を保証するアーティファクト:

アーティファクト 役割
claude-progress.txt エージェント作業履歴と決定記録
feature_list.json 200+ 項目の機能リスト (合格/不合格状態)
init.sh 環境設定と開発サーバー起動スクリプト
Git コミット履歴 復旧チェックポイント + 状態理解用記録
スプリント契約ファイル エージェント間の事前合意した完了定義

5. ハネス設計 第1世代: 2エージェント直列構造

出典: Effective harnesses for long-running agents (2025-11)

複数のコンテキスト ウィンドウにわたる長期タスクで一貫した進捗を達成するための最初の体系的アプローチ。

5.1 構造

[Initializer Agent]  →  [Coding Agent]  →  [Coding Agent]  →  ...
     (1回のみ)              (セッション2)      (セッション3)

2つのエージェントは異なるプロンプトを受け取る。最初のコンテキスト ウィンドウには基盤構築のための特化した指示を、その後のセッションには漸進的進行を強調する異なるプロンプトを提供する。

5.2 Initializer エージェント

最初のセッションのみ実行され、3つの必須アーティファクトを生成する:

1) init.sh - 環境設定スクリプト

  • 開発サーバー起動コマンド
  • その後のすべてのCoding Agentはセッション開始時このスクリプトを読み実行

2) claude-progress.txt - 作業履歴ファイル

  • エージェントの作業と決定を記録
  • 後続セッションで現在状態を迅速に把握するために使用

3) feature_list.json - 機能リスト

200個以上の詳細な機能項目をJSON形式で生成:

{
  "category": "functional",
  "description": "新規チャットボタンが新しい会話を作成する",
  "steps": [
    "メインインターフェースに移動",
    "「新規チャット」ボタンをクリック",
    "新しい会話が作成されることを確認",
    "チャット領域がウェルカム状態を表示することを確認",
    "会話がサイドバーに表示されることを確認"
  ],
  "passes": false
}

JSONを使う理由: Markdown形式を使うと、エージェントが機能説明を恣意的に修正・削除する問題が発生した。JSON形式ならpassesフィールドのみ修正するよう強い指示が可能。地味だけどこの知見は実用的。構造化フォーマットで「触っていい場所」を制限するのは、エージェント設計の基本テクニックとして覚えておきたい。

"テストを削除・編集することは容認できない。機能漏れやバグにつながるからだ。"

4) 初期Gitコミット

  • 追加ファイルを文書化する最初のコミット

5.3 Coding エージェント

後続のすべてのセッションで実行され、各セッションの起動手順が定まっている:

1. pwd           → 作業ディレクトリ確認
2. read          → claude-progress.txt を読む
3. read          → feature_list.json を読む
4. git log       → 最近のコミット20個を確認
5. init.sh       → 開発サーバーを起動
6. E2E検証       → 基本機能が動作するか確認
7. 実装開始      → 次の未完了機能を1つ選び実装

行動原則:

  • 一度に1機能のみ実装する
  • 実装後、ブラウザ自動化でE2Eテストを実行
  • テスト合格後のみ、passesフィールドをtrueに変更
  • 説明的なGitコミット メッセージでコミット
  • claude-progress.txtに進捗を記録

5.4 ブラウザ自動化テストの重要性

最初、エージェントはユニットテストとcurlコマンドだけで機能を検証していた。この方法の致命的な限界:

  • ユニットテストはE2E機能を検証できない
  • エージェントがテストが通ったと勘違いして機能を「完了」とマーク
  • ユーザー観点のバグが蓄積

解決策: Puppeteer MCPを提供し、エージェントが「実際のユーザーのように」ブラウザを操作してテストするようにした。

既知の限界: ClaudeはPuppeteer MCPを通じてブラウザ ネイティブのalertモーダルを検出できず、これに依存する機能でバグが発生する可能性がある。


6. ハネス設計 第2世代: GAN 着想の3エージェント構造

出典: Harness design for long-running application development (2026-03)

第1世代の教訓の上に、GAN(Generative Adversarial Network)から着想を得たGenerator-Evaluator分離構造を発展させた設計。

6.1 核心的洞察: 生成と評価の分離

GANでGeneratorとDiscriminatorが互いに競争して品質を高めるように、エージェント ハネスでも:

  • Generator: 最高の成果物を作ろうと努力
  • Evaluator: 懐疑的観点から成果物を評価し、具体的なフィードバック提供

この分離が自己評価より効果的な理由:

  1. 独立した評価者を懐疑的にチューニングするのが構造的により扱いやすい
  2. Generatorが具体的な外部フィードバックを受け取り、改善方向が明確になる
  3. 分離だけで寛容さが即座に消えるわけではないが、全体的な有効性が大幅に向上

(個人的にはこのGenerator-Evaluatorの分離が、単一エージェントの自己評価問題を最も効果的に解決するアプローチだと感じた。)

6.2 3エージェント構造

[Planner] → [Generator] ⇄ [Evaluator]
              ↑                ↓
              └── フィードバック ループ ──┘
エージェント 役割 詳細
Planner 仕様拡張 1~4文のプロンプトを詳細な製品仕様書に拡張。製品コンテキストと高レベル技術設計に焦点; 詳細実装は扱わない。AI機能統合機会も仕様に含める
Generator 機能実装 スプリント単位で機能を実装。ハンドオフ前に自己評価を実行。Gitで版管理。構造化アーティファクトでコンテキスト持続性を維持
Evaluator 品質検証 Playwright MCPで実行中のアプリをユーザーのように探索。UI、API、DBの状態をテストベースで検証。事前定義基準とハード閾値でスコアリング

6.3 エージェント間通信

エージェント間通信はファイルベースだ。あるエージェントがファイルに書き、別のエージェントが同じファイルを読み応答するか、新しいファイルを生成して続ける。

6.4 スプリント契約 (Sprint Contracts)

各機能実装の前に、GeneratorとEvaluatorが**「完了の定義」を事前合意**する:

  • 具体的実装詳細
  • テスト可能な成功基準
  • 「完了」の定義
  • 検証方法

これにより、ユーザー ストーリーとテスト可能な実装の間隙を埋めつつ、過度に制約することなく進める。

6.5 Evaluator 具体的フィードバック例

QAエージェントが実際に生成するフィードバックのレベル:

評価基準 結果
長方形塗りつぶしツールがクリック-ドラッグを許可するか FAIL — タイルがドラッグ終点にのみ配置。fillRectangle関数がmouseUpで発火しない
エンティティ スポーン ポイントを選択・削除できるか FAIL — 削除ハンドラが'selection'と'selectedEntityId'の両方を要求するが、クリック時は後者のみ設定される
APIを通じてアニメーション フレームを並び替えられるか FAIL — ルート順序の問題。FastAPIが'reorder'をframe_id整数と照合し422を返す

このレベルの具体性のおかげで、Generatorは即座に修正できる。


7. フロントエンド デザイン ハネス

第2世代ハネスの最初の適用領域。主観的品質評価が必要なデザイン領域では、Generator-Evaluator分離が特に効果的だ。

7.1 構造

GeneratorとEvaluatorが**5~15回の反復(iteration)**でデザインを改善するループ構造。Claude Agent SDKとPlaywright MCPを使用。

[Generator: デザイン生成] → [スクリーンショット キャプチャ] → [Evaluator: 4基準スコアリング]
      ↑                                                  ↓
      └──────────── スコア + 具体的フィードバック ──────────────┘

7.2 評価基準 (4つ)

基準 説明 重み
デザイン品質(Design Quality) 色、タイポグラフィ、レイアウトが結合して、一貫した雰囲気とアイデンティティを形成するか。部分の合計ではなく1つの全体として一貫性がある
独創性(Originality) テンプレートやAIパターン(例: 紫グラデーション上の白いカード)ではなく、カスタムデザイン決定に根拠があるか。人間デザイナーが意図的創作と認識できるか
完成度(Craft) タイポグラフィ階層、間隔一貫性、色調和、コントラスト比の技術的実行 普通
機能性(Functionality) ユーザーがインターフェースの目的を理解し、主要操作を見つけ、推測なく課題を完了できるか 普通
  • モデルの弱点であるデザイン品質と独創性により高い重みを付与
  • 完成度と機能性は合理的実装で大抵通過するため重みが低い
  • 評価者は少数ショット例詳細スコア分析でキャリブレーション

7.3 生成者の戦略的決定

各評価後、Generatorはスコア傾向に応じて2つのいずれかを選択:

  • スコア上昇傾向 → 現在の美的方向を精緻化(refine)
  • スコア停滞/低下 → 完全に異なる美的方向に転換(pivot)

7.4 プロンプティングが出力に及ぼす影響

評価基準で使用された言語が出力特性に直接影響を与える。

例: 「最高のデザインは博物館レベル(museum quality)」という表現が特定の視覚的収束を誘導した。これはスコアリング基準の文言が単なる評価ツールではなく、生成方向を暗黙裏に操舵する**調舵装置(steering mechanism)**であることを意味する。(かなり興味深いメタレイヤーだと思う。)

7.5 視覚的フィードバック ループ

Claude Agent SDKのアプローチと同様に、エージェントが自分の出力についてのスクリーンショットやレンダリングを受け取り、次を評価する:

  • 要素位置と間隔
  • 色とフォントの正確性
  • コンテンツ階層と強調
  • レスポンシブ動作

8. フルスタック コーディング ハネス

第2世代ハネスの2番目の適用領域。客観的検証(テスト合格/失敗)が可能なコーディング領域での適用。

8.1 Version 1 (Opus 4.5): スプリント基盤構造

[Planner]
    ↓ 製品仕様書
[Generator: Sprint 1] → [Evaluator: QA 1]
    ↓ フィードバック反映
[Generator: Sprint 2] → [Evaluator: QA 2]
    ↓ ...
[Generator: Sprint N] → [Evaluator: QA N]

Planner エージェント:

  • 1~4文のプロンプトを16機能、10スプリントレベルの詳細仕様に拡張
  • スプライト アニメーション システム、ビヘイビア テンプレート、サウンド/音楽統合などを自動で仕様に含める
  • AI基盤スプライト生成機、レベル デザイナーなどAI機能統合機会も識別

Generator エージェント:

  • React + Vite + FastAPI + SQLite(後にPostgreSQL)スタックで実装
  • 各スプリントで1機能を実装
  • ハンドオフ前に自己評価を実行
  • Git で版管理を使用

Evaluator エージェント:

  • Playwright MCPで実行中のアプリをユーザーのように探索
  • UI機能、APIエンドポイント、DB状態をテスト
  • ハード合格/不合格閾値でスコアリング
  • 失敗時に具体的フィードバック提供

プロジェクト例: 2D レトロ ゲーム メイカー

  • レベル エディタ、スプライト エディタ、エンティティ ビヘイビア、テスト可能プレイ モード含む
  • 内蔵Claude統合でゲーム生成
  • ゲーム書き出しと共有リンク

8.2 Version 2 (Opus 4.6): 簡素化構造

[Planner]
    ↓ 製品仕様書
[Generator: 連続実装] → [Evaluator: 最終QA]
    ↓ フィードバック反映
[Generator: 修正] → [Evaluator: QA 2]
    ↓ ...

削除されたもの:

  • スプリント分解構造 (Opus 4.6が長時間一貫した作業を実現可能)
  • コンテキスト リセット (Opus 4.6が自ら一貫性を維持)

変更されたもの:

  • Evaluatorをスプリント毎ではなく単一統合QAパスに変更
  • Evaluator有用性が作業複雑度に応じて変動

プロジェクト例: ブラウザベースDAW (Digital Audio Workstation)

  • Web Audio API基盤フル機能DAW
  • エージェントがツール経由でアプリ機能を直接操作

8.3 Version 2 詳細コスト内訳

エージェント/ステージ 所要時間 コスト
Planner 4.7分 $0.46
Build Round 1 2時間7分 $71.08
QA Round 1 8.8分 $3.24
Build Round 2 1時間2分 $36.89
QA Round 2 6.8分 $3.09
Build Round 3 10.9分 $5.88
QA Round 3 9.6分 $4.06
合計 3時間50分 $124.70

Generatorが2時間以上中断なく一貫して実行された。$124で3時間50分。個人開発で気軽に出せる金額ではないけど、プロダクション品質のアプリを半自動で作れると考えると、費用対効果は悪くないと思う。


9. コスト・パフォーマンス比較

9.1 単一エージェント vs フル ハネス

Version 1 (Opus 4.5)

項目 単一エージェント フル ハネス 倍数
所要時間 20分 6時間 18x
コスト $9 $200 ~22x

単一エージェントの具体的限界

  • 硬直したワークフロー、UI ガイドラインなし
  • 破損したエンティティ-ランタイム接続
  • 入力に反応しないゲーム エンティティ
  • 固定高さパネルで空間ムダ

フル ハネスの具体的改善

  • ビューポート全体のキャンバス活用
  • 合理的なパネル サイズ調整
  • デザイン仕様を追跡する一貫した視覚的アイデンティティ
  • より整理されたツール パレット、より優れたカラー ピッカー、使用可能なズーム制御を備えたリッチ スプライト エディタ
  • 動作するプレイ モード (物理エンジンに粗い部分はあるが)

9.2 Version 1 vs Version 2

項目 V1 (Opus 4.5) V2 (Opus 4.6) 変化
所要時間 6時間 3時間50分 -36%
コスト $200 $124.70 -38%
スプリント構造 必須 不要 削除
コンテキスト リセット 必須 不要 削除
最大連続実行 限定的 2時間+ 大幅増加

10. モデル進化に応じたハネス簡素化

ハネス設計で最も重要な原則の1つ: モデルが進化すれば、ハネスも一緒に進化する必要がある。

10.1 ハネス要素 = モデル限界への仮定

ハネスのすべての要素は「モデルが単独でできない」ことについての仮定を内包している:

要素 内包された仮定 Opus 4.6での状態
スプリント分解 モデルが長時間一貫性を維持できない 無効化 — 2時間+ 連続作業可能
コンテキスト リセット モデルがコンテキスト不安を経験する 無効化 — 自ら一貫性を維持
Evaluator モデルが自分の仕事を客観的に評価できない 部分有効 — タスク種別により異なる
Planner モデルが短いプロンプトから充分な仕様を生成できない 依然有効

10.2 Evaluatorの条件付き価値

"Evaluatorは固定的なyes/noの決定ではない。現在のモデルが単独で確実に処理できないタスク境界にあるとき、コスト効率が出現する。"

  • モデルが既に得意な単純タスク → Evaluatorコストが正当化されない
  • モデル能力の境界にある複雑タスク → Evaluatorが大きな価値を提供
  • コスト-便益計算がモデル能力向上に応じて継続的に変化

10.3 Opus 4.6の具体的改善

改善領域 説明
思慮深い計画 より体系的なタスク分解
長時間エージェンティック タスク継続 コンテキスト不安なく長時間一貫した作業
大規模コードベース安定性 大規模プロジェクトでも安定動作
コード レビュー/デバッグ 向上した分析能力
長期コンテキスト検索 長いコンテキストの情報検索精度向上

10.4 簡素化原則

"可能な限り単純な解決策を見つけ、必要な時だけ複雑性を増加させよ。"

定期的に実施すべき検証:

  1. 各ハネス要素がまだ必要か、ストレス テストを実行
  2. 新モデル リリースのたび、最も単純なバージョンから再開始
  3. 複雑性を追加する前に、単純版が充分か確認
  4. 以前必須だった要素が今は不要なオーバーヘッドではないか検証

11. 実装ガイド: GAN着想ハネスの作り方

Anthropicのブログが説明する「何を」は理解した。このセクションではClaude Agent SDKで**「どう」作るか**を扱う。

11.1 全体アーキテクチャ

harness.py (オーケストレーター スクリプト)
    │
    ├─ ステージ1: Planner エージェント実行
    │         └→ product_spec.md 生成
    │
    ├─ ステージ2: Generator エージェント実行 (セッション保持)
    │         └→ コード実装 + Git コミット
    │
    ├─ ステージ3: Evaluator エージェント実行
    │         └→ Playwrightで E2E テスト + 評価レポート生成
    │
    └─ ステージ4: 評価結果に応じて ステージ2~3 を反復または終了

核心的制約: Claude Agent SDKのサブエージェントは自ら別のサブエージェントを生成できない。したがってオーケストレーションは最上位スクリプトで実行する必要がある。

11.2 インストール・準備

pip install claude-agent-sdk
export ANTHROPIC_API_KEY=your-api-key

# Playwright MCP (Evaluator用ブラウザ自動化)
npm install -g @playwright/mcp@latest

11.3 実装: オーケストレーター

ハネス全体を制御するメイン スクリプト。

# harness.py
import asyncio
import json
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions

WORK_DIR = Path("./project")
MAX_ROUNDS = 3
PASS_THRESHOLD = 0.8  # 80%以上なら合格


async def run_planner(user_prompt: str) -> str:
    """ステージ1: ユーザーの短いプロンプトを詳細な製品仕様書に拡張する。"""
    spec_path = WORK_DIR / "product_spec.md"

    async for message in query(
        prompt=f"""You are a product planner. Convert this brief into a detailed product spec.

Brief: {user_prompt}

Requirements:
- Write a comprehensive product specification to {spec_path}
- Include feature list, technical architecture, and UI/UX guidelines
- Focus on deliverables and high-level design, NOT granular implementation
- Identify opportunities to integrate AI features
- Output the spec as a markdown file""",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Write", "Glob"],
            model="sonnet",
        ),
    ):
        if hasattr(message, "result"):
            return message.result

    return spec_path.read_text()


async def run_generator(spec: str, feedback: str = "") -> str:
    """ステージ2: 仕様書(+ 前回フィードバック)に基づいてコードを実装する。"""
    feedback_section = ""
    if feedback:
        feedback_section = f"""

CRITICAL - Previous QA feedback that MUST be addressed:
{feedback}

Fix every issue listed above before adding new features."""

    async for message in query(
        prompt=f"""You are a senior full-stack developer. Build the application
described in product_spec.md.{feedback_section}

Rules:
- Read product_spec.md first
- Use React + Vite for frontend, FastAPI for backend
- Implement one feature at a time, commit after each
- Run the dev server and verify it works before moving on
- Write descriptive git commit messages""",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
            model="opus",
            cwd=str(WORK_DIR),
        ),
    ):
        if hasattr(message, "result"):
            return message.result

    return ""


async def run_evaluator(spec: str) -> dict:
    """ステージ3: Playwrightでアプリを E2E テストし、評価レポートを生成する。"""
    report_path = WORK_DIR / "qa_report.json"

    async for message in query(
        prompt=f"""You are a skeptical QA engineer. Your job is to FIND PROBLEMS,
not to praise.

Instructions:
1. Read product_spec.md to understand what was promised
2. Start the dev server
3. Use Playwright to test EVERY feature as a real user would
4. For each feature, report PASS or FAIL with specific evidence
5. Write your report to {report_path} in this JSON format:

{{
  "overall_score": 0.0-1.0,
  "features_tested": 10,
  "features_passed": 7,
  "failures": [
    {{
      "feature": "feature name",
      "expected": "what should happen",
      "actual": "what actually happened",
      "severity": "critical|high|medium|low"
    }}
  ],
  "summary": "one paragraph overall assessment"
}}

Be HARSH. If something is broken, say so. Do not give benefit of the doubt.""",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Bash", "Glob", "Grep"],
            model="sonnet",
            cwd=str(WORK_DIR),
            mcp_servers={
                "playwright": {
                    "command": "npx",
                    "args": ["@playwright/mcp@latest"],
                }
            },
        ),
    ):
        pass

    if report_path.exists():
        return json.loads(report_path.read_text())
    return {"overall_score": 0, "failures": [], "summary": "Report not generated"}


async def main():
    user_prompt = "Build a kanban board with drag-and-drop, user auth, and real-time sync"

    # ステージ1: 計画
    print("=== Phase 1: Planning ===")
    spec = await run_planner(user_prompt)
    print(f"Spec generated: {len(spec)} chars")

    # ステージ2~3: Build-QA ループ
    feedback = ""
    for round_num in range(1, MAX_ROUNDS + 1):
        print(f"\n=== Round {round_num}: Build ===")
        await run_generator(spec, feedback)

        print(f"\n=== Round {round_num}: QA ===")
        report = await run_evaluator(spec)

        score = report.get("overall_score", 0)
        print(f"Score: {score:.0%}")

        if score >= PASS_THRESHOLD:
            print("PASSED - Quality threshold met")
            break

        # 失敗項目をフィードバックに変換
        failures = report.get("failures", [])
        feedback = "\n".join(
            f"- [{f['severity'].upper()}] {f['feature']}: "
            f"Expected: {f['expected']} / Actual: {f['actual']}"
            for f in failures
        )
        print(f"FAILED - {len(failures)} issues found, iterating...")

    print("\n=== Harness Complete ===")


asyncio.run(main())

11.4 核心的設計決定の解説

A. なぜサブエージェントではなく別のquery()呼び出しか

# 方法1: サブエージェント (使わない理由)
# - サブエージェントはサブエージェントを生成できない
# - 結果が要約されて返されるため、細かい制御が困難
agents={"generator": AgentDefinition(...)}

# 方法2: 別の query() 呼び出し (採用)
# - 各エージェントの結果をファイルで受け取り、次エージェントに渡す
# - オーケストレーターがループ制御、終了条件判定
result = await run_generator(spec, feedback)
report = await run_evaluator(spec)

Anthropicのブログでも、エージェント間通信はファイルベースだ。あるエージェントがファイルに書き、次がそれを読む。SDKのサブエージェント機能は独立した下流タスク(コード レビュー、検索など)に適し、GANループのような順序付き反復構造には別のquery()呼び出しが適切だ。

B. Evaluatorを懐疑的にチューニングする方法

プロンプト レベルでのチューニング ポイント:

# 悪い例: 寛容な評価者
prompt = "Review the application and provide feedback."

# 良い例: 懐疑的評価者
prompt = """You are a skeptical QA engineer. Your job is to FIND PROBLEMS,
not to praise.
...
Be HARSH. If something is broken, say so. Do not give benefit of the doubt."""

Anthropicの経験によれば、基本的Claudeはqa で性能が低い:

  • 正当な問題を見つけても「非重要」で無視
  • 表面的テストのみ実行、エッジ ケース漏れ

改善サイクル: ハネスを実行し → Evaluatorログを読み → 人間判断との差分を特定し → プロンプトを更新する。このプロセスを複数回繰り返す必要がある。

C. ファイルベース状態渡しパターン

project/
├── product_spec.md          ← Planner が生成、Generator/Evaluator が読む
├── qa_report.json           ← Evaluator が生成、オーケストレーターが読む
├── src/                     ← Generator が生成/修正
└── .git/                    ← Generator がコミット (復旧チェックポイント)

エージェント間の「対話」はコードでしない。ファイルが契約書であり通信チャネルだ。

D. エージェント別モデル選択

エージェント 推奨モデル 理由
Planner Sonnet 仕様拡張は比較的単純タスク。コスト効率的
Generator Opus 長時間一貫したコード生成に最高性能が必須
Evaluator Sonnet QAはコード生成より単純。プロンプト チューニングが重要

実際のAnthropicのV2ハネスではPlannerコストはわずか$0.46。Generatorが全体コストの90%を占める。

E. 終了条件の設計

# 単純なスコア閾値
if score >= PASS_THRESHOLD:
    break

# より洗練された終了条件 (推奨)
if score >= PASS_THRESHOLD:
    break
if round_num >= 2 and score <= previous_score:
    # スコアが停滞/低下なら方向転換が必要
    feedback += "\nCRITICAL: Score is not improving. Try a fundamentally different approach."

フロントエンド デザイン ハネスでGeneratorはスコア傾向に応じて:

  • 上昇 → 現在の方向を精緻化(refine)
  • 停滞/低下 → 完全に異なる方向に転換(pivot)

11.5 フロントエンド デザイン ハネス変形

フルスタック コーディング ハネスと構造は同じだが、Evaluatorの評価方式が異なる。

async def run_design_evaluator(design_brief: str) -> dict:
    """デザイン品質を4基準でスコアリングする。"""
    async for message in query(
        prompt=f"""You are a design critic with museum-level standards.
Evaluate the UI at http://localhost:5173 against these criteria:

1. DESIGN QUALITY (weight: 30%) - Coherent mood and identity
   - Do colors, typography, layout combine into a distinct whole?
   - Or does it look like assembled parts?

2. ORIGINALITY (weight: 30%) - Custom creative decisions
   - Would a human designer recognize deliberate choices?
   - Or does it use AI-typical patterns (purple gradients, white cards)?

3. CRAFT (weight: 20%) - Technical execution
   - Typography hierarchy, spacing consistency, color harmony

4. FUNCTIONALITY (weight: 20%) - Usability
   - Can users complete tasks without guessing?

Take a screenshot, score each criterion 1-10, and write to eval_report.json:
{{
  "design_quality": {{"score": N, "reasoning": "..."}},
  "originality": {{"score": N, "reasoning": "..."}},
  "craft": {{"score": N, "reasoning": "..."}},
  "functionality": {{"score": N, "reasoning": "..."}},
  "weighted_total": N,
  "direction": "refine" or "pivot",
  "specific_feedback": ["list of concrete improvements"]
}}""",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Bash"],
            model="sonnet",
            mcp_servers={
                "playwright": {
                    "command": "npx",
                    "args": ["@playwright/mcp@latest"],
                }
            },
        ),
    ):
        pass

    return json.loads((WORK_DIR / "eval_report.json").read_text())

注意: 評価基準の文言が生成方向を暗黙裏に操舵する。「museum quality」という表現が特定の視覚的収束を誘導したように、基準文言自体がデザインの調舵装置になる。

11.6 第1世代ハネス変形 (マルチセッション用)

コンテキスト ウィンドウを超える長期タスクにはセッションベースアプローチが必須。

async def run_multi_session_harness(user_prompt: str, max_sessions: int = 10):
    """第1世代パターン: Initializer + 反復Coding Agent"""

    # 1回: Initializer
    async for message in query(
        prompt=f"""You are an initializer agent. Set up the project foundation:

1. Create init.sh - dev server startup script
2. Create claude-progress.txt - work history log
3. Create feature_list.json with 50+ features, all "passes": false
4. Make initial git commit

Project brief: {user_prompt}""",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Write", "Bash"],
            cwd=str(WORK_DIR),
        ),
    ):
        pass

    # N回: Coding Agent
    for session in range(max_sessions):
        features = json.loads((WORK_DIR / "feature_list.json").read_text())
        remaining = sum(1 for f in features if not f["passes"])

        if remaining == 0:
            print("All features complete!")
            break

        print(f"Session {session + 1}: {remaining} features remaining")

        async for message in query(
            prompt="""You are a coding agent. Follow this startup procedure:

1. Read claude-progress.txt for context
2. Read feature_list.json to find next incomplete feature
3. Run init.sh to start dev server
4. Implement ONE feature only
5. Test it with browser automation
6. If tests pass, set "passes": true in feature_list.json
7. Git commit with descriptive message
8. Update claude-progress.txt""",
            options=ClaudeAgentOptions(
                allowed_tools=["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
                cwd=str(WORK_DIR),
                mcp_servers={
                    "playwright": {
                        "command": "npx",
                        "args": ["@playwright/mcp@latest"],
                    }
                },
            ),
        ):
            pass

11.7 実装チェックリスト

ハネスを初めて作るときに確認する項目:

ステージ 確認事項
Planner 仕様が充分詳細か? Generatorが曖昧さなく実装できるか?
Generator Gitコミットしているか? (復旧チェックポイント)
Evaluator 実際にブラウザを開いてテストしているか? curlやユニット テストだけではないか?
Evaluator チューニング ログを読んで人間判断との差分を分析したか? プロンプトを反復修正したか?
終了条件 スコア閾値が合理的か? 無限ループ防止装置(MAX_ROUNDS)があるか?
コスト監視 各ラウンドのトークン消費を追跡しているか?
状態ファイル エージェント間で渡されるファイル形式が明確かつ解析可能か? (JSON推奨)

12. ツール・技術スタック

12.1 ハネス インフラ

ツール 用途 備考
Claude Agent SDK エージェント オーケストレーション エージェント生成、ツール提供、フィードバック ループ構成
Playwright MCP インタラクティブ ページテストと評価 E2E検証、スクリーンショット キャプチャ、ビューポート テスト
Puppeteer MCP ブラウザ自動化 (第1世代) Playwright以前に使用された代替案
MCP サーバー 外部統合の標準化 Slack、GitHub、Asana等
Git 版管理 + 復旧チェックポイント セッション間状態理解にも活用

12.2 アプリケーション スタック (プロジェクトにより可変)

ツール 用途
React + Vite フロントエンド
FastAPI バックエンド API
SQLite / PostgreSQL データベース
Web Audio API オーディオ処理 (DAWプロジェクト)

12.3 検証フレームワーク

Anthropicが推奨する3つの検証レベル:

レベル 方法 信頼度
ルールベース フィードバック コード リンティング、TypeScript検証 最高 — 明示的ルール定義
視覚的フィードバック ループ スクリーンショット/レンダリングでUI評価 高 — 要素位置、色、階層確認
LLM-as-Judge 補助LLMがファジー基準(トーン、スタイル)で評価 普通 — 遅延時間比でエッジ性能

13. 設計原則と推奨事項

13.1 Anthropicの3つの核心的実装原則

原則 説明
単純性 不要な複雑性なくクリーンなエージェント設計を維持
透明性 エージェントの計画ステップを明示的に表示し、解釈可能性確保
ACI設計 Agent-Computer InterfaceにHCI(Human-Computer Interface)レベルの設計努力を投資

13.2 ツール設計原則

  • モデルが「追い詰められる前に充分なトークンで思考できるよう」フォーマットを設計
  • インターネットで自然発生するテキストに近いフォーマット維持
  • 数千行のコード追跡や文字列エスケープのような「フォーマット オーバーヘッド」を回避
  • ポカヨケ(Poka-yoke)原則: ツール引数を誤用しにくく再設計 (例: 相対パスではなく絶対パス要求)
  • ドキュメントに使用例、エッジ ケース、ツール間の明確な境界を含める

13.3 ハネス設計推奨事項

1. ハネス仮定を定期的に検証せよ

すべての要素が内包する仮定を定期的にストレス テストする。モデルが進化するたび、この仮定が無効化される可能性がある。

2. 評価基準を反復的に調整せよ

QAエージェント チューニングには複数回のプロンプト改善が必要:

  • 基本的Claudeはqa で性能が低い
  • 正当な問題を見つけても「非重要」で無視
  • 表面的テストのみ、エッジ ケース漏れ
  • 改善サイクル: ログ読み → 判断差分識別 → プロンプト更新

3. 明確な契約を確立せよ

エージェント間に事前合意した仕様がないと、実装が漂流しながらも、過度に制約されない。

4. 複雑タスクを分解せよ

機能単位に分解すれば、数時間かかるセッションでも一貫性を維持できる。

5. 新モデルが出たらハネスを再検討せよ

モデル性能が向上すれば、単純化機会が生じる。既存ハネスの複雑性が依然正当化されるか確認する。

6. 実験せよ

実問題でモデル トレースを読み、性能をチューニングする。理論的アプローチより実証的アプローチが効果的だ。


14. 障害モードと対応戦略

14.1 総合障害モード表

問題 症状 第1世代解決策 第2世代解決策
早期完了宣言 部分完成を「完了」と宣言 200+ 項目JSON機能リストで進行強制 スプリント契約で完了定義を事前合意
バグ放置/未文書化 進捗が記録されない Gitコミット + claude-progress.txt Git + エージェント間ファイルベース通信
テストなし完了 E2E検証なく合格表示 Puppeteer MCP ブラウザ自動化 Playwright MCP + ハード合格/不合格閾値
環境混乱 アプリ実行方法不明で時間浪費 init.sh スクリプト Plannerが技術スタック仕様に含める
自己評価寛容 自分の成果物を過大評価 (解決不可) 独立したEvaluatorエージェント分離
コンテキスト不安 タスク早期終了 (解決不可) コンテキスト リセット / モデルアップグレード
一貫性低下 長時間作業で品質低下 セッション別独立実行 スプリント分解 / モデルアップグレード
仕様エラー伝播 初期仕様エラーが実装に伝播 (解決不可) Planner エージェント分離
浅いqa 表面的テスト、エッジ ケース漏れ 詳細機能ステップ定義 qa プロンプト反復チューニング

14.2 Evaluatorの既知限界

チューニング後も残る限界:

  • 小さなレイアウト問題の見落とし
  • 直感的でないインタラクション処理
  • 深くネストされた機能の未検出バグ
  • オーディオ関連評価不可 (Claudeは音を聞けない)

15. まとめ

15.1 ハネス設計の進化

2024-12: 基本的エージェント パターン確立 (Building Effective Agents)
          └→ 5パターン: チェーニング、ルーティング、並列化、オーケストレーター、評価者
2025-11: 第1世代ハネス (Effective Harnesses)
          └→ 2エージェント直列構造、状態ハンドオフ アーティファクト、JSON機能リスト
2026-03: 第2世代ハネス (Harness Design)
          └→ GAN着想3エージェント構造、スプリント契約、モデル進化対応
          └→ Opus 4.6で単純化: スプリント/リセット削除、条件付きEvaluator

15.2 核心的教訓

  1. 生成と評価を分離せよ: 自己評価より独立した評価者が構造的に優越する
  2. 状態ハンドオフを設計せよ: セッション間連続性は自動では発生しない。明示的アーティファクトが必須
  3. ブラウザ自動化で検証せよ: ユニットテストだけではE2E機能を保証できない
  4. モデルとともに進化せよ: ハネスのすべての要素はモデル限界への仮定だ。モデルが進化すれば仮定を再検証せよ
  5. 単純性を追求せよ: 可能な限り単純な解決策を見つけ、必要な時だけ複雑性を増加させよ

15.3 将来展望

"モデルが進化しても、興味深いハネス組み合わせの空間は縮小しない。移動するだけだ。AI エンジニアの興味深い仕事は、次の新しい組み合わせを継続的に見つけ出すことだ。"

ハネス設計とはモデルの現在の限界を補う構造を作ることだ。モデルが改善されるたび、ハネスも一緒に進化する必要がある。以前必須だった複雑な構造が不要になる一方、新しいモデル能力が以前不可能だった新しいハネス設計を可能にする。

3本のブログを通して読んで一番感じたのは、「ハネス設計 = 今のモデルの弱点リスト」だということ。Opus 4.5で必要だったスプリント分解が、Opus 4.6で不要になった事実が象徴的。次のモデルが出たら、今のハネスのどの部分が不要になるか——それを考えながら設計するのが、AI エンジニアの仕事になっていくんだと思う。

未探索領域:

  • 複数専門エージェント: テスト、qa、コード整理など特化したエージェントが汎用エージェントを上回るか
  • 領域拡張: Web開発を超える科学研究、金融モデリングなど長期タスクへの応用
  • アーキテクチャ最適化: 拡張ワークフローでの最適なコンテキスト管理

参考資料

資料 URL
Building Effective Agents https://www.anthropic.com/research/building-effective-agents
Effective harnesses for long-running agents https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents
Harness design for long-running application development https://www.anthropic.com/engineering/harness-design-long-running-apps
Building agents with the Claude Agent SDK https://claude.com/blog/building-agents-with-the-claude-agent-sdk
Anthropic Engineering Blog https://www.anthropic.com/engineering
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?