今回はAIエージェントを評価・ベンチマークするためのフレームワークであるHarborを紹介します。
- 公式サイト: Harbor
- GitHub: harbor-framework/harbor: Harbor is a framework for running agent evaluations and creating and using RL environments.
※ 注意点として、同じAI関連のプロジェクトにav/harborという同名のツールがありますが、こちらはOllamaやOpen WebUIなどのローカルLLMスタックを管理するためのツールとなっており、このエントリーで紹介するAIエージェント評価フレームワークであるHarborとは全くの別物なので注意してください。
※ コンテナレジストリにHarborという同名プロダクトがありますがこちらも全く関係ありません。
Harborのコンセプト
公式サイトには以下のように説明されており、Harborはコンテナ環境におけるAIエージェント/LLMの評価・最適化を行うためのフレームワークとして開発されました。Terminal-Benchというベンチマークを設計・開発したチームが、評価用フレームワークも作ったということです。
Harbor is a framework for evaluating and optimizing agents and models in container environments.
When we released Terminal-Bench in May, we were surprised to see it used in unexpected ways like building custom evals, optimizing prompts, running RL, generating SFT traces, and CI/CD agent testing.
We also learned that defining and managing containerized tasks at scale is hard. We built Harbor to make it easy.
Harborは以下のような特徴や機能を備えています。
- シンプルなモジュール式のインターフェイス(環境設定、エージェント、タスクをそれぞれ別々に管理する)
- 既存のエージェントCLI(Claude Code/Codex/Geminiなど)を標準サポート
- 有名なベンチマークとデータセットが登録済み
- クラウド環境との統合(Daytona/Modal/E2B/Runloop)
- エージェント最適化フレームワークとの統合(SkyRL/GEPA)
全ての機能を使ったことがあるわけではないのでこのエントリーでも全ては紹介できませんが、上の2つくらいまでをまずは紹介します。
Terminal-Benchとは
Harborが生まれるきっかけとなったTerminal-Benchについても簡単に紹介します。
Terminal-Benchの名前の通り、LLMが実際のターミナル環境でどれだけ正確にタスクを遂行できるかを測定するために設計された非常に実践的なベンチマークスイートです。従来のソフトウェアエンジニアリングのベンチマーク(SWE-BenchやHumanEvalなど)が「コードを書く能力」に焦点を当てていたのに対し、Terminal-Benchは「ツールを使いこなし、システムを操作するエージェントとしての能力」を評価します。モデルがコマンドを出力し、エージェントが実行して、その実行結果(標準出力、エラー、ファイルの変化)を見て次の行動を決定するという試行錯誤のプロセスを追跡してくれます。
Terminal-Benchの実際のタスク例としては、例えばOSのネットワーク設定が壊れているサーバ環境を復旧するタスクや、機械学習の一連の業務フロー(データセットの準備、学習コードの実装、モデル学習と評価)のタスクなど、単純なコーディングタスクではない実践的なエンジニアリングタスクによってエージェントの性能を評価することができます。
多様なタスク領域の例は以下の通り。
- システム管理: ユーザー管理、権限設定、パッケージ管理操作など
- データ処理: ログ解析、テキスト整形(sed/awk)、データベース操作など
- 機械学習: データセット準備・前処理、コーディング、モデル学習・評価など
- 開発ワークフロー: Git操作、コンパイル、ユニットテストの実行など
- ネットワーク: DNS設定・疎通確認、ポート監視、APIリクエストなど
コーディングエージェントの評価によく使われる非常に著名なSWE-Benchは実際のところPythonプログラマとして振舞えるかどうかという限定的な部分しか評価できないベンチマークとなっており、さらに細かく言うと、Djangoフレームワークが使えるかどうかを問う問題が全体の3割以上となっておりソフトウェアエンジニアエージェントの評価として不十分です。Terminal-Benchはより高度かつ多様なタスクが含まれており、アプリケーションエンジニア・インフラエンジニア・機械学習エンジニア(データサイエンティスト)などの幅広い領域の問題設定により総合的に評価されます。
ちなみにSWE-Benchに対する問題提起はOpenAIも2月に以下のようなポストを書いていましたので参考までに。
個人的にTerminal-Benchは命名がいまいちだと思っていて、確かにターミナル操作を駆使してエージェントが問題を解くわけですが、コーディングはもちろん、機械学習やネットワーク構築など問題設定の多様さと複雑さをもっとアピールできそうな名前にすればいいのになと思っています。
他ツールとの比較
僕はこれまで、Lighteval、Inspect AI、DeepEvalなどの著名なLLM評価ツールをいくつか試してきましたが、とりわけTerminal-Bench 2.0に代表されるような高度・複雑ななAIエージェントの行動評価においてはHarborが優れていると感じており、既存のAIエージェント評価タスクをHarborに移行できるか検証している最中です。
端的に比較すると、LightevalやInspect AIは一問一答(シングルターン)や対話(マルチターン)において、LLMの回答精度を評価する類のタスク、つまるところレスポンスのテキストをなんらかの評価指標(metrics)を使って測るタスクには向いています。一方でターミナル操作やコーディング作業、機械学習のためのデータセット整備などの途中作業経過まで含めたマルチステップの行動評価においては単一のメトリクスでは評価が難しいです。
ユニットテスト形式の評価ツールだと、Harborよりも遙かに著名なDeepEvalが君臨していますが、これはインタフェースがユニットテスト形式になっているだけで、実態としてはLightevalやInspect AIと同じ評価指標ベースの評価となります。その中で比較するならフットプリントが比較的小さいInspect AIが使いやすいと思いますが、DeepEvalでもどちらでもいいかなという感じです。Harborのテストは普通の開発過程と全く同じユニットテスト形式を採用しているので、AIエージェント評価においてもTDDスタイルで評価タスクを実装するようなフローになります。
DeepEvalではエージェント評価のための高度な評価指標(Task Completion、Tool Correctnessなど)が提供されていますが、内部的にはLLM as a Judgeに評価を委ねる前提のため、評価のために別途トークン消費が必要となります。コスト節約のためにvLLMなどを立ててローカルLLMを使うにしても、ジャッジ用のLLMを置くためのそれなりのGPUサーバが必要というハードルもありますし、Claude 4.6 OpusやGemini 3.1 Proのような強力なクローズドモデルも利用できません。Harborでもエージェント評価のための環境構築はもちろん必要ですが、評価対象のエージェントを実際に動作させる環境を用意することになるので実践的と言えます。開発工程におけるQAやステージング環境を用意するようなイメージです。
一方でHarborでは実行環境構築も含めて処理工程毎にテストを行うため、単一の評価指標(metrics)ベースでLLMの回答精度を測る用途では不向きです。例えばRAGの精度評価においてはDeepEvalやInspect AIがやはり最適な選択肢になるかと思います。また、HarborでもLLM as a Judgeによる評価は可能ですが、DeepEvalのようなリッチなインターフェースは提供されていないので、LLM as a Judgeでの評価で許容されるならDeepEval一択と言っていいかと思います。
Harborの使い方
以下の問題設定でHarborの使い方を紹介します。
- 問題設定(hello-world): /app/hello.txtファイルを作成し、そのテキストファイルに”Hello, World!”と書き込む
エージェントに対して指示を行い、エージェントの行動が終了したら結果を評価します。
harbor tasksコマンドで評価タスクを管理しますが、initでボイラープレートが生成されるので各ファイルを修正します。
$ harbor tasks init hello-world
✓ Task initialized in hello-world
Next steps:
- Add your instruction to: hello-world/instruction.md
- Define the environment by implementing the Dockerfile: hello-world/environment/Dockerfile
- Use the test script to generate a reward: hello-world/tests/test.sh
- Fill out the solution: hello-world/solution/solve.sh
$ cd hello-world
$ tree
.
├── environment
│ └── Dockerfile
├── instruction.md
├── solution
│ └── solve.sh
├── task.toml
└── tests
├── test.sh
└── test_outputs.py
1. エージェントへの指示(タスク)を書く
順番はどこからでもいいのですが、ここではまずinstruction.mdファイルにエージェントへの指示を書きます。Claude CodeやCodexを使う時と同様に自由な自然言語で記述します。
* instruction.md
/app/hello.txtファイルを作成し、そのファイルに"Hello, world!"と書き込んで。
タスク設定はこれだけです。評価結果を見て必要に応じて修正することになるファイルです。
2. Dockerイメージによる環境設定
次に、エージェントの実行環境となるDockerイメージを設定します。意識する点としては、このDockerイメージは実際にエージェントが作業する環境を想定すべきということです。安易にAlpineやDistrolessなどのイメージを使って軽量化したところで、実際にエージェントを動かす想定環境と乖離していると評価が無意味となるため環境定義は重要です。今回のサンプルではHello, world!をファイルに書くだけの簡易なタスクですが、通常のUbuntu OSの環境を使ってテストします。
* environment/Dockerfile
FROM ubuntu:24.04 WORKDIR /app
3. エージェント評価のテストを書く
エージェントの行動の結果を評価するためのユニットテストを書きます。人間による開発工程においては至極普通のステップですが、AIエージェント評価においてもその工程を取り入れたのはHarborの特徴です。LightEvalやInspect AIのような評価指標ベースのツールでは採れないアプローチです。
* tests/test_output.py
from pathlib import Path
def test_hello_file_exists():
"""Test that hello.txt file exists in the current directory."""
hello_path = Path("/app/hello.txt")
assert hello_path.exists(), f"File {hello_path} does not exist"
def test_hello_file_content():
"""Test that hello.txt contains exactly 'Hello, world!' followed by a newline."""
hello_path = Path("/app/hello.txt")
assert hello_path.read_text().strip() == "Hello, world!", (
f"Expected 'Hello, world!' but got '{hello_path.read_text().strip()}'"
)
/app/hello.txtが存在するか、ファイルの中身は”Hello, world!”と書かれているか、それを確認するための普通のユニットテストです。
他にはtask.tomlファイルにCPU/メモリ/GPU使用有無などの細かな環境設定からエージェントが使うMCPサーバの設定等もできるのですが、今回のhello-worldタスクの実行に必要な最低限の設定はこれで完了です。
エージェント評価
タスク(問題設定)、エージェント実行環境(Dockerコンテナ)、評価(ユニットテスト)の定義が終わったのでHarborによるエージェント評価を実行します。ここではCodexをエージェントとして使って評価してみましょう。デフォルトだとユニットテストにはpytestが使われますが、任意のフレームワークでテスト可能です。
# harborにより、カレントディレクトリに定義したタスクをopenai/gpt-5.4モデルを使ったCodexにより実行・評価
$ harbor run -p . -m openai/gpt-5.4 -a codex
Wrote Codex trajectory to jobs/2026-03-24__08-36-19/hello-world__8YU6bYr/agent/trajectory.json
1/1 Mean: 0.000 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0:01:22 0:00:00
Results written to jobs/2026-03-24__08-36-19/result.json
codex (gpt-5.4) on adhoc
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ Agent │ codex (gpt-5.4) │
│ Dataset │ adhoc │
│ Trials │ 1 │
│ Errors │ 0 │
│ │ │
│ Mean │ 1.000 │
│ │ │
│ Reward Distribution │ │
│ reward = 1.0 │ 1 │
└─────────────────────┴─────────────────┘
Harborによる評価結果として↑のように出力されます。hello-worldタスクにおいてはエージェントのテスト通過率が100%達成されたことがわかります。HarborのログファイルにもCodexの標準出力の内容と、pytestの実行結果が出力されていました。
# codex.txt
{"type":"thread.started","thread_id":"019d1f49-a75a-7c61-87e2-8743c5ba9d83"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"`/app/hello.txt`を作成して、指定の文字列を書き込みます。編集はそのまま反映します。"}}
{"type":"item.completed","item":{"id":"item_1","type":"file_change","changes":[{"path":"/app/hello.txt","kind":"add"}],"status":"completed"}}
{"type":"item.completed","item":{"id":"item_2","type":"agent_message","text":"`/app/hello.txt`を作成し、`Hello, world!`を書き込みました。"}}
{"type":"turn.completed","usage":{"input_tokens":23512,"cached_input_tokens":11776,"output_tokens":173}}
# test-stdout.txt
============================= test session starts ==============================
platform linux -- Python 3.14.0, pytest-8.4.1, pluggy-1.6.0
rootdir: /tests
plugins: json-ctrf-0.3.5
collected 2 items
../tests/test_outputs.py .. [100%]
==================================== PASSES ====================================
=========================== short test summary info ============================
PASSED ../tests/test_outputs.py::test_hello_file_exists
PASSED ../tests/test_outputs.py::test_hello_file_content
============================== 2 passed in 0.07s ===============================
ここではエージェントにはCodexを使いましたが、もちろん自作のエージェントでも評価可能です。というより自作エージェントの評価こそが本命でしょう。
Oracleモードによる評価
Habor固有の機能として、エージェントにoracleという名前を指定して実行した時に、予め用意された模範解答を実行できる機能があります。例として、今回のhello-worldタスクだと以下のようなシェルスクリプトになります。
* solution/solve.sh
#!/bin/bash # 問題設定(タスク)を再掲: 「/app/hello.txtファイルを作成し、そのテキストファイルに"Hello, World!"と書き込む」 echo "Hello, world!" > /app/hello.txt
模範解答を書けたら、oracleモードによる評価を実行します。Dockerコンテナ内でsolve.shが実行された後にユニットテストが実行されます。
# エージェント(-a)にoracleを指定
$ harbor -p . -a oracle
1/1 Mean: 1.000 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0:00:40 0:00:00
Results written to jobs/2026-03-24__08-58-07/result.json
oracle on adhoc
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃ Metric ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
│ Agent │ oracle │
│ Dataset │ adhoc │
│ Trials │ 1 │
│ Errors │ 0 │
│ │ │
│ Mean │ 1.000 │
│ │ │
│ Reward Distribution │ │
│ reward = 1.0 │ 1 │
└─────────────────────┴────────┘
このoracleモードは今回のhello-worldタスクのようなシンプルなタスクには不要ですが、例えば機械学習系タスクをエージェントに任せるような高度な問題設定だと模範解答の提供は重要です。機械学習系タスクの場合はGPU周りの環境構築が正しいかどうかも重要なので、oracleモードでは必ずテストをパスするようなエージェントを使わない模範解答を渡すので、oracleモードで正しくテストをパスしたならば環境構築は成功していることが保証されます。高度なエージェントタスクを評価したい場合はoracleモードでまずは動作するように整備してからエージェントの評価に移ることをオススメします。
セキュリティ
HarborはDockerコンテナの隔離環境の中でエージェントを実行します。とはいえ、普通のコンテナではホストOSのカーネルを共有しており見かけの隔離に留まるため、コンテナランタイムを変更することでより安全な環境で実行することができます。コンテナランタイムを通常使われるrunCからgVisorやKata Containersに交換するだけでもセキュリティは向上するのでオススメです。また、Harborの設定で「インターネット環境をエージェントに使わせない」という設定もできるのでネットワーク制御も少しは可能です。欲を言えば、KubernetesのCiliumのような柔軟なネットワークポリシーを定義できると理想ですが、ローカルのCLIツールでそこまでやるのは大変なので仕方ないんですよね。
クラウド環境との統合
Harborは前述の通りコンテナの隔離環境でエージェント評価を実行してくれますが、それでもセキュリティに不安・不満がある場合は、エージェントのサンドボックス環境として著名なクラウドとの連携もサポートされています。僕はまだ使ったことないので簡単な紹介に留めますが、以下のクラウドプロバイダがサポートされているようです。
クラウドプロバイダを使うメリットとしてはサンドボックス環境による隔離目的以外にもあります。エージェントがクラウド側で行動するようになり、テストのボトルネックがCPU処理からI/O処理となるため、通常はCPUコア数よりもはるかに高いレベルで並列化が可能になりパフォーマンス面でもメリットがあります。
使い方は簡単で、環境変数にAPI Keyを設定してHarborコマンドの-eオプションでクラウドプロバイダ名を指定するだけです。クラウド側に演算を任せられるので並列数(-n)も同時に指定するのが良いでしょう。
$ harbor run -d "データセット" \ -m "モデル" \ -a "エージェント" \ -e daytona \ -n "<テスト実行の並列数>"
ちなみに、HarborチームではDaytonaをオススメしているようです。
他フレームワークとの連携
Harborは高度・複雑なエージェント評価に適したツールですが、LLMが出力するテキスト評価にはオーバースペックであり不向きです。LLMのレスポンステキストの内容を評価するなら、やはりLightEvalやInspect AI、DeepEvalの方が使いやすいです。Inspect AIにはHarborと連携するモジュール(inspect_harbor)もあるのであるので、評価指標ベースのLLMの精度評価にはInspect AIを、エージェント評価にはHarborを使うという使い分けができます。Inspect AIについてはこのエントリーのスコープ外なので省略しますが、機会があれば別エントリーに書くかもしれません。
おわりに
高度・複雑なAIエージェントの性能評価を行うのに適したHarborという評価ツールを紹介しました。今回はhello, worldレベルの単純なエージェントを評価する例しか紹介していないので、Harborの良さを伝えきれてはいないのですが、Inspect AIやDeepEvalなどの他の評価ツールを知っている人にとってはそれらのツールとの違いはわかる内容にはなっているかなと思います。
LLMのベンチマーク界隈についてはいろんな問題提起の論文があって追いかけきれてないのですが、どうやらあまり健全ではない状況のようです。例えばOpenAIも言っているようにSWE-Benchで測れるコーディング能力は限定的ですし、特定のベンチマークのスコアだけ見てLLMの性能を判断するのは不適切・不健全ということです。その辺りについても機会があればブログに書きたいと思います。