agentsclimarketplace

Ai evals

Skill Deadlymind/nanolama/skills/ai-evals

A portable Claude Code skills library (plugin) for a Django 5.2 + Next.js 16 house stack: 30 model-agnostic, tenant-isolation-and-security-first skills.

Install
npx -y skills add Deadlymind/nanolama --skill ai-evals

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Regression-tests agent and LLM behaviour with two layers that are never confused — a deterministic hard gate that runs on every CI build (assert which tool the agent routes to, must-NOT-call cases for destructive paths, tenant and RBAC enforcement through the tool layer, golden exact-value extractions) and an advisory LLM-as-judge graded set that tracks quality trend but never blocks a merge. Use when changing a prompt, model id, or tool schema, adding a golden set, wiring evals into CI, catching a silent agent regression, deciding whether an eval may gate a merge, or asking how to test that the model picks the right tool. Not for the agent loop or the write-tool safety contract itself (see ai-integration) or ordinary application tests (see write-tests).

SKILL.md

6.3 KB, as published. Nobody here has run it

AI evals (stop silent agent regressions)

When to use

Any change to a prompt, a model id, or a tool schema. All three are untyped, un-compiled inputs — nothing fails loudly when they break. A reworded system prompt can quietly stop the agent from routing to refund_tool, and every test still passes. Evals are how that change fails at merge time instead of in prod.

Pattern

Two layers, and never confuse them.

  1. Hard gate — deterministic, blocking. Repeatable assertions with a fixed expected value. Runs on every CI build, blocks the merge on red. Most of it makes no model call at all.
  2. Graded set — LLM-as-judge, advisory. A judge model scores answer quality on a rubric. Stochastic: same input, different score run to run. It reports a trend, it does not gate.

The rule, stated outright: never enforce safety with a check that can flake. A gate that goes red at random gets rerun until green, then ignored, then deleted — and it takes the real signal with it. If a property matters enough to block a merge, assert it deterministically.

What belongs in the hard gate

  • Routing / tool selection. Given a user turn, assert which tool the agent picks and with what arguments. Stub the model client and assert your dispatch layer — no API call, no key, no flake, milliseconds per case.
  • Must-NOT-call cases. The mirror image, and the one people forget: for a destructive or out-of-scope request the model must refuse the path. Assert the tool was never invoked. This is the case a friendly prompt tweak breaks.
  • Tenant and RBAC through the tool layer. Run the tool with tenant B's actor against tenant A's row and assert it returns nothing — the model layer must not become a way around the scoping the ViewSets enforce (see multi-tenancy).
  • Golden exact values. Extraction and calculation have one right answer. Pin a hand-verified fixture and assert == — the invoice total is "1250.00", never "roughly right".
# tests/evals/test_routing.py — hard gate: deterministic, no live model call.
GOLDEN = json.loads((Path(__file__).parent / "golden/routing.json").read_text())

@pytest.mark.parametrize("case", GOLDEN)          # golden set lives beside the prompts
def test_routes_to_expected_tool(case):
    client = StubClient(script=case["model_reply"])   # replay a recorded tool_use block
    calls = []
    tools = {name: recorder(name, calls) for name in TOOL_IMPLS}

    run_agent(case["messages"], TOOL_SCHEMAS, tools, user=case_user(case), client=client)

    called = [c.name for c in calls]
    if case["expect_tool"] is None:
        assert called == [], f"must NOT call a tool: {case['prompt']}"   # refusal case
    else:
        assert called == [case["expect_tool"]]                           # routing case
        assert calls[0].args == case["expect_args"]                      # golden args

def test_tool_layer_is_tenant_scoped(tenant_a_invoice, user_b):
    # the model layer is not a bypass around ViewSet scoping
    assert TOOL_IMPLS["get_invoice"](user=user_b, id=tenant_a_invoice.pk) is None

Running the graded set

Keep it as a separate, non-blocking CI job (or a nightly run) — it needs an API key and costs money per run. Score each answer on a small rubric (grounded in the retrieved context, answers the question, correct refusal), record the score, and watch the trend across runs, not any single number. A drop is a prompt to go look, not an automatic red build. Report it; never let it fail the pipeline.

When to run them

Whenever the prompt text, the model id, or a tool schema changes — those are the three inputs the type system cannot see. Wire the hard gate into the same CI job as the unit suite so it cannot be skipped (see ci-cd), and re-run the graded set before a model upgrade ships, comparing old id against new on the same set.

Adapt to your repo

Keep the golden set in the repo, next to the prompts it testsprompts/ and tests/evals/golden/ move together in one commit, so a reviewer sees the prompt diff and the expected-behaviour diff side by side. Version the prompt (a file per version, or a PROMPT_VERSION constant recorded in each eval run) so a score change maps to a known revision. Rename entreprise, the tool names, and the actor accessor to match your project; swap StubClient for whatever your SDK makes stubbable — a recorded reply object is enough, do not invent a fake API.

Gotchas

  • A live model call in the blocking gate makes the gate stochastic — and one red build from randomness teaches the team to rerun CI until green.
  • Judge scores are not comparable across judge-model versions. Pin the judge id and note it with the scores, or the trend line is meaningless.
  • The must-NOT-call cases are the ones that rot: nobody notices when a refusal quietly becomes a call. Add one for every destructive tool you expose.
  • An eval set that only holds happy-path prompts passes forever. Include the ambiguous, the out-of-scope, and the adversarial/injected turn.
  • Never fix a red gate by loosening the assertion to in or a fuzzy match — that is deleting the test with extra steps.
  • Golden fixtures must be hand-verified, never captured from the model's own output — recording a wrong answer as golden freezes the bug in place.
  • Evals prove the model's behaviour, not the tool's blast radius. A correctly routed call to an unsafe tool still ships a breach (see ai-integration).

See also

  • ai-integration
  • write-tests
  • ci-cd
  • verify

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.