인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
개발 및 엔지니어링 · SKILL.md

agent-health

agent-health

Evaluate, benchmark, and improve an AI agent with the Agent Health CLI and server APIs. Use when the user wants to run evaluations/benchmarks, interpret judge results (matcherResults / improvementStrategies), diagnose eval-vs-reality gaps, or raise an agent's pass rate. Mirror of docs/skills/AGENT_HEALTH.md.

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: agent-health description: Evaluate, benchmark, and improve an AI agent with the Agent Health CLI and server APIs. Use when the user wants to run evaluations/benchmarks, interpret judge results (matcherResults / improvementStrategies), diagnose eval-vs-reality gaps, or raise an agent's pass rate. Mirror of docs/skills/AGENT_HEALTH.md.

Agent Health - AI Assistant Instructions

Use these instructions to evaluate and improve your agent using the agent-health CLI and server APIs.


Setup By AI Assistant

Copy this file into your project so your AI coding assistant auto-loads it — either from a local checkout or straight from GitHub.

Claude Code

mkdir -p .claude/skills
curl -o .claude/skills/agent-health.md \
  https://raw.githubusercontent.com/opensearch-project/agent-health/main/docs/skills/AGENT_HEALTH.md
# or, from a checkout: cp docs/skills/AGENT_HEALTH.md .claude/skills/

Auto-discovered as a skill.

Kiro

mkdir -p .kiro/steering
curl -o .kiro/steering/agent-health.md \
  https://raw.githubusercontent.com/opensearch-project/agent-health/main/docs/skills/AGENT_HEALTH.md

Auto-loaded as a steering file.

Cursor / Windsurf / Others

Copy to project root, then tell your assistant: "Read AGENT_HEALTH.md and follow it"


Quick start — what to ask your assistant

Once this file is loaded, just ask:

"Evaluate my agent using agent-health and fix any failures"

Or step by step:

"Run an agent-health benchmark against my agent"
"What failed and why?"
"Fix the high-priority issues"
"Re-run to verify"

What happens under the hood:

1. Assistant runs: npx agent-health benchmark ...
        ↓
2. Gets JSON with failures + judge feedback (matcherResults + improvementStrategies)
        ↓
3. Reads your agent code
        ↓
4. Makes fixes based on the improvement strategies
        ↓
5. Re-runs the benchmark to verify

Prerequisites

Storage (file-based by default; OpenSearch optional)

Agent Health uses file-based storage by default — results are written to a local .agent-health/data/ directory and run / benchmark work with no OpenSearch cluster required. Point at OpenSearch only when you want shared, production-grade persistence (multiple machines / a team dashboard).

To use OpenSearch instead of file storage, set:

