ハーネスエンジニアリングとは
ハーネスエンジニアリングとは、AIエージェントシステムを本番環境で安定運用するために必要なハーネス(制御・評価・監視の足場)を系統的に設計・実装・維持するソフトウェアエンジニアリングの手法を指す。単一のエージェントハーネスを構築することにとどまらず、複数エージェント・複数モデルにまたがる評価基盤全体をソフトウェアプロジェクトとして管理する領域だ。
AnthropicやOpenAIがエージェントフレームワークの整備を進めるにつれ、「どのようにAIシステムの品質を継続的に保証するか」という問いが実務的な課題として浮上してきた。その答えとして体系化されつつあるのがハーネスエンジニアリングだ。評価セット(Evals)の設計・実行・集計・回帰検知をCIパイプラインに組み込む一連の実践がその中核をなす。
なぜ注目されているのか
従来のソフトウェアテストでは、入力と期待出力の対(ユニットテスト)で品質を保証できた。しかしLLMを使ったエージェントでは「正しい出力」が確率的であり、モデルのバージョンアップや外部ツールの仕様変更で挙動が静かに劣化する「サイレントデグレーデーション」が起きやすい。ハーネスエンジニアリングはこの問題を継続的な評価ループで検出・対処することを主眼とする。
OpenAIのEvals、AnthropicのAgents SDK、LangChainなどのフレームワークはいずれもハーネスエンジニアリングを支援するツールとして位置づけられる。特にAnthropicが推奨する「評価主導の開発(Eval-Driven Development)」の考え方は、エージェントを本番投入する前にEvalで品質の閾値を設けるというプロセスを必須にする哲学として広まりつつある。CI/CDに組み込まれたEvalがパスしなければリリースできない仕組みが、AI開発の標準的なプラクティスになりつつある。
エージェントハーネスとの違い
エージェントハーネスが「単一エージェントの実行をラップするフレームワーク層(技術)」であるのに対し、ハーネスエンジニアリングは「そのようなハーネスをいかに設計・管理・進化させるか(手法・実践)」だ。ちょうど「テストコードを書くこと」と「テスト戦略を設計すること」の関係に近い。メタハーネスが複数ハーネスの技術的統合を扱うのに対し、ハーネスエンジニアリングはチームの開発プロセスとCI/CDの観点を含む、より広い概念として使われる。
ドラゴンボールで例えると
エージェントハーネスをスカウター(計測道具)、メタハーネスを全スカウターを束ねる界王神の司令室と考えたとき、ハーネスエンジニアリングは「そのスカウターをどう設計し・何を計測し・どの数値で合格とするか」を亀仙人・界王様が決める役割にあたる。
- 評価セット(Evals) = スカウターで計測する試練メニュー(かめはめ波の精度・瞬発力・体力など)。本番前に「これだけのスコアが出るか」を測る指標の一覧
- スコアリングと閾値 = 「スカウター戦闘力5万未満は天下一武道会に出場させない」という合格基準。AIなら「正解率80%未満はリリース禁止」に相当する
- サイレントデグレーデーション = スカウターなしで訓練を続けて「知らないうちに界王拳の後遺症でこっそり弱くなっている」状態。定期計測がないと劣化に気づけない
- CI/CDパイプライン = 毎回の修行後に必ずスカウターで計測し、基準未達なら「まだ出陣させない」と自動で止める仕組み
「スカウター(ハーネス)を作ること」がエージェントハーネス、「複数のスカウターを束ねること」がメタハーネス、そして「スカウターを何種類用意し・どう使い・何の数値で合否を決めるか」を設計することがハーネスエンジニアリング——道具・束ねる仕組み・設計の哲学、この3層が揃って初めてAIエージェントの品質保証が成り立つ。
実装してみる
- 01 評価セットを
evals/ディレクトリにJSONL形式で管理し、入力・期待出力・スコアリング関数を定義する - 02 ハーネスランナーを実装し、各ケースをエージェントに流してスコアを集計する
- 03 GitHub ActionsのCIジョブに組み込み、スコアが閾値を下回ったらfailにする
import json
import anthropic
client = anthropic.Anthropic()
def run_eval(eval_path: str, threshold: float = 0.8) -> bool:
cases = [json.loads(l) for l in open(eval_path)]
passed = 0
for case in cases:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": case["input"]}],
)
output = response.content[0].text
score = case["scorer"](output, case["expected"])
if score >= threshold:
passed += 1
rate = passed / len(cases)
print(f"Pass rate: {rate:.1%} ({passed}/{len(cases)})")
return rate >= threshold
if not run_eval("evals/summarize.jsonl"):
raise SystemExit("Eval failed — below threshold")
{"input": "以下を要約して: ...", "expected": "重要なポイントは3つ", "scorer": "contains_all_keywords"}
{"input": "次の文章の主語は?: ...", "expected": "Claude", "scorer": "exact_match"}
以下のタスクを実行し、各ステップで判断の根拠を説明してください。出力は必ず指定されたJSON形式で返してください。 タスク: [タスクを記述] 出力形式: {"result": "...", "confidence": 0.0~1.0}