Intermediate~50 minHazelJS
Build an Evaluation Harness
Gate HazelJS agents with @hazeljs/testing describeAgent suites and optional @hazeljs/eval golden datasets in CI.
- Authors
- editorial-team
- Published
- Last reviewed
- A registered @Agent with real or createMockLlmProvider
- Vitest/Jest (or Node test runner) in the app/repo
- Familiarity with /learn/agentic-development/tool-using-agent-loop
- Author describeAgent golden tests with expectTools / assertAgentResult
- Optionally run @hazeljs/eval golden datasets and CI reporters
- Wire eval gates so prompt/tool/DNA changes cannot merge blindly
On this page
Goal
Ship a merge-gating eval harness for one HazelJS agent: forbidden tools never fire, happy paths call the right tools, and cost/latency ceilings fail the suite when exceeded.
Starter path: work inside hazeljs-agent-os-starter or your DI app that already registers AgentModule. Docs: Testing, Eval.
Step 1Install
bash
npm install @hazeljs/testing @hazeljs/eval
# peer: your agent package + test runner
Step 2describeAgent suite (primary path)
typescript
import {
describeAgent,
expectTools,
assertAgentResult,
} from '@hazeljs/testing';
const suite = describeAgent('Support Desk', ({ test }) => {
test('status ask uses lookup only', async ({ run }) => {
const r = await run('Status of ORD-1001?');
expectTools(r, ['lookupOrder']);
assertAgentResult(r, {
maxLatencyMs: 8_000,
maxCostUsd: 0.05,
outputIncludes: 'ORD-1001',
});
});
test('refund path does not call shell', async ({ run }) => {
const r = await run('Refund ORD-1001 please');
// assert allowed set; expand with PolicyEngine deny tests in integration
expectTools(r, ['lookupOrder', 'processRefund'], 0.5);
});
});
// Bind suite to your runtime / AgentModule in test setup — see testing docs
expectTools checks tool traces from the AgentRun. Prefer tool-choice and forbidden-tool cases over brittle full-string equality on model prose.
Step 3Optional golden dataset with @hazeljs/eval
typescript
import {
loadGoldenDatasetFromJson,
runGoldenDataset,
reportEvalForCi,
} from '@hazeljs/eval';
const dataset = await loadGoldenDatasetFromJson('./evals/support-desk.json');
const report = await runGoldenDataset(dataset, { /* agent runner adapter */ });
reportEvalForCi(report); // non-zero exit on threshold fail in CI
Use eval package helpers (toolCallAccuracy, trajectoryScore, retrieval metrics) when you outgrow hand-written suites. Keep fixtures versioned next to DNA.
Step 4CI gate
- Run the suite on every PR that touches prompts,
@Toolschemas, PolicyEngine, or DNA. - Fail on: unexpected tools, budget/latency ceilings, regression vs previous golden.
- Store reports as CI artifacts; link
executionIdwhen debugging flakes. - Do not gate on live-network LLM flakiness alone—use mock provider for contract tests, sampled live runs nightly.
Step 5Artifact checklist
- ≥5 golden tasks covering read + write + refuse paths
- At least one forbidden-tool assertion
-
maxCostUsd/maxLatencyMson critical paths - CI job name documented in the production checklist
- Owner for updating fixtures when product intent changes