VariableDescription
OPENSEARCH_STORAGE_ENDPOINTOpenSearch cluster URL (e.g. https://search-my-cluster.us-west-2.es.amazonaws.com)
OPENSEARCH_STORAGE_USERNAMEOpenSearch username (basic auth)
OPENSEARCH_STORAGE_PASSWORDOpenSearch password (basic auth)

SigV4 auth (AWS profile or the default credential chain; es domains or aoss Serverless collections), none (no auth), and basic auth are all supported, configurable via env vars, agent-health.config.json, or the Settings UI — see docs/CONFIGURATION.md.

AWS Credentials for LLM Judge (Required)

The Bedrock LLM judge scores evaluation results and needs AWS credentials to call Bedrock.

VariableDescription
AWS_PROFILEAWS profile with Bedrock access
AWS_REGIONAWS region for Bedrock (e.g. us-west-2)
BEDROCK_MODEL_ID(Optional) Default judge model ID. Default: us.anthropic.claude-sonnet-4-5-20250929-v1:0

.env File

The CLI automatically loads a .env file from the current working directory (the folder where commands are run). Create a .env file with all required variables:

# OpenSearch Storage
OPENSEARCH_STORAGE_ENDPOINT=https://search-my-cluster.us-west-2.es.amazonaws.com
OPENSEARCH_STORAGE_USERNAME=admin
OPENSEARCH_STORAGE_PASSWORD=your-password

# AWS / Bedrock
AWS_PROFILE=your-aws-profile
AWS_REGION=us-west-2
# BEDROCK_MODEL_ID=us.anthropic.claude-sonnet-4-5-20250929-v1:0  # optional, this is the default

Alternatively, pass --env-file <path> to load a .env file from a different location.

Doctor Check

After configuring your environment, verify everything is set up correctly:

npx @opensearch-project/agent-health doctor

This checks OpenSearch connectivity, AWS credentials, and Bedrock access. Fix any reported issues before proceeding.


Commands

# Verify setup
npx @opensearch-project/agent-health doctor --output json

# Generate config files
npx @opensearch-project/agent-health init

# List available resources
npx @opensearch-project/agent-health list agents --output json
npx @opensearch-project/agent-health list test-cases --output json
npx @opensearch-project/agent-health list benchmarks --output json

# Run single test case
npx @opensearch-project/agent-health run -t <test-case-id> -a <agent-key> --output json

# Run full benchmark with export
npx @opensearch-project/agent-health benchmark -n <benchmark-name> -a <agent-key> --export results.json

# Import test cases from JSON file and run benchmark
npx @opensearch-project/agent-health benchmark -f ./test-cases.json -a <agent-key>

# Import with a custom benchmark name
npx @opensearch-project/agent-health benchmark -f ./test-cases.json -n "My Benchmark" -a <agent-key>

# Export test cases from a benchmark (produces import-compatible JSON)
npx @opensearch-project/agent-health export -b <benchmark-name> -o test-cases.json

Code-based test SDK (write tests as files)

Besides JSON test cases, Agent Health has an experimental code-based SDK for writing tests as .eval.js / .eval.ts files (Playwright-style test() / expect() / fixtures / judge() / lifecycle hooks). Run them the same way you run a JSON file:

# `-f` accepts BOTH JSON test-case files and code SDK (.eval.js / .eval.ts) files
npx @opensearch-project/agent-health benchmark -f ./examples/eval-files/demo.eval.js -a my-agent

They produce per-matcher results (matcherResults[]) instead of a single pass/fail. Full guide: docs/SDK.md.


Improvement Workflow

Step 0: Verify Setup

npx @opensearch-project/agent-health doctor --output json

Confirm all checks pass before proceeding. Fix any issues reported.

Step 1: Baseline Evaluation

npx @opensearch-project/agent-health benchmark -n "My Benchmark" -a my-agent --export baseline.json

Step 2: Analyze Failures

Read baseline.json and find entries where passFailStatus: "failed".

Key fields to examine:

  • matcherResults[*].reasoning - the canonical judge verdict + reasoning per matcher (llmJudgeReasoning is a legacy, deprecated shim carrying the same text)
  • improvementStrategies - Specific recommendations with priority
  • trajectory - Step-by-step agent execution

Distinguish failed (the agent answered, judge scored it below threshold) from errored (metricsStatus: "error" — the evaluator itself could not run, e.g. a judge validation error). Errored runs are excluded from pass-rate aggregation; don't treat them as agent misses.

Step 3: Fix Based on Strategies

Focus on priority: "high" issues first:

{
  "category": "Tool Usage",
  "issue": "Agent called search without time filter",
  "recommendation": "Always include start_time and end_time parameters",
  "priority": "high"
}

Read the agent's code and implement the recommendation.

Step 4: Verify Fix

npx @opensearch-project/agent-health benchmark -n "My Benchmark" -a my-agent --export after-fix.json

Compare passRate between baseline and after-fix.

Step 5: Iterate

Repeat until all high-priority issues are resolved.


Output Reference

Benchmark Export Structure

{
  "benchmark": { "id": "...", "name": "...", "testCaseCount": 10 },
  "runs": [{
    "agent": { "key": "my-agent", "name": "My Agent" },
    "passed": 7,
    "failed": 3,
    "passRate": 70,
    "reports": [{
      "testCaseId": "tc-001",
      "passFailStatus": "failed",
      "metricsStatus": "ready",
      "metrics": { "accuracy": 45 },
      "matcherResults": [
        { "method": "llm-judge", "description": "identifies the root cause", "pass": false, "score": 0.45, "reasoning": "The agent failed because..." }
      ],
      "llmJudgeReasoning": "The agent failed because...",
      "improvementStrategies": [{
        "category": "Tool Usage | Reasoning | Completeness",
        "issue": "What went wrong",
        "recommendation": "How to fix it",
        "priority": "high | medium | low"
      }],
      "trajectory": [
        { "type": "thinking", "content": "Agent's reasoning..." },
        { "type": "action", "toolName": "search", "toolArgs": {} },
        { "type": "tool_result", "content": "...", "status": "SUCCESS" },
        { "type": "response", "content": "Final answer..." }
      ]
    }]
  }]
}

Metrics, scoring, and run status

  • Metrics are evaluator-defined and heterogeneous. Only the RCA Default evaluator emits a metric named accuracy. Other evaluators emit their own metric names (tool_selection_accuracy, reasoning_coherence, bias_detection, …) and custom evaluators emit whatever their scoringConfig defines. Don't assume metrics.accuracy exists.
  • A run's overall score is the rounded mean of whatever numeric metrics the run's evaluator emitted (null/— when none), not a single "accuracy".
  • matcherResults[] is the canonical judge surface. Read the judge verdict
    • reasoning from matcherResults entries (method: 'llm-judge'). Each entry is a MatcherResult: { description, pass (boolean), score? (0–1), method, reasoning?, durationMs? }. llmJudgeReasoning is still populated as a backward-compatible shim but is deprecated.
  • Run status: passed / failed come from passFailStatus; a separate errored state (metricsStatus: 'error') means the evaluator could not run and is excluded from pass-rate denominators.

Tips

  1. Always use --output json for reliable parsing
  2. Use --export to get full reports with improvement strategies
  3. Fix high-priority issues first - they cause actual failures
  4. Compare trajectories between passing and failing cases
  5. Make incremental changes - one fix, then re-test
  6. Don't over-engineer - fix the specific issue identified

Troubleshooting

SymptomFix
npx agent-health not foundnpm install -g @opensearch-project/agent-health
"No agents configured"npx agent-health init (creates config with sample agents)
Server / config issuesnpx agent-health doctor (checks config + connectivity)
"OpenSearch storage not configured"Fine for local use — file-based storage is the default. Set OPENSEARCH_STORAGE_* only for shared / production persistence.

Server API Reference

The Agent Health server runs on port 4001 and exposes the following REST APIs. All endpoints return JSON unless noted (SSE endpoints return text/event-stream).

Health & Configuration

MethodPathDescription
GET/healthHealth check → { status: 'ok', version, service: 'agent-health' }
GET/api/agentsList all agents → { agents: AgentConfig[], total }
POST/api/agents/customAdd custom agent → { name, endpoint, connectorType?, useTraces? }
DELETE/api/agents/custom/:idRemove custom agent
GET/api/modelsList all models → { models: ModelConfig[], total }
GET/api/debugDebug status → { enabled: boolean }
POST/api/debugToggle debug → { enabled: boolean }

Agent Execution & Evaluation

MethodPathDescription
POST/api/agentProxy agent request (SSE) → { endpoint, payload, headers?, agentKey? }
POST/api/evaluateRun evaluation (SSE) → { testCaseId?, testCase?, agentKey, modelId }

Judge

MethodPathDescription
POST/api/judgeEvaluate a captured trajectory → { trajectory, expectedOutcomes? | expectedTrajectory?, modelId?, evaluatorId?, runId?, logs?, agents? } → JudgeResponse { passFailStatus, metrics, llmJudgeReasoning, improvementStrategies }
GET/api/judge/bedrock-modelsDiscover Bedrock judge models (ListInferenceProfiles)
GET/api/judge/openai-compatible-modelsList OpenAI-compatible models → { models, endpoint, configured } (renamed from /api/judge/litellm-models)
GET/api/judge/anthropic-modelsDiscover Anthropic-direct models (needs ANTHROPIC_API_KEY)
GET/api/judge/github-modelsDiscover GitHub Models / Copilot models (needs GITHUB_TOKEN)

On /api/judge, modelId is the judge model — the trajectory is already captured, so there is no agent invocation here, and /api/judge does not take a judgeModelId. The agent-vs-judge model distinction (where modelId is the agent's LLM and judgeModelId is the judge's) applies to the run endpoints — POST /api/evaluate and POST /api/storage/evaluation-runs — and the CLI (-m / --judge-model). Agentic-provider judges (pi / agent / agentic / claude-code) pick their own model and ignore the judge model id. The run-level matcherResults[] are assembled by the runner from the judge's response (see Output Reference); the raw /api/judge primitive returns llmJudgeReasoning.

Traces & Metrics

MethodPathDescription
POST/api/tracesFetch traces → { traceId?, runIds?, startTime?, endTime?, size? } → { spans, total, hasMore }
GET/api/traces/healthTraces health → { status: 'ok' | 'error' }
GET/api/metrics/:runIdRun metrics → { totalTokens, costUsd, durationMs, llmCalls, toolCalls }
POST/api/metrics/batchBatch metrics → { runIds: string[] } → { metrics[], aggregate }

Logs

MethodPathDescription
POST/api/logsFetch logs → { runId?, query?, startTime?, endTime?, size? } → { logs[], total }

Observability

MethodPathDescription
GET/api/observability/healthCheck observability data source health
POST/api/observability/test-connectionTest connection to observability cluster
GET/api/observability/defaultsGet default OTEL index patterns

Storage: Test Cases

MethodPathDescription
GET/api/storage/test-casesList test cases (latest versions). Query: ids?, fields?, size?, after?
GET/api/storage/test-cases/:idGet latest version of test case
GET/api/storage/test-cases/:id/versionsGet all versions
GET/api/storage/test-cases/:id/versions/:versionGet specific version
POST/api/storage/test-casesCreate test case (v1)
PUT/api/storage/test-cases/:idUpdate (creates new version)
DELETE/api/storage/test-cases/:idDelete all versions
POST/api/storage/test-cases/bulkBulk create

Storage: Benchmarks

MethodPathDescription
GET/api/storage/benchmarksList benchmarks. Query: fields?, size?
GET/api/storage/benchmarks/:idGet by ID. Query: fields?, runsSize?, runsOffset?
GET/api/storage/benchmarks/:id/exportExport test cases as JSON
POST/api/storage/benchmarksCreate → { name, description?, testCaseIds }
PUT/api/storage/benchmarks/:idUpdate
PATCH/api/storage/benchmarks/:id/metadataUpdate metadata
DELETE/api/storage/benchmarks/:idDelete
POST/api/storage/benchmarks/:id/executeExecute benchmark (SSE) → { runConfig: RunConfigInput }
DELETE/api/storage/benchmarks/:id/runs/:runIdDelete specific run
POST/api/storage/benchmarks/:id/cancelCancel execution
POST/api/storage/benchmarks/:id/refresh-all-statsRecompute all run stats

Storage: Runs (TestCaseRun)

MethodPathDescription
GET/api/storage/runsList runs. Query: size?, from?, fields?
GET/api/storage/runs/:idGet run by ID
POST/api/storage/runsCreate run
PATCH/api/storage/runs/:idUpdate run
DELETE/api/storage/runs/:idDelete run
POST/api/storage/runs/searchSearch with filters
GET/api/storage/runs/by-test-case/:testCaseIdRuns for test case
GET/api/storage/runs/by-benchmark/:benchmarkIdRuns for benchmark
GET/api/storage/runs/by-benchmark-run/:benchmarkId/:runIdResults for benchmark run
GET/api/storage/runs/iterations/:benchmarkId/:testCaseIdIterations for test case in benchmark
POST/api/storage/runs/:id/annotationsAdd annotation

Storage: Analytics

MethodPathDescription
GET/api/storage/analyticsQuery analytics. Filters: experimentId?, testCaseId?, agentId?, modelId?
GET/api/storage/analytics/aggregationsAggregated metrics
POST/api/storage/analytics/searchComplex search with aggregations

Storage: Reports

MethodPathDescription
GET/api/storage/benchmarks/:id/reportDownload report. Query: format? ('json'|'html'|'pdf')

Storage: Evaluators

MethodPathDescription
GET/api/storage/evaluatorsList evaluators (5 built-in system + custom)
GET/api/storage/evaluators/:idGet evaluator (latest version)
GET/api/storage/evaluators/:id/versionsList all versions (newest first)
GET/api/storage/evaluators/:id/versions/:versionGet a specific version snapshot
POST/api/storage/evaluatorsCreate custom evaluator
PUT/api/storage/evaluators/:idUpdate (creates a new immutable version)
DELETE/api/storage/evaluators/:idDelete custom evaluator (system evaluators are protected)

Storage: Evaluation Runs (code-based SDK)

MethodPathDescription
GET/api/storage/evaluation-runsList evaluation runs
GET/api/storage/evaluation-runs/:idGet evaluation run
POST/api/storage/evaluation-runsStart a run (SSE). Accepts sources: [{ type: 'code-import', filenames }] for .eval.js / .eval.ts SDK files, plus agentKey, modelId, judgeModelId?, evaluatorId?
POST/api/storage/evaluation-runs/:id/cancelCancel a run
POST/api/storage/evaluation-runs/:id/promotePromote a run

Comparison

MethodPathDescription
POST/api/comparison/cluster-failuresCluster regressed cases into named failure patterns (knowledge / tool_gap / reasoning / other)
GET/api/comparison/clusters/:clusterIdFetch a previously computed cluster by id

Storage: Admin

MethodPathDescription
GET/api/storage/healthStorage backend health
POST/api/storage/test-connectionTest storage connection
POST/api/storage/initInitialize indexes
GET/api/storage/config/statusConfig status
POST/api/storage/config/storageUpdate storage config
POST/api/storage/config/observabilityUpdate observability config

Assistant (NEW)

MethodPathDescription
POST/api/assistant/chatChat with AI assistant (SSE) → { sessionId, message, context } → { type: 'delta'|'done', content }
DELETE/api/assistant/session/:sessionIdClear session
GET/api/assistant/healthCheck assistant availability

UI Pages

The evaluation surface lives under /evaluations/*. The app uses BrowserRouter — plain paths, no # prefix.

PageRouteDescription
Dashboard/Overview with agent stats, recent runs, system health
Benchmarks/evaluations/benchmarksList benchmarks with pass rates, run counts, version badges
Benchmark Runs/evaluations/benchmarks/:id/runsRuns for a benchmark (split / tabs layout)
Run Inspector/evaluations/benchmarks/:id/runs/:runId/inspectPer-test-case results, trajectory, judge, traces
Test Cases/evaluations/test-casesList / create / edit / version test cases
Test Case Detail/evaluations/test-cases/:idDefinition + runs with an inline live-run panel
Eval Runs/evaluations/runs · /evaluations/runs/:idCode-import (SDK) and ad-hoc run results
Evaluators/evaluators · /evaluators/:idManage evaluators; version history + Git-style diff
Compare/compare/:benchmarkId?runs=a,bDiagnosis surface: verdict strip, first-divergence, failure clusters
Agent Traces/agent-tracesOpenTelemetry trace explorer (timeline / flow)
Coding Agents/coding-agentsCoding Agent Analytics (Claude Code / Kiro / Codex)
Settings/settingsConfigure agents, models, storage, observability connections
Assistant/assistantFull-page AI chat interface for help and analysis

Common Tasks

Ask about a benchmark's results

"What's the pass rate for benchmark bench-xxx? Which test cases are failing and why?" → The assistant will query /api/storage/benchmarks/:id and /api/storage/runs/by-benchmark/:id

Interpret judge reasoning

"Why did test case tc-xxx fail in run run-xxx? What should I fix?" → The assistant reads llmJudgeReasoning and improvementStrategies from the run

Write a test case

"Help me write a test case for testing log search with time filters" → The assistant creates a test case with prompt, context, expectedOutcomes, and labels

Analyze traces

"What are the most expensive LLM calls in run run-xxx?" → The assistant queries /api/traces and /api/metrics/:runId to find token-heavy spans

Compare runs

"Compare the results of run A vs run B in benchmark bench-xxx" → The assistant fetches both runs and diffs pass/fail status, accuracy, and strategies

Diagnose eval-vs-reality gaps

"My agent passes 8/9 in evals but fails in real life — why?" → The assistant investigates: fetches the runs and test cases, examines judge reasoning and expectedOutcomes, checks whether the evaluator is appropriate for the domain, and recommends tightening outcomes or creating a custom evaluator


Evaluator System

How Evaluation Works

  1. Agent runs against a test case → produces a trajectory (thinking, action, tool_result, response steps)
  2. The judge (LLM) receives the trajectory + expectedOutcomes and scores each outcome
  3. Each outcome: Fully achieved = 1.0, Partially = 0.5, Not achieved = 0.0
  4. accuracy = (sum of scores / total outcomes) × 100
  5. Pass/Fail determined by evaluator's passThreshold + critical failure checks

Built-in Evaluators

IDNameThresholdKey Metrics
system-rca-defaultRCA Default70%accuracy
system-factualityFactuality80%factual_accuracy, hallucination_rate, source_grounding
system-tool-usageTool Usage80%tool_selection_accuracy, redundant_calls, tool_ordering
system-reasoning-depthReasoning Depth75%reasoning_coherence, step_completeness, logical_validity
system-safetySafety90%safety_score, bias_detection, guardrail_adherence

Custom Evaluators

Create custom evaluators via the UI or API for domain-specific scoring:

{
  "name": "My Domain Evaluator",
  "systemPrompt": "You are evaluating an agent for [domain]. CRITICAL CRITERIA: ...",
  "scoringConfig": {
    "metrics": [
      { "name": "routing_accuracy", "weight": 0.4, "scale": 100 },
      { "name": "tool_correctness", "weight": 0.4, "scale": 100 },
      { "name": "diagnostic_completeness", "weight": 0.2, "scale": 100 }
    ],
    "passThreshold": 85,
    "scale": 100
  }
}

Assign to a benchmark run via evaluatorId in the run config.

When to Recommend Custom Evaluators

  • Domain has hard correctness rules: specific CLI commands, valid syntax, routing tables, API formats
  • Default judge is too charitable: agents pass evals but fail in real-world usage (eval-vs-reality gap)
  • Multiple independent dimensions: routing correctness AND tool syntax AND diagnostic completeness all matter
  • 70% threshold is too low: critical domains (oncall, security, compliance) need 85%+

Common Eval-vs-Reality Gap Causes

  1. expectedOutcomes describe results, not procedures: Judge rewards "got the right answer" without verifying "used the right method"
  2. 70% threshold too permissive: Missing 30% of steps may be catastrophic in practice
  3. No tool-correctness validation: Generic judge can't verify domain-specific tool syntax
  4. Trajectory compaction hides details: toolOutput truncated to 1000 chars, content to 500 chars — wrong commands may be invisible to judge
  5. Test cases cleaner than reality: Hand-authored prompts include hints that real incidents don't
  6. Single-turn evaluation: Real work is iterative; test cases may compress multi-step workflows

Fix Strategies (in order)

  1. Tighten expectedOutcomes: Add negative constraints ("do NOT use X"), require specific tool names, specify correct syntax
  2. Raise passThreshold: Set to 85%+ for critical domains
  3. Use system-tool-usage as secondary evaluator: Catches redundant/wrong tool calls
  4. Create domain-specific custom evaluator: Include explicit rubric criteria the generic judge can't verify
저장한 Skills