Skip to content
Back to skills

2452 Benchmark Analysis 98345bed

ASecurity

Guide for analyzing agent benchmark runs (like Terminal Bench/Harbor) using LangSmith traces.

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 31, 2026
toolspythongobashapi

Works with

  • terminal
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 31, 2026

npx -y skills add tools-only/X-Skills --skill 2452-benchmark-analysis_98345bed --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2452 Benchmark Analysis 98345bed?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2452 Benchmark Analysis 98345bed
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2452-benchmark-analysis-98345bed/badge)](https://www.skillsdirectory.com/skills/tools-only-2452-benchmark-analysis-98345bed)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Benchmark Analysis Guide

Guide for analyzing agent benchmark runs (like Terminal Bench/Harbor) using LangSmith traces.

## Understanding Benchmark Structure

### Terminology

| Term | Definition | Example |
|------|------------|---------|
| **Benchmark** | A suite of tasks evaluating agent capabilities | Terminal Bench, Harbor |
| **Task** | A specific problem to solve | "crack-7z-hash", "build-pov-ray" |
| **Trial** | One attempt at a task | `crack-7z-hash__Lu7oSkD` |
| **Job** | Complete benchmark run (all tasks × N trials) | job_id in metadata |
| **Reward** | Task outcome | 1.0 (pass) or 0.0 (fail) |

### Data Sources

After a benchmark run:
1. **result.json** - Aggregated statistics and outcomes
2. **Trial directories** - Local logs and outputs
3. **LangSmith traces** - Full conversation history

> Compatibility note: LangSmith run status is a top-level field (`status`) in the run data format. Some benchmark export pipelines also mirror status under metadata; examples below support both.

## Download Workflow

### Step 1: Get Job ID from result.json

```python
import json

with open("result.json") as f:
    result = json.load(f)

job_id = result["id"]
print(f"Job ID: {job_id}")
```

### Step 2: Query LangSmith for Traces

```bash
# Using download_traces.py script
uv run skills/langsmith-trace-analyzer/scripts/download_traces.py \
  --project "my-benchmark-project" \
  --filter "and(eq(metadata_key, \"job_id\"), eq(metadata_value, \"${job_id}\"))" \
  --output ./traces \
  --organize
```

### Step 3: Organize by Outcome

The script automatically organizes traces:

```
traces/
├── manifest.json
└── by-outcome/
    ├── passed/           # reward=1.0
    ├── failed/           # reward=0.0
    └── errors/
        ├── GraphRecursionError/
        ├── TimeoutError/
        └── DaytonaError/
```

## Analysis Workflow

### Phase 1: High-Level Statistics

```python
import json
from pathlib import Path
from collections import Counter

traces_dir = Path("./traces/by-outcome")

# Count outcomes
passed = list((traces_dir / "passed").glob("*.json"))
failed = list((traces_dir / "failed").glob("*.json"))
errors = list((traces_dir / "errors").rglob("*.json"))

total = len(passed) + len(failed) + len(errors)

print(f"Benchmark Results:")
print(f"  Total trials: {total}")
print(f"  Passed: {len(passed)} ({len(passed)/total*100:.1f}%)")
print(f"  Failed: {len(failed)} ({len(failed)/total*100:.1f}%)")
print(f"  Errors: {len(errors)} ({len(errors)/total*100:.1f}%)")

# Error breakdown
error_types = Counter()
for error_dir in (traces_dir / "errors").iterdir():
    if error_dir.is_dir():
        error_types[error_dir.name] = len(list(error_dir.glob("*.json")))

print(f"\nError breakdown:")
for error_type, count in error_types.most_common():
    print(f"  {error_type}: {count}")
```

### Phase 2: Per-Task Analysis

```python
def analyze_by_task(traces_dir: Path):
    """Analyze success rate per task."""
    from collections import defaultdict

    by_task = defaultdict(lambda: {"passed": 0, "failed": 0, "errors": 0})

    # Extract task name from trial name: task__id -> task
    def get_task(trial_name):
        return trial_name.rsplit("__", 1)[0]

    for outcome in ["passed", "failed"]:
        for trace_file in (traces_dir / outcome).glob("*.json"):
            task = get_task(trace_file.stem)
            by_task[task][outcome] += 1

    for error_dir in (traces_dir / "errors").rglob("*.json"):
        if error_dir.is_file():
            task = get_task(error_dir.stem)
            by_task[task]["errors"] += 1

    # Report
    print("\nPer-Task Results:")
    for task in sorted(by_task.keys()):
        stats = by_task[task]
        total = stats["passed"] + stats["failed"] + stats["errors"]
        pass_rate = stats["passed"] / total * 100 if total > 0 else 0

        status = "✅" if stats["passed"] == total else "⚠️" if stats["passed"] > 0 else "❌"
        print(f"{status} {task}: {stats['passed']}/{total} passed ({pass_rate:.0f}%)")
```

### Phase 3: Failure Deep Dive

Focus on systematic failures (0/N passed):

```python
def find_systematic_failures(traces_dir: Path):
    """Find tasks that fail consistently across all trials."""
    from collections import defaultdict

    by_task = defaultdict(list)

    # Collect all outcomes by task
    for outcome in ["passed", "failed"]:
        for trace_file in (traces_dir / outcome).glob("*.json"):
            task = trace_file.stem.rsplit("__", 1)[0]
            by_task[task].append(outcome)

    # Find tasks with 0 passes
    systematic_failures = []
    for task, outcomes in by_task.items():
        if "passed" not in outcomes and len(outcomes) >= 2:
            systematic_failures.append({
                "task": task,
                "trials": len(outcomes),
                "outcomes": Counter(outcomes)
            })

    print(f"\nSystematic Failures ({len(systematic_failures)} tasks):")
    for item in sorted(systematic_failures, key=lambda x: -x["trials"]):
        print(f"  {item['task']}: {dict(item['outcomes'])}")

    return systematic_failures
```

## Categorization Framework

### Error Categories for Benchmarks

```python
def categorize_benchmark_failure(trace: Dict) -> tuple[str, str]:
    """
    Categorize benchmark failure into actionable categories.

    Returns (category, subcategory)
    """
    messages = trace.get("messages", [])
    status = trace.get("status") or trace.get("metadata", {}).get("status")
    last_message = str(messages[-1]) if messages else ""

    # Infrastructure errors
    if len(messages) < 3 or status == "error":
        last_content = " ".join(str(m.get("content", "")) for m in messages[-3:])

        if "DaytonaError" in last_content:
            return "infrastructure", "sandbox_failure"
        elif "BadRequestError" in last_content:
            return "infrastructure", "api_error"

    # Resource limit errors
    if "GraphRecursionError" in last_message:
        # Check if agent was stuck in loop or making progress
        tool_calls = []
        for msg in messages:
            if msg.get("tool_calls"):
                tool_calls.extend([tc["name"] for tc in msg["tool_calls"]])

        # If same tool called >5 times consecutively, it's a loop
        for i in range(len(tool_calls) - 5):
            if len(set(tool_calls[i:i+5])) == 1:
                return "resource_limit", "stuck_in_loop"

        return "resource_limit", "recursion_limit_progressing"

    # Task failures - need manual review
    return "task_failure", "needs_review"
```

## Comparison Patterns

### Compare Multiple Benchmark Runs

```python
def compare_benchmark_runs(run1_dir: Path, run2_dir: Path):
    """Compare two benchmark runs (e.g., different models or versions)."""

    def get_stats(run_dir):
        passed = len(list((run_dir / "passed").glob("*.json")))
        failed = len(list((run_dir / "failed").glob("*.json")))
        errors = len(list((run_dir / "errors").rglob("*.json")))
        total = passed + failed + errors

        return {
            "total": total,
            "passed": passed,
            "failed": failed,
            "errors": errors,
            "pass_rate": passed / total if total > 0 else 0
        }

    stats1 = get_stats(run1_dir)
    stats2 = get_stats(run2_dir)

    print("Benchmark Comparison:")
    print(f"\nRun 1:")
    print(f"  Pass rate: {stats1['pass_rate']*100:.1f}% ({stats1['passed']}/{stats1['total']})")
    print(f"  Errors: {stats1['errors']}")

    print(f"\nRun 2:")
    print(f"  Pass rate: {stats2['pass_rate']*100:.1f}% ({stats2['passed']}/{stats2['total']})")
    print(f"  Errors: {stats2['errors']}")

    print(f"\nDifference:")
    print(f"  Pass rate: {(stats2['pass_rate'] - stats1['pass_rate'])*100:+.1f}%")
    print(f"  Net improvement: {stats2['passed'] - stats1['passed']:+d} tasks")
```

### Find Regressions

```python
def find_regressions(baseline_dir: Path, new_dir: Path):
    """Find tasks that passed in baseline but failed in new run."""

    def get_passed_tasks(run_dir):
        tasks = set()
        for trace_file in (run_dir / "passed").glob("*.json"):
            task = trace_file.stem.rsplit("__", 1)[0]
            tasks.add(task)
        return tasks

    baseline_passed = get_passed_tasks(baseline_dir)
    new_passed = get_passed_tasks(new_dir)

    regressions = baseline_passed - new_passed
    improvements = new_passed - baseline_passed

    print(f"Regressions ({len(regressions)} tasks):")
    for task in sorted(regressions):
        print(f"  ❌ {task}")

    print(f"\nImprovements ({len(improvements)} tasks):")
    for task in sorted(improvements):
        print(f"  ✅ {task}")
```

## Report Template

```python
def generate_benchmark_report(traces_dir: Path, output_file: str):
    """Generate a comprehensive benchmark report."""

    # Collect data
    passed = list((traces_dir / "passed").glob("*.json"))
    failed = list((traces_dir / "failed").glob("*.json"))
    errors = list((traces_dir / "errors").rglob("*.json"))
    total = len(passed) + len(failed) + len(errors)

    # Load manifest for metadata
    with open(traces_dir.parent / "manifest.json") as f:
        manifest = json.load(f)

    # Generate report
    report = f"""# Benchmark Analysis Report

Generated: {manifest['created_at']}
Project: {manifest.get('project', 'N/A')}

## Summary

- **Total Trials**: {total}
- **Pass Rate**: {len(passed)/total*100:.1f}% ({len(passed)}/{total})
- **Failures**: {len(failed)} ({len(failed)/total*100:.1f}%)
- **Errors**: {len(errors)} ({len(errors)/total*100:.1f}%)

## Error Breakdown

"""

    # Add error breakdown
    error_types = Counter()
    for error_dir in (traces_dir / "errors").iterdir():
        if error_dir.is_dir():
            count = len(list(error_dir.glob("*.json")))
            error_types[error_dir.name] = count
            report += f"- {error_dir.name}: {count}\n"

    # Per-task analysis
    report += "\n## Per-Task Results\n\n"

    from collections import defaultdict

    by_task = defaultdict(lambda: {"passed": 0, "failed": 0, "errors": 0})

    for outcome in ["passed", "failed"]:
        for trace_file in (traces_dir / outcome).glob("*.json"):
            task = trace_file.stem.rsplit("__", 1)[0]
            by_task[task][outcome] += 1

    # Sort by pass rate
    task_results = []
    for task, stats in by_task.items():
        task_total = sum(stats.values())
        pass_rate = stats["passed"] / task_total if task_total > 0 else 0
        task_results.append((task, stats, pass_rate))

    task_results.sort(key=lambda x: x[2])  # Sort by pass rate

    report += "| Task | Passed | Failed | Errors | Pass Rate |\n"
    report += "|------|--------|--------|--------|----------|\n"

    for task, stats, pass_rate in task_results:
        report += f"| {task} | {stats['passed']} | {stats['failed']} | {stats['errors']} | {pass_rate*100:.0f}% |\n"

    # Write report
    with open(output_file, "w") as f:
        f.write(report)

    print(f"Report written to: {output_file}")
```

## Best Practices

1. **Download all traces first** before analyzing (avoids rate limits)
2. **Organize by outcome** automatically using scripts
3. **Start with high-level stats** before diving into individual traces
4. **Focus on systematic failures** (0/N passed) for highest impact
5. **Compare versions** to measure improvements
6. **Categorize errors** to prioritize fixes (infrastructure vs agent issues)
7. **Track over time** to spot trends and regressions

Files in this skill

  • README.md1000 B
  • skill.md11.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.