Skip to main content
Source Code: src/gaia/eval/
This page covers gaia eval agent — the scenario-based benchmark that stress-tests the live Agent UI end-to-end. See the Getting Started guide for a quick introduction.

Overview

The Agent Eval Benchmark drives the live Agent UI through multi-turn conversations, then judges every response with an LLM (claude-sonnet-4-6 by default). Each scenario creates a real Agent UI session via MCP, sends user messages, captures the full transcript, and produces a scored evaluation. 54 YAML scenario files span 10 categories covering RAG quality, context retention, tool selection, error recovery, hallucination resistance, adversarial inputs, personality compliance, vision capabilities, web/system tools, and real-world documents. Why this matters: Unlike the general eval framework (which compares isolated model outputs), the Agent Eval Benchmark tests the full system end-to-end — RAG indexing, tool dispatch, context window management, multi-turn state, and hallucination resistance — through the same Agent UI that real users interact with. Key Features:
  • Multi-turn scenario simulation with persona-driven user messages
  • 7-dimension scoring rubric with deterministic weighted aggregation
  • Automated fix mode that invokes Claude Code to repair failures and re-evaluate
  • Regression testing with baseline comparison and per-scenario deltas
  • Architecture audit mode (no LLM calls) to detect structural limitations
  • CI/CD integration with budget and timeout controls

Architecture

The benchmark runs as two distinct processes connected over MCP: a Python orchestrator (AgentEvalRunner) that manages scenarios, timeouts, and scoring; and a claude -p subprocess per scenario that acts as both user simulator and LLM judge. The system under test — the Agent UI and its Lemonade backend — runs independently and is treated as a black box.

System Overview

Key design decisions:

Eval Agent Lifecycle (per-scenario subprocess)

Each claude -p subprocess runs a 6-phase protocol. The eval agent has access to the Agent UI MCP server tools and uses them to drive a real session: Phase 2 detail: The eval agent generates natural language user messages from the turn’s objective and persona — not verbatim copies. It calls send_message() and waits for the full agent response before scoring. It does not retry on poor responses; it scores and moves to the next turn regardless. Error short-circuits and skips: Timeout scaling — the runner computes an effective timeout per scenario to account for document indexing and turn count:

Score Computation Pipeline

After each subprocess returns its JSON result, the runner validates and deterministically overwrites the eval agent’s arithmetic before writing the trace file: The recomputation applies in three passes:
  1. Per-turn: recompute_turn_score(scores_dict) applies _SCORE_WEIGHTS. If the recomputed value differs from the eval agent’s reported value by more than 0.25, the discrepancy is logged and the recomputed value wins. The per-turn pass flag is also recalculated (correctness ≥ 4 AND computed ≥ 6.0).
  2. Scenario-level: overall_score is recomputed as the arithmetic mean of recomputed per-turn scores, replacing the eval agent’s scenario-level value entirely.
  3. Status re-derivation: The runner applies the rubric rules to recomputed values. An eval-agent-reported PASS can be overridden to FAIL (if any turn has correctness < 4 or overall_score < 6.0), and a reported FAIL can be upgraded to PASS (if all turns satisfy both criteria). BLOCKED_BY_ARCHITECTURE is never overridden — if it passes all rubric criteria, a warning is emitted for human review instead of an automatic upgrade. True infrastructure statuses (TIMEOUT, BUDGET_EXCEEDED, INFRA_ERROR, SETUP_ERROR) are also never overridden.
  4. Average score integrity: In scorecard.json, FAIL scenario scores are capped at 5.99 before computing avg_score. A scenario can score 9.8/10 on five of seven dimensions and still FAIL on hallucination — that 9.8 would inflate the benchmark’s quality signal if included raw.

Fix Mode Loop

When --fix is passed, the runner repeats a diagnose-repair-retest cycle: The fixer subprocess (claude -p fixer.md) receives the scorecard.json path, summary.md path, and a JSON list of failing scenario IDs with their root_cause and recommended_fix fields. It patches files in src/gaia/ and writes a fix_log.json documenting each change. The loop exits early if judged_pass_rate ≥ --target-pass-rate or all scenarios pass.

Prerequisites

1

Install eval dependencies

2

Set up the judge model API key

The benchmark uses Claude as the judge model. Export your API key:
3

Start the LLM backend

Lemonade server provides the local LLM and embeddings for the Agent UI:
4

Start the Agent UI backend

5

Verify Claude Code CLI

The runner invokes scenarios via claude -p subprocess:
If not installed, see Claude Code installation.

Quick Start

Results are written to eval/results/<run_id>/.

Scenario Categories


Scoring System

