Repository navigation
feat(eval): reproducible incident benchmark for the agent - #57
Merged
Merged
Conversation
Adds demos/eval/ — a small framework for running the agent against known-bad AWS/Azure scenarios and scoring its submit_investigation output against ground truth. Foundation for the README's "X/N root causes found, $Y, Zs median" headline and a regression net every PR can run against. Layout - demos/eval/run.py — discover scenarios, run end-to-end (setup -> POST /chat with SSE consumption -> teardown), print a table, write a Markdown report. Single-file CLI, stdlib only. - demos/eval/scoring.py — pure scoring functions (root-cause category match, evidence-keyword overlap with a 50% threshold, services intersection with AWS/Amazon prefix tolerance, expected-tools any-of). Side-effect-free so the scoring contract is unit-testable. - demos/eval/scenarios/ — one directory per scenario with a single scenario.json that points at an existing demo script + adds prompt and ground truth. Three to start: * 001_lambda_crashing (e2_lambda_crashing -> KeyError on user_id) * 002_lambda_throttled (e1_lambda_throttled -> reserved concurrency 0) * 003_azure_vm_deallocated (vm_deallocated -> powerState deallocated) - demos/eval/README.md — running, adding scenarios, ground-truth schema, cost guard, and the read-only-runtime vs write-capable-setup pattern. - Makefile: make eval target. Design choices worth knowing - Multi-category ground truth: root_cause_category accepts a string OR a list. Real incidents are often defensibly classifiable two ways (a Lambda crashing on a missing key is both COMPONENT_FAILURE and SYSTEM_CHANGE); the scorer shouldn't punish a defensible call. Documented in the README. - --setup-profile flag: the agent runtime should be read-only (least privilege, what the docs ship), but eval setup/teardown needs write permissions. The flag rewrites AWS env for setup/teardown subprocesses ONLY; the agent /chat keeps the original (read-only) creds. - Auto .env loader: reads apps/backend/.env (and a repo-root .env if present) into os.environ at startup so demo subprocesses inherit AWS_PROFILE / AWS_ACCESS_KEY_ID without manual shell exports. - Per-run subprocess logs in demos/eval/results/<timestamp>/ so the actual cloud error is never lost to terminal truncation. Testability - apps/backend/tests/test_eval/test_scoring.py — 19 unit tests pinning the scorer contract (correct pass, wrong category, multi-category list, missing log inspection, generic evidence, soft-metric preservation on failure). Total suite 232 green. First end-to-end run: 1/1 PASS, 45.6s, $0.0014 on Lambda-crashing.
- apps/documentation/eval.md — how to run the eval, the read-only-runtime vs write-capable-setup pattern, scenario authoring, ground-truth schema, scoring rules, the model-choice caveat (smaller models skip the structured submit_investigation step and fail the eval). - .gitignore: demos/eval/results/. The per-run output (report.md + subprocess logs) is per-machine and shouldn't be in version control. - Remove the four accidentally-committed test runs from the previous commit on this branch.
…enario
Brings the suite from 3 to 10 scenarios, all reusing existing demos/aws
scripts. Each is a single scenario.json — no Python duplication. Ground
truth uses the multi-category list pattern wherever an incident is
defensibly classifiable two ways (most of them are).
New scenarios
- 004 alarm_in_alarm (e3 -> alarm in ALARM state)
- 005 lambda_dynamodb_throttle (h1 -> cross-service: Lambda errors caused
by DynamoDB ProvisionedThroughputExceeded)
- 006 iam_permission_regression (h2 -> AccessDenied after DetachRolePolicy)
- 007 lambda_vpc_no_egress (h3 -> Lambda in VPC, security group has no egress)
- 008 bad_deploy (m1 -> errors correlate with UpdateFunctionCode)
- 009 timeout_misconfig (m2 -> timeout lowered to 1s)
- 010 ecs_image_pull (m3 -> CannotPullContainerError on bad image tag)
Planned (designed, not yet executable)
- demos/eval/scenarios/_planned/011_aks_dns_resolution.md — AKS pods can't
pull from ACR because the cluster's VNet isn't linked to the private DNS
zone. Real-world enterprise Azure case described by a practicing SRE.
Setup script TODO (AKS provisioning is 10-15 min so not blind-scaffolded).
Underscore prefix on the directory keeps the runner from picking it up.
The runner discovers only directories containing scenario.json directly, so
_planned/* stays out of the suite until the demo script is implemented.
10 scenarios discovered + 0 broken stubs in the active suite.
…benchmark Make the incident benchmark fair across models and publish the results. - run.py: recover a submit_investigation payload from the final message text when a model emits it as JSON instead of calling the tool (gpt-oss); tag such runs in the report. Takes gpt-oss-120b from 3/10 to 9/10. - 004 alarm_in_alarm: back the alarm with a real failing Lambda (regressed pricing -> TypeError) so the root cause is genuinely discoverable, instead of a forced-state alarm over a synthetic metric with no resource behind it. - chat.py: extract text from list-form content blocks, capture reasoning tokens, and guard list-shaped tool results. - scoring/scenarios: match services against the summary, accept multi-category ground truth, calibrate keywords. - docs: new benchmark.md (KPIs, ROI, cost-at-scale) + README headline table; eval.md notes the salvage fallback. - tests: pin the salvage parser and the loosened scoring contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
demos/eval/— a small framework that runs the agent against known-bad AWS / Azure scenarios and scores itssubmit_investigationoutput against ground truth. Foundation for the README's "X/N root causes found, $Y/run, Zs median" line and a regression net every PR can run against.Verified: 1/1 PASS on a real Lambda-crashing investigation (45.6s, $0.0014, 11 tool calls).
What's in the box
demos/eval/run.py— discover scenarios, run end-to-end (setup → POST/chatwith SSE consumption → score → teardown), print a table, write a Markdown + JSON report. Single-file CLI, stdlib only.demos/eval/scoring.py— pure scoring functions (category match, evidence-keyword overlap with 50% threshold, services intersection with AWS/Amazon prefix tolerance, expected-tools any-of). Side-effect-free so the contract is unit-testable.demos/eval/scenarios/— one directory per scenario with a singlescenario.jsonthat points at an existing demo script + adds prompt and ground truth. Three to start:001_lambda_crashing(e2_lambda_crashing → KeyError on user_id)002_lambda_throttled(e1_lambda_throttled → reserved concurrency 0)003_azure_vm_deallocated(vm_deallocated → powerState deallocated)demos/eval/README.md— implementation reference (how scenarios compose, scoring internals).apps/documentation/eval.md— user-facing guide (running, authoring scenarios, model-choice caveats).apps/backend/tests/test_eval/test_scoring.py— 19 unit tests pinning the scorer contract. Total suite: 232 green.Makefile:make evaltarget.Design choices worth knowing
root_cause_categoryaccepts a string OR a list. Real incidents are often defensibly classifiable two ways (a Lambda crashing on a missing key is bothCOMPONENT_FAILUREandSYSTEM_CHANGE); the scorer shouldn't punish a defensible call. First run uncovered exactly this — see lesson baked into the docs.--setup-profileflag: the agent runtime should be read-only (least privilege, whatiam_setup.mdships), but eval setup/teardown needs write perms. The flag rewrites AWS env for setup/teardown subprocesses ONLY; the agent/chatkeeps the original (read-only) creds..envloader: readsapps/backend/.env(and a repo-root.envif present) intoos.environat startup so demo subprocesses inheritAWS_PROFILE/ keys without manual shell exports.demos/eval/results/<timestamp>/so a cloud error (AccessDeniedException, etc.) is never lost to terminal truncation.results/is gitignored.Test plan
cd apps/backend && uv run pytest tests/test_eval/— 19 passingcd apps/backend && uv run pytest— 232 passingopenrouter/openai/gpt-4o-minion001_lambda_crashing— PASS in 45.6s, $0.0014002,003) — follow-up, low riskWhat this unlocks