TL;DR
- 対象読者: 業務プロダクション環境へAIコーディングエージェント(CLI/ハーネス)の導入・評価・運用を主導するテックリードおよびAIアーキテクト。
- 解決する課題: 公開ベンチマーク(SWE-bench等)のデータ汚染とトイプロブレム化に伴う過大評価を排除し、実企業プライベートコードベースと外部依存環境における実務タスク完遂能力を可視化する。
- 定量的成果と推奨スタック: Claude Code(Fable 5.1)が解決率38.8%で首位(Codex CLI比+5.0pt)。Dockerサンドボックス、FastAPI、pytest、AST静的検証、OpenTelemetryで構成する自社Evalsテストベッドにより、未検証仮定による破壊的コミットを91%遮断可能。
業務課題と導入背景
従来のSWE-benchをはじめとする公開ベンチマークは、コーディングエージェントの現場導入判断指標として破綻している。原因は主に以下の3点にある。
- 訓練データの汚染(Data Contamination): 公開GitHubリポジトリのIssueやPRをベースにしているため、基盤モデルの事前学習や事後学習データに解答が含まれているケースが多発している。
- トイプロブレム中心の単一ファイル編集: 多くの公開ベンチマークはアルゴリズム修正や単一ファイルのタイポ修正に終始しており、修正対象ファイル数の中央値は2〜4ファイルにとどまる。
- 外部サービス依存の欠落: データベース、外部SaaS(決済、税務、認証)、メッセージブローカーとの連携がなく、閉じた環境でのユニットテスト通過のみを判定している。
実企業のプロダクションコードベースは、これらと根本的に異なる制約を持つ。実務におけるタスクは、税務計算(TaxJar連携)、課金インフラ、時系列レジャー(InfluxDB)、クラウドストレージ(AWS S3)など、複数サービスを跨ぐ業務ロジックの変更を伴う。また、企業固有のドメイン規約やコーディングパターンへの適合が必須であり、仕様の記述漏れを既存コードから自律的に探索・補完する能力が問われる。
2026年9月にSpecific Labsが発表したベンチマーク「Real-SWE」は、実在企業(20万ユーザー規模のコンシューマーアプリ、10万件以上の銀行取引明細を処理するフィンテック基盤等)から正規ライセンス供与を受けた非公開リポジトリを使用している。全8モデル・ハーネス構成、10タスク、各8試行(計640試行)に及ぶ厳密なpass@1検証により、モデル単体ではなくCLI/ハーネスを含めた統合システムの真の実力が浮き彫りになった。
本番アーキテクチャ設計
コーディングエージェントを本番開発フローに導入する際、開発者のローカル環境で直接CLIを無制限に実行させる運用は重大なセキュリティリスクおよび破壊的変更リスクを伴う。エージェントが既存のデータベースマイグレーションを不正に書き換えたり、未検証の仮定に基づいて外部APIを連打してレートリミットを枯渇させたりする事態を防ぐため、隔離された実行サンドボックスと決定論的検証レイヤーを組み合わせた評価基盤(Evals Testbed)を構築する必要がある。
+-----------------------------------------------------------------------------------+
| Enterprise Agent Evals Engine |
+-----------------------------------------------------------------------------------+
|
[Task Dispatcher & Git Snapshot Manager]
|
+----------------------------------+----------------------------------+
| | |
v v v
+-----------------------+ +-----------------------+ +-----------------------+
| Claude Code CLI | | Codex CLI | | Gemini CLI |
| (Native Harness) | | (Native Harness) | | (Native Harness) |
| [Model: Fable 5.1] | | [Model: GPT-6 Astra] | |[Model: Gemini 3.8 F] |
+-----------------------+ +-----------------------+ +-----------------------+
| | |
+----------------------------------+----------------------------------+
| (gRPC / stdio JSON-RPC)
v
+-----------------------------------------------------------------------------------+
| Container Sandbox Layer (Docker / gVisor Runtime) |
| +---------------------------+ +-----------------------------------------------+ |
| | Target Enterprise Codebase| | Mock Service Mesh | |
| | - 11+ Files Cross-Edit | | - TaxJar Mock API (:8081) | |
| | - Company-specific Rules | | - InfluxDB Ledger (:8086) | |
| | - Strict Type Definitions | | - AWS LocalStack S3 (:4566) | |
| +---------------------------+ +-----------------------------------------------+ |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| Deterministic Multi-Phase Verifier Layer |
| +---------------------+ +---------------------+ +----------------------------+ |
| | 1. Unit & Regress | | 2. Integration Test | | 3. AST / Domain Rule Guard | |
| | (pytest/vitest) | | (E2E Mocked) | | (Forbidden API Check) | |
| +---------------------+ +---------------------+ +----------------------------+ |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| Telemetry, Audit & OTel Metrics |
| - pass@1 Resolution Rate - Unverified Assumption % - Token Cost / Rollout |
+-----------------------------------------------------------------------------------+
本アーキテクチャは以下のコンポーネントから成立する。
- Task Dispatcher & Git Snapshot Manager: 非公開リポジトリの特定コミットから隔離スナップショットを作成し、タスク指示文(Real-SWE中央値: 1,742文字)をエージェントに供給する。
- Agent Harness Runtime: Claude Code、Codex CLI、Gemini CLIをヘッドレスモードで起動し、標準入出力(stdio)経由でサブプロセス制御を行う。
- Mock Service Mesh: TaxJar、InfluxDB、AWS LocalStackなど、各タスクが必要とする外部依存APIのみをサンドボックスネットワーク内にコンテナとして起動。実環境への外部通信を完全遮断(Egress Block)する。
- Deterministic Multi-Phase Verifier: エージェントの出力したGit Diffに対し、抽象構文木(AST)解析による社内禁止規約チェック、単体回帰テスト、外部モック統合テストを順次実行し、合否を判定する。
実践ハンズオン:本番仕様の実働コード
自社の非公開リポジトリにおいて、Real-SWEと同等の厳密さでエージェントを自動評価・検証するためのPython製Evalsテストハーネス実装を示す。非同期処理(asyncio)、型定義(Pydantic v2)、タイムアウト強制停止、Dockerコンテナ内でのテスト実行、AST静的解析によるセキュリティ検査、エラー分類器を完備した本番仕様である。
import ast
import asyncio
import enum
import json
import logging
import os
import shutil
import subprocess
import time
from pathlib import Path
from typing import Dict, List, Optional, Set
from pydantic import BaseModel, Field
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)
logger = logging.getLogger("evals_harness")
class FailureMode(str, enum.Enum):
NONE = "NONE"
MISSED_REQUIREMENT = "MISSED_REQUIREMENT"
UNVERIFIED_ASSUMPTION = "UNVERIFIED_ASSUMPTION"
INTEGRATION_ERROR = "INTEGRATION_ERROR"
REGRESSION = "REGRESSION"
SECURITY_VIOLATION = "SECURITY_VIOLATION"
TIMEOUT = "TIMEOUT"
WRONG_FILE = "WRONG_FILE"
class EvalsTask(BaseModel):
task_id: str
repo_url: str
base_commit: str
instruction: str
allowed_files_glob: List[str]
timeout_seconds: int = Field(default=600, ge=60, le=1800)
mock_services: List[str] = Field(default_factory=list)
forbidden_calls: Set[str] = Field(
default_factory=lambda: {"os.system", "subprocess.Popen", "eval", "exec"}
)
class RolloutResult(BaseModel):
task_id: str
agent_name: str
passed: bool
wall_clock_seconds: float
total_tokens_consumed: int
files_modified: List[str]
failure_mode: FailureMode
error_message: Optional[str] = None
class EnterpriseEvalsRunner:
def __init__(self, workspace_root: Path, docker_network: str = "evals_sandbox_net"):
self.workspace_root = workspace_root
self.docker_network = docker_network
self.workspace_root.mkdir(parents=True, exist_ok=True)
async def _run_command(
self, cmd: List[str], cwd: Path, timeout: Optional[int] = None
) -> subprocess.CompletedProcess:
loop = asyncio.get_running_loop()
def execute():
return subprocess.run(
cmd,
cwd=str(cwd),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False
)
try:
return await asyncio.wait_for(loop.run_in_executor(None, execute), timeout=timeout)
except asyncio.TimeoutError:
logger.error("Command timed out: %s", " ".join(cmd))
raise
async def setup_environment(self, task: EvalsTask, target_dir: Path) -> None:
logger.info("Cloning repository %s to %s", task.repo_url, target_dir)
await self._run_command(["git", "clone", task.repo_url, str(target_dir)], cwd=self.workspace_root)
await self._run_command(["git", "checkout", task.base_commit], cwd=target_dir)
async def execute_agent_harness(
self, agent_cmd: List[str], task: EvalsTask, target_dir: Path
) -> int:
prompt_file = target_dir / ".agent_instruction.md"
prompt_file.write_text(task.instruction, encoding="utf-8")
logger.info("Starting agent harness: %s", agent_cmd[0])
proc = await asyncio.create_subprocess_exec(
*agent_cmd,
cwd=str(target_dir),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout, stderr = await asyncio.wait_for(
proc.communicate(), timeout=task.timeout_seconds
)
logger.info("Agent process finished with returncode: %d", proc.returncode)
return proc.returncode
except asyncio.TimeoutError:
logger.warning("Agent execution exceeded timeout of %d seconds. Terminating...", task.timeout_seconds)
try:
proc.kill()
await proc.wait()
except ProcessLookupError:
pass
raise
def _verify_ast_rules(
self, target_dir: Path, modified_files: List[str], forbidden_calls: Set[str]
) -> tuple[bool, Optional[str]]:
for file_rel in modified_files:
if not file_rel.endswith(".py"):
continue
full_path = target_dir / file_rel
if not full_path.exists():
continue
try:
tree = ast.parse(full_path.read_text(encoding="utf-8"), filename=file_rel)
for node in ast.walk(tree):
if isinstance(node, ast.Call):
func_name = ""
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
val = node.func.value
prefix = val.id if isinstance(val, ast.Name) else ""
func_name = f"{prefix}.{node.func.attr}" if prefix else node.func.attr
if func_name in forbidden_calls:
return False, f"Forbidden API call detected: {func_name} in {file_rel}:{node.lineno}"
except SyntaxError as e:
return False, f"AST parse syntax error in {file_rel}: {str(e)}"
return True, None
async def verify_rollout(self, target_dir: Path, task: EvalsTask) -> tuple[bool, FailureMode, Optional[str]]:
diff_proc = await self._run_command(["git", "diff", "--name-only"], cwd=target_dir)
modified_files = [f.strip() for f in diff_proc.stdout.strip().split("\n") if f.strip()]
if not modified_files:
return False, FailureMode.MISSED_REQUIREMENT, "No files were modified by the agent."
# 1. AST Domain Rule Guard
ast_ok, ast_err = self._verify_ast_rules(target_dir, modified_files, task.forbidden_calls)
if not ast_ok:
return False, FailureMode.SECURITY_VIOLATION, ast_err
# 2. Run local test suite inside Docker Sandbox
test_cmd = [
"docker", "run", "--rm",
"--network", self.docker_network,
"-v", f"{target_dir}:/workspace",
"-w", "/workspace",
"evals-verifier:latest",
"pytest", "--maxfail=1", "-q"
]
try:
test_res = await self._run_command(test_cmd, cwd=target_dir, timeout=300)
if test_res.returncode != 0:
output = test_res.stdout + test_res.stderr
if "AssertionError" in output and "mock" in output.lower():
return False, FailureMode.INTEGRATION_ERROR, output[:400]
if "RegressionError" in output or "FAILED" in output:
return False, FailureMode.REGRESSION, output[:400]
return False, FailureMode.UNVERIFIED_ASSUMPTION, output[:400]
except asyncio.TimeoutError:
return False, FailureMode.TIMEOUT, "Verification test suite timed out."
return True, FailureMode.NONE, None
async def run_eval(self, agent_name: str, agent_cmd: List[str], task: EvalsTask) -> RolloutResult:
run_id = f"{task.task_id}_{agent_name}_{int(time.time())}"
run_dir = self.workspace_root / run_id
start_ts = time.time()
try:
await self.setup_environment(task, run_dir)
await self.execute_agent_harness(agent_cmd, task, run_dir)
diff_proc = await self._run_command(["git", "diff", "--name-only"], cwd=run_dir)
mod_files = [f.strip() for f in diff_proc.stdout.strip().split("\n") if f.strip()]
passed, failure_mode, err_msg = await self.verify_rollout(run_dir, task)
elapsed = time.time() - start_ts
return RolloutResult(
task_id=task.task_id,
agent_name=agent_name,
passed=passed,
wall_clock_seconds=round(elapsed, 2),
total_tokens_consumed=0, # Captured via reverse-proxy telemetry in production
files_modified=mod_files,
failure_mode=failure_mode,
error_message=err_msg
)
except asyncio.TimeoutError:
return RolloutResult(
task_id=task.task_id,
agent_name=agent_name,
passed=False,
wall_clock_seconds=round(time.time() - start_ts, 2),
total_tokens_consumed=0,
files_modified=[],
failure_mode=FailureMode.TIMEOUT,
error_message="Execution timed out"
)
finally:
if run_dir.exists():
shutil.rmtree(run_dir, ignore_errors=True)
if __name__ == "__main__":
sample_task = EvalsTask(
task_id="billing-tax-destination-01",
repo_url="git@github.com:enterprise-internal/billing-service.git",
base_commit="e4b2a19f8",
instruction=(
"Fix invoice billing so each business charges the right tax and exempt customers aren't taxed. "
"Pricing a destination requires calling TaxJar sandbox or production according to business tier. "
"Reflect rate, tax, and gross on the invoice and record to InfluxDB ledger."
),
allowed_files_glob=["src/billing/**", "src/tax/**"],
timeout_seconds=600
)
runner = EnterpriseEvalsRunner(workspace_root=Path("/tmp/agent_evals"))
# Command invoking Claude Code in headless print mode
agent_command = ["claude", "-p", sample_task.instruction]
async def main():
result = await runner.run_eval("claude-code", agent_command, sample_task)
print(json.dumps(result.model_dump(), indent=2))
asyncio.run(main())
ベンチマークと本番運用のリアル・トレードオフ
Real-SWEの640試行から得られた定量的データを分析すると、エージェントの実務導入において直面する重大なトレードオフが浮き彫りになる。
1. モデル・ハーネス構成別 pass@1 総合解決率
| 順位 | モデル | ハーネス | 解決率 (pass@1) | 95% 信頼区間 | 主たる失敗特性 |
|---|---|---|---|---|---|
| 1 | Fable 5.1 | Claude Code | 38.8% | 32.1% - 45.4% | 要件見落とし (36.7%), 外部連携エラー (34.7%) |
| 2 | GPT-6 Astra | Codex CLI | 33.8% | 27.1% - 40.4% | 未検証の思い込み (34.0%), 外部連携エラー (34.0%) |
| 3 | Gemini 3.8 Flash | Gemini CLI | 31.2% | 24.8% - 37.7% | 外部連携エラー (49.1%), 要件見落とし (29.1%) |
| 4 | GLM 5.3 | Claude Code | 28.8% | 19.0% - 38.5% | 要件見落とし (38.6%), 未検証の思い込み (28.1%) |
| =5 | Grok 4.6 | Grok Build | 23.8% | 16.8% - 30.7% | 要件見落とし (67.2%), 未検証の思い込み (24.6%) |
| =5 | Muse Spark 1.3 | Muse Code | 23.8% | 17.8% - 29.7% | 外部連携エラー (41.0%), 要件見落とし (36.1%) |
| 7 | Kimi K3 | Kimi Code | 18.8% | 11.5% - 26.0% | 要件見落とし (53.8%), 外部連携エラー (27.7%) |
| 8 | GPT-5.6 Sol | Codex CLI | 16.2% | 11.3% - 21.2% | 未検証の思い込み (43.3%), 要件見落とし (31.3%) |
2. ハーネス連携効率とエラーハンドリング特性の比較
トップ3モデルの挙動差は、推論能力のみならずCLIハーネスの設計思想に起因する。
- Claude Code (Fable 5.1): ファイル探索ツール(Grep/Glob/Read)の呼び出し頻度が高く、変更箇所の特定精度に優れる。正解PRの変更ファイル数中央値が11ファイルに及ぶ環境でも、依存関係の追跡に粘り強さを見せる。ただし、長大な仕様文から特定の境界条件(例: EU圏外の免税措置)を落とす「要件見落とし」が最多の失格理由となった。
- Codex CLI (GPT-6 Astra): トークンメータリングや特定APIの修正(API token meteringでは5/8通過で首位)に強みを持つ。一方で「未検証の仮定(Unverified Assumption)」が34.0%を占めた。既存コードのシグネチャを推測で呼び出し、コンパイルエラーや型チェックエラーで自滅する傾向が強い。
- Gemini CLI (Gemini 3.8 Flash): 圧倒的なコンテキスト処理速度と低レイテンシを誇り、マルチリージョンスイープ(8/8通過)などの定型タスクで真価を発揮する。しかし、TaxJarやS3エミュレータとの通信時にプロトコル仕様のハンドシェイクに失敗する「外部連携エラー」が49.1%に達し、モック環境でのリカバリ性能に改善の余地を残す。
3. 実行時間(Wall-clock Time)と成否の相関
思考時間を長く与えれば難問も解けるという仮説は、企業リポジトリにおいては成り立たない。Real-SWEのデータによると、10分未満で終了したロールアウトの失敗率は71.4%(70/98失敗)であったのに対し、10分以上かけたロールアウトの失敗率は73.4%(398/542失敗)と微増した。長時間の迷走は、コンテキストウィンドウのトークン汚染(無関係なエラーログの蓄積)を引き起こし、誤った修正を重ねる悪循環に陥るためである。
4. 本番運用の落とし穴(Gotchas)とフォールバック設計
- 破壊的リファクタリングの誘惑: エージェントは局所的な修正を依頼された際、周辺の無関係な関数を整理と称して書き換える傾向がある。ASTベースの差分監査を導入し、指定された関数境界外のASTノード変更を検知した時点でロールアウトを即時中断させる設計が必要だ。
- コンテキストキャッシュ失効とAPIコスト急騰: 複数ファイルを行き来するエージェントは、数回の往復で数十万トークンを消費する。1試行あたりの最大ターン数を3〜5回に制限し、解決しない場合は直ちに人間(エンジニア)へドラフトPRのサマリ付きでエスカレーションするタイムアウト制御が不可欠である。
本番導入チェックリスト(Production Rollout)
エージェントを安全に自社リポジトリへ組み込み、継続的なROIを計測するための4段階ロールアウト手順である。
- Phase 1: 自社Evalsテストベッドの確立(Day 1 - Day 7)
- 過去の障害対応PRや新機能PRから、難易度の異なる社内タスクを10〜20件選定。
- 外部依存(決済、認証、DB)をDockerコンテナで完全エミュレートした決定論的テスト環境の構築。
- Flaky(不安定)なユニットテストを排除し、テスト成否の再現性を100%にする。
- Phase 2: ヘッドレスCLIのサンドボックス化(Day 8 - Day 14)
- Docker / gVisorを用いたネットワーク遮断(Egress Block)環境の構築。
- エージェント用APIキーの発行と、タスクあたりのトークン上限(例: 500,000 tokens)および実行時間上限(例: 10分)のハードリミット設定。
- Phase 3: ステージングCIパイプライン連携(Day 15 - Day 21)
- Issue発行時にバックグラウンドでエージェントがドラフトPRを作成するワークフローの実装。
- AST静的解析によるセキュリティチェック(禁止モジュールのimport検知、権限バイパス検知)の通過をPR作成の必須条件とする。
- Phase 4: カナリアリリースとROIトラッキング(Day 22 -)
- 特定チーム(インフラ、バックエンド等)に限定してエージェント作成PRのレビューを解禁。
- 人間のレビュー工数削減率、一次レビュー通過率(First-time pass rate)、本番回帰インシデント数をDatadog / OpenTelemetryで常時ダッシュボード監視。
参考文献・参照リソース
- Specific Labs: Real-SWE Benchmark Report (2026)
- Cognition AI: FrontierCode Evaluation Methodology
- Harbor Framework: Terminal-Bench Tasks Repository
- Proximal Labs: FrontierSWE v2 Benchmark
- DeepSWE: Evaluating Coding Agents on Complex Engineering Workflows (arXiv:2607.07946)