第3章 概念の整理 ── プロンプト・コンテキスト・ハーネスの三層モデル
混乱の原因
「ハーネスエンジニアリング」という言葉が出回り始めた初期、「コンテキストエンジニアリングと何が違うのか」「プロンプトエンジニアリングの言い換えに過ぎないのでは」という議論が起きた。
この混乱は理解できる。三つの概念はいずれも「AIエージェントをうまく動かすこと」を目的にしており、境界が曖昧に見える。しかし三つは入れ子構造の別レイヤーであり、それぞれが扱う問題の性質が根本的に異なる。
graph TD
subgraph H["ハーネスエンジニアリング"]
subgraph C["コンテキストエンジニアリング"]
subgraph P["プロンプトエンジニアリング"]
P1["何を聞くか<br/>言葉の選び方・構造化"]
end
C1["何を送るか<br/>情報の選択・整形・注入"]
end
H1["システム全体をどう動かすか<br/>防止・測定・修正・環境設計"]
end
三者は「置き換え」ではなく「拡張」の関係だ。ハーネスエンジニアリングはコンテキストエンジニアリングを含み、コンテキストエンジニアリングはプロンプトエンジニアリングを含む。
Layer 1:プロンプトエンジニアリング
扱う問い:「何を聞くか」 単位:単発のAPIコール
プロンプトエンジニアリングは最初の層だ。モデルに何をどんな言葉で伝えるか、という最適化を行う。
有効なテクニックは多数あり、今も価値を持つ。
- Chain of Thought(思考の連鎖):「ステップバイステップで考えてください」と加えるだけで、推論精度が向上する
- Few-shot プロンプティング:出力形式のサンプルをいくつか見せることで、期待通りの構造で応答させる
- ロールプレイ:「あなたはシニアエンジニアです」と設定することで、応答の質と視点が変わる
- 否定指示の回避:「〇〇しないで」より「〇〇してください」の方が指示が通りやすい
プロンプトエンジニアリングの限界:これらのテクニックは単発の品質を上げるが、エージェントが複数ステップを自律実行する場面では機能しない。10ステップ目でプロンプトを書き直すことはできないからだ。
Layer 2:コンテキストエンジニアリング
扱う問い:「何を送るか」 単位:マルチターンセッション・ツール呼び出しのチェーン
コンテキストエンジニアリングは、プロンプトの「言葉」から「情報」へと視点を広げる。モデルが確信を持って答えるために、何の情報をどの形で渡すかを設計する。
主な関心事は以下だ。
RAG(検索拡張生成):コードベース全体、ドキュメント、過去の会話履歴から関連情報を検索し、コンテキストに注入する。エージェントが「知らない」ことによるハルシネーションを減らす。
メモリ管理:長いセッションでコンテキストウィンドウが圧迫されると、初期の指示が失われる。重要情報を保持しつつ不要な中間出力を刈り込む戦略が必要になる。
ツール出力の整形:git diff や npm test の出力をそのままコンテキストに入れると、数千行がウィンドウを圧迫する。エラーのみを抽出する、サマリーを作る、といった整形が必要だ。
プログレッシブ開示:全ドキュメントを一度に渡すのではなく、タスクの段階に応じて必要な情報だけを順番に開示する。
コンテキストエンジニアリングの限界:これは「エージェントが正しい知識を持つ」ことへの対処だ。しかし知識があっても起きる問題がある。アーキテクチャ制約の無視、並列実行時の競合、慢性的なコード品質劣化——これらはコンテキストの問題ではない。
Layer 3:ハーネスエンジニアリング
扱う問い:「システム全体をどう動かすか」 単位:複数コンテキストウィンドウにまたがる長期実行・マルチエージェント
ハーネスエンジニアリングが扱う問題を一言で言うと、「知識があっても起きる慢性的劣化」 だ。
コンテキストエンジニアリングで正しい知識を渡しても、エージェントはアーキテクチャルールを無視することがある。ドキュメントに「レイヤー間の依存方向を守ること」と書いても、エージェントは利便性のためにショートカットを選ぶ。一度の失敗はコンテキストの問題かもしれない。しかし数週間にわたって繰り返す場合、それはハーネスの問題だ。
この区別を明確に示したのは Substack のポスト “Harness Engineering Is Not Context Engineering”(M. Trajan著)だ。
「間違った出力が一度?おそらくコンテキストの問題。数週間にわたる緩やかな劣化?それはハーネスの問題だ」
ハーネスエンジニアリングの構成要素は以下だ。
ハーネスの四構成要素
1. アーキテクチャ制約の機械的強制
「こう設計すること」をドキュメントに書くだけでは不十分だ。機械的に強制する仕組みが必要だ。
graph LR
A[エージェントがコードを生成] --> B[カスタムリンターが実行]
B --> C{制約違反あり?}
C -- Yes --> D[違反内容と修正方法を出力]
D --> A
C -- No --> E[次のステップへ]
カスタムリンターのポイントは「違反の検出」だけでなく「修正方法の提示」も出力することだ。エージェントはエラーメッセージを読んで修正を試みる。修正方法が書いてあれば、エージェントは自律的に対処できる。
2. フィードバックループの設計
エージェントが自分の誤りに気づける仕組みが必要だ。人間が「それは違う」と言い続けるのはスケールしない。
| フィードバック種別 | 実装例 | 対処できる問題 |
|---|---|---|
| 型チェック | tsc --noEmit のフック | 型エラーの自動検知 |
| リンター | ESLint / カスタムルール | コーディング規約違反 |
| 構造テスト | ArchUnit / 依存チェック | アーキテクチャ制約違反 |
| E2Eテスト | Puppeteer / Playwright | 動作レベルの破壊 |
| ブラウザ自動化 | スクリーンショット撮影 | UI確認のすり抜け |
重要な設計原則:フィードバックはエラーのみを出力する。成功したテストの出力4,000行がコンテキストを圧迫すると、エージェントはタスクを見失う。
3. 状態管理とセッション間継続性
AIエージェントは記憶を持たない。セッションが終わると、それまで学んだことはすべて消える。
ハーネスがこの問題を解決する方法は二つだ。
外部化された状態:進捗ファイル(claude-progress.txt)、機能チェックリスト(feature-list.json)、セッション引き継ぎ書(summary.md)。エージェントはセッション開始時にこれらを読み込み、前回どこまで進んだかを把握する。
Gitによる原状復帰点:各機能の完了時に意味のあるコミットメッセージでコミットする規律。セッションをまたいでも「どの状態に戻れば正しいか」が明確になる。
4. AGENTS.md:失敗の累積知
AGENTS.md は「指示書」ではなく「失敗の記録」だ。エージェントが犯した具体的な失敗が、その防止策として記録される。
AGENTS.md の理想的な1エントリ
# API使用について
- ❌ `fetch()` を直接使わないこと
- ✅ 必ず `src/lib/api-client.ts` の `apiClient` を使うこと
- 理由: 認証ヘッダーとエラーハンドリングがラップされているため
(この行は、エージェントが2回 fetch() を直接呼び出した結果、
認証エラーが本番で発生したことを受けて追加された)
三層の相互関係
三つの層は互いに補完する。どれかが欠けていると全体が成立しない。
flowchart TD
P["プロンプトエンジニアリング<br/>・タスクの明確な記述<br/>・出力形式の指定<br/>・計画フェーズの分離"]
C["コンテキストエンジニアリング<br/>・AGENTS.mdの内容<br/>・関連ドキュメントの注入<br/>・進捗ファイルの提供<br/>・コンテキスト圧縮"]
H["ハーネスエンジニアリング<br/>・アーキテクチャ制約の強制<br/>・フィードバックループ<br/>・状態管理の設計<br/>・失敗パターンの制度化"]
P --> C
C --> H
H --> P
style P fill:#d4edda,stroke:#28a745,color:#155724
style C fill:#cce5ff,stroke:#004085,color:#004085
style H fill:#f8d7da,stroke:#721c24,color:#721c24
プロンプトは入り口だ。タスクを明確に定義し、計画を承認してから実行に移すという規律はプロンプトの設計から始まる。
コンテキストは知識の層だ。エージェントが誤りを犯さないための情報を適切に渡す。AGENTS.mdの内容も、技術的にはコンテキストとして渡される。
ハーネスは構造の層だ。知識があっても起きる問題に対して、機械的な防壁とフィードバックを設置する。
次章では、この概念がコミュニティの議論を通じてどのように形が定まってきたかを追う。