The judge evaluates each turn across 7 dimensions with fixed weights: Per-turn score is the weighted sum of all 7 dimensions (0–10 scale). The runner recomputes this deterministically from dimension scores rather than trusting the LLM’s arithmetic — ensuring consistent results regardless of which model is used as judge. Scenario-level score is the mean of all per-turn scores. FAIL scores are capped at 5.99 in the average so a single perfect FAIL cannot inflate the benchmark’s overall quality signal.

Pass / Fail Rules

  • PASS: overall_score >= 6.0 AND no turn has correctness < 4
  • FAIL: overall_score < 6.0 OR any turn has correctness < 4

Severity Levels

  • critical — Automatic FAIL if the agent hallucinates, invents facts, or fails the primary objective. Scenarios like hallucination_resistance, cross_turn_file_recall, and smart_discovery use this level.
  • standard — Scored purely on the numeric threshold.

Status Legend

Statuses are grouped by how they affect scoring. Judged statuses count toward avg_score and judged_pass_rate. Infrastructure statuses are excluded from quality metrics — they indicate environmental issues, not agent quality.

Test Corpus

The benchmark ships with a synthetic corpus in eval/corpus/documents/ with ground truth facts defined in eval/corpus/manifest.json. The manifest also defines adversarial documents (empty.txt, unicode_test.txt, duplicate_sections.md) used by the adversarial category.
RAG cache freshness — If you see cached documents showing “1 chunk, 0B”, clear the RAG cache before running:
Stale caches can contain synthesized summaries instead of verbatim document content, causing false failures.

CLI Reference


Fix Mode

Fix mode automates the repair loop: evaluate, diagnose failures, patch source code, and re-evaluate. Phases:
  1. Phase A: Full eval run — All scenarios (or filtered set) execute normally
  2. Phase B: Diagnose + repair — Claude Code reads failing scenario transcripts and patches Agent UI source files
  3. Phase C: Re-run failures — Only the previously failed scenarios are re-evaluated
  4. Phase D: Diff scorecard — Produces a comparison showing regressions and improvements
The fixer prioritizes repairs in this order:
  1. Critical severity scenarios first
  2. Architecture fixes (in _chat_helpers.py, base agent classes) before prompt fixes
  3. Multi-scenario failures before single-scenario issues
Fix mode uses Claude Code to patch src/gaia/ source files. Review diffs before committing. Always run python util/lint.py --all --fix after fix iterations.

Regression Testing

Comparison output includes:
  • Per-scenario delta: PASS to FAIL regressions (highlighted), FAIL to PASS improvements
  • Category-level pass rate change
  • Score delta per scenario (warns when score drops by more than 2.0 points within the same status)

Writing Custom Scenarios

Scenario YAML files live under eval/scenarios/<category>/. The runner discovers them automatically via recursive glob.

Full Schema Example

Each turn needs at least one of ground_truth (non-null dict) or success_criteria (non-empty string) — providing both gives maximum judging precision. Valid personas: casual_user, data_analyst, power_user, confused_user, adversarial_user.
Place your YAML file under eval/scenarios/<category>/ and it will be picked up automatically on the next run.

Capturing Real Sessions

This reads the session from the Agent UI database (~/.gaia/chat/gaia_chat.db), extracts turns and indexed documents, and writes a scenario YAML to eval/scenarios/captured/. After capture, you must review and edit the generated file to add proper ground_truth and success_criteria fields — the capture tool populates the structure but cannot infer expected answers.

Architecture Audit

Runs a static analysis of the Agent UI’s internal constraints without making any LLM calls:
  • History window size (_MAX_HISTORY_PAIRS in _chat_helpers.py)
  • Message truncation limits (_MAX_MSG_CHARS)
  • Tool result persistence in conversation history
  • Agent persistence model (stateless per-message vs. persistent)
The audit flags which scenarios will be automatically BLOCKED_BY_ARCHITECTURE and provides recommendations (e.g., “increase _MAX_HISTORY_PAIRS to 10+”). Run this before the full benchmark to understand expected failures due to architecture limits rather than AI quality.

Output Files

After a run, results are written to eval/results/<run_id>/:

Sample summary.md Output


CI/CD Integration

Use --category to limit CI costs. The rag_quality and context_retention categories cover the highest-impact tests and typically complete in under 10 minutes.
The benchmark includes a GitHub Actions workflow at .github/workflows/test_eval.yml that runs structural validation (scenario YAML parsing, manifest integrity, scorecard generation) on every push to main or PR targeting main. Full LLM-driven eval runs are triggered via workflow_dispatch or scheduled separately.

Next Steps

Evaluation Framework

Batch experiments, ground truth generation, and model comparison

Agent UI Guide

The desktop chat application that the benchmark tests

RAG SDK

Document indexing and retrieval under the hood

Agent System

Base Agent class, tools, and state management