Find out whether your AI agent's evals are lying to you: stability reports with flip attribution, LLM judge audits, and deterministic checks mined from your KB.
Project description
CIAgent
Your eval score is stable. Your system isn't.
CIAgent tells you whether your agent's evals are lying to you:
- Run the suite N times — see which verdicts flip, with the blame attached
(
agent-variance→ fix the agent,judge-flake→ fix the eval) - Audit your LLM judge against ground truth you already have
- Replace judge calls with deterministic fact-checks mined from your knowledge base
Built from a real study: 1 in 7 answers an LLM judge passed were wrong, and deterministic checks caught 8 of 8 — methodology and raw numbers in STUDY.md. Native adapters for OpenAI, Anthropic, and LangGraph; imports exported OTel, LangSmith, and Langfuse traces from any stack. Runs inside pytest.
See It in 30 Seconds
No install, no API keys, no config — one command runs a bundled demo suite three times on synthetic traces:
uvx ciagent test --mock --runs 3
Run 1/3: 7/8 passed
Run 2/3: 7/8 passed
Run 3/3: 7/8 passed
────────────────────────────────────────────────────────────
Stability Report
────────────────────────────────────────────────────────────
Suite score across 3 runs: 88% / 88% / 88% ← looks stable
⚠️ FLAKY — 3/8 queries flipped verdicts across runs: ← is not
"What's your return window?" ❌✅✅ pass_rate=0.67 source: agent-variance
"Do you ship internationally?" ✅❌✅ pass_rate=0.67 source: agent-variance
"How do I reset my password?" ✅✅❌ pass_rate=0.67 source: agent-variance
Flip sources: 3 agent-variance (fix the agent) │ 0 judge-flake (fix the eval)
The aggregate score is identical every run. Three of the eight verdicts underneath it flipped. A single-run eval score would never tell you — the stability report does, and attributes every flip to the layer that caused it. (The demo simulates a flaky agent; point it at your own with the spec below.)
A stable score is not a stable system
Run the identical eval three times and you can get 96% / 95% / 96% — rock solid — while
individual queries flip verdicts every run. The aggregate holds because the errors move
around. --runs N shows what a single run can't:
ciagent test --runs 3
Run 1/3: 18/19 passed
Run 2/3: 18/19 passed
Run 3/3: 18/19 passed
────────────────────────────────────────────────────────────
Stability Report
────────────────────────────────────────────────────────────
Suite score across 3 runs: 95% / 95% / 95%
⚠️ FLAKY — 2/19 queries flipped verdicts across runs:
"What's your return window?" ✅❌✅ pass_rate=0.67 source: agent-variance (answer changed)
"Do you sell gift cards?" ❌✅✅ pass_rate=0.67 source: judge-flake (same answer, verdict flipped)
Flip sources: 1 agent-variance (fix the agent) │ 1 judge-flake (fix the eval) │ 0 infra-error (retry) │ 0 mixed
Stability verdict: FLAKY
Every flip is attributed to its source, so it's a routed work item, not a scary number: agent-variance means the agent produced different output (fix the prompt, retrieval, or temperature); judge-flake means the output — or every deterministic check's outcome — was identical but the LLM judge changed its mind (fix the rubric, or replace the judge with a deterministic check); infra-error means a judge API call failed (retry, fix nothing). Attribution is structural, not guessed: deterministic checks cannot flip on identical output, and per-layer sub-verdicts are compared across runs. The console shows observed facts; pass@k/pass^k estimates live in the JSON output, labeled as estimates.
Flaky-but-passing exits 0 so adoption won't break your CI; add --fail-on-flaky when
you're ready to gate on it. Try it with zero API keys:
AGENTCI_MOCK_FLAKY=1 ciagent test --mock --runs 3. Details: docs/stability.md.
Audit the judge itself
An LLM judge that shares your agent's context inherits your agent's blind spots: when
retrieval comes up empty, the agent answers from nothing — and the judge, reading the same
nothing, passes it. judge-audit measures your judge against ground truth you already have,
by re-scoring recorded baselines (the agent is never re-run):
ciagent judge-audit
- Judge vs. deterministic checks — the disagreement matrix. The row that matters: answers the judge PASSED that a hard fact-check FAILED.
- Retest stability — the same answer judged
--repeatstimes; flips on identical input are the judge's own noise floor. - Hand labels (
--labels) — agreement + Cohen's κ against your own review.
The claim is deliberately one-directional: a judge that fails where you can check it
shouldn't be trusted where you can't. Verdict: TRUSTWORTHY / NEEDS CALIBRATION /
UNRELIABLE. Details: docs/judge-audit.md.
Check facts in code. Save the judge for judgment.
Most agent failures that matter involve a hard fact — a product name, a price, a version number. Those are checkable deterministically, for free. And an LLM judge grading against the same context as your agent inherits your agent's blind spots: when retrieval comes up empty, the agent answers from nothing and the judge — reading the same nothing — passes it.
So CIAgent runs deterministic checks first and treats the judge as the last resort, not the default:
- Fact checks in code —
expected_in_answer,not_in_answer,regex_match,json_schema. Zero LLM calls, zero flakiness, same verdict every run. - Path checks — did the agent call the tools it should have? A missing expected tool warns; a forbidden tool fails.
- Cost budgets — LLM calls, tokens, dollars per query.
- LLM judge (
llm_judgerubrics, optional) — only for answers that genuinely need judgment, evaluated after every deterministic check has run.
Don't write the fact checks by hand — mine them from your knowledge base:
ciagent generate-checks
It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertions, and validates every candidate against your recorded golden answers first — a check that would fail a known-good answer is rejected before you ever see it. One LLM call at authoring time; the checks run free forever. Details: docs/generate-checks.md.
Add to Your Project
pip install ciagent
Write your golden queries — what should your agent handle, and what should it refuse?
# agentci_spec.yaml
agent: my-agent
# runner: any function that takes a query string and returns a response
runner: my_app.agent:run_for_agentci
queries:
- query: "How do I install CIAgent?"
correctness:
any_expected_in_answer: ["pip install", "ciagent"]
path:
expected_tools: [retrieve_docs]
retrieval:
tool: retrieve_docs # assert on what the retriever actually returned
forbid_empty: true # empty retrieval + confident answer = ungrounded
expected_sources: [install.md]
cost:
max_llm_calls: 8
- query: "What's the CEO's favorite restaurant?"
correctness:
not_in_answer: ["restaurant", "favorite"]
path:
expected_tools: [] # expect no tools called for out-of-scope queries
Run:
ciagent test --mock # start here: zero-cost with synthetic traces
ciagent test # run live against your real agent
ciagent test evaluates each query through 4 layers — correctness, path, retrieval, and cost. The retrieval layer reads the retriever tool's captured result and warns on empty retrievals, missing sources, and count floors — deterministically, and it SKIPs (never guesses) when a result wasn't captured or doesn't parse:
============================================================
Query: How do I install CIAgent?
Answer: To install CIAgent, you can use pip with the following command:
pip install ciagent. Make sure you have Python 3.10 or later.
✅ CORRECTNESS: PASS
✓ Found keywords: "pip install ciagent"
✓ LLM judge passed (score: 5 ≥ 0.6)
📈 PATH: PASS
✓ Tool recall: 1.000 (expected: [retrieve_docs])
✓ Tool precision: 0.500
✓ No loops detected
💰 COST: PASS
✓ LLM calls: 8 ≤ max 8
============================================================
Query: What Python version does CIAgent require and what frameworks does it support?
Answer: CIAgent currently does not specify a required Python version
in the provided context, so I don't have that information...
❌ CORRECTNESS: FAIL
• Expected '3.10' not found in answer
📈 PATH: PASS
✓ Tool recall: 1.000 (expected: [retrieve_docs])
✓ Loops: 1 ≤ max 3
💰 COST: PASS
✓ LLM calls: 4 ≤ max 5
============================================================
Don't have golden queries yet? ciagent init --generate scans your code and generates a starter spec.
Let your coding agent set it up
CIAgent ships as a Claude Code plugin. Two skills: onboard (writes the runner, records golden baselines, generates the spec, verifies it) and check (runs the right test after every change to your agent and routes failures by flip source).
/plugin marketplace add suniel12/ciagent
/plugin install ciagent@ciagent
Then ask your coding agent to "set up CIAgent for this repo." It records goldens with
ciagent bootstrap --yes and verifies with ciagent test --runs 3 — no human CLI use
needed. The runner it writes is one function: (query: str) -> str; trace capture is
automatic.
Demo
Here's a RAG agent demo where someone "optimizes for latency" by reducing retriever docs from 8 to 1. CIAgent catches the correctness regression:
CLI
ciagent init --generate # Scan project, generate test spec
ciagent init # Generate GitHub Actions workflow + pre-push hook
ciagent test --mock --yes # Zero-cost synthetic traces, CI-friendly (no keys, no prompts)
ciagent test # Run 3-layer evaluation (correctness → path → cost)
ciagent test --runs 3 # Stability report: verdict flips + flip-source attribution
ciagent judge-audit # Audit the LLM judge against checks, retests, hand labels
ciagent generate-checks # Mine KB facts into deterministic assertions (gated)
ciagent test --format html -o report.html # HTML report with per-query details
ciagent calibrate # Measure real agent metrics, auto-tune spec budgets
ciagent doctor # Health check: spec, deps, API keys
ciagent record <test> # Record golden baseline
ciagent diff # Diff against baseline
ciagent report -i results.json # Generate HTML report from JSON results
Docs
- Quickstart — install to first green run
- Stability testing —
--runs N, flip-source attribution - Judge audit — is your LLM judge lying to you?
- Generate checks — mine KB facts into gated assertions
- Writing tests — the full spec reference
- Cost tracking — budgets and spike detection
- Golden traces — record baselines, diff regressions
- Import production traces — turn an exported OTel/LangSmith/Langfuse trace into a gated regression test
- CI/CD integration — GitHub Actions setup
- LangGraph — graph-based agent support
- Metrics reference — every metric, defined
Why not just an LLM judge?
Judge-only evals are expensive, flaky, and blind to their own context. CIAgent is pytest-native regression testing: deterministic checks catch the factual failures, golden traces catch behavioral drift, cost budgets catch spend regressions — and the judge handles only what genuinely needs judgment. Mock mode (ciagent test --mock) runs the whole suite with zero API keys and zero cost, so it can gate every PR.
Contributing
GitHub Issues · DemoAgents — working examples for OpenAI, Anthropic, and LangGraph agents
Apache 2.0. If you build an agent and test it with CIAgent, I'd love to hear about it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ciagent-0.10.0.tar.gz.
File metadata
- Download URL: ciagent-0.10.0.tar.gz
- Upload date:
- Size: 182.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e141f760586977f5a5a98fea9bcea2bd045503fb32a974d42166a9d3079d501
|
|
| MD5 |
b54531738dbe9a4f9d72f2c577fd4693
|
|
| BLAKE2b-256 |
7613dd60530da90f6ed71971c57a81767491cab60d0b96fc729c21f93cf52f44
|
Provenance
The following attestation bundles were made for ciagent-0.10.0.tar.gz:
Publisher:
release.yml on suniel12/ciagent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ciagent-0.10.0.tar.gz -
Subject digest:
1e141f760586977f5a5a98fea9bcea2bd045503fb32a974d42166a9d3079d501 - Sigstore transparency entry: 2104891229
- Sigstore integration time:
-
Permalink:
suniel12/ciagent@7e2ed7fda2fd71e92d708b352c4afa60e3e98d6d -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/suniel12
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7e2ed7fda2fd71e92d708b352c4afa60e3e98d6d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ciagent-0.10.0-py3-none-any.whl.
File metadata
- Download URL: ciagent-0.10.0-py3-none-any.whl
- Upload date:
- Size: 204.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e3158f793c6a4f57b905098ec3b1230b3fc6891f0b01c1e111e2451beab906c
|
|
| MD5 |
57fd7d628cb109aefeaf1a61726abdf4
|
|
| BLAKE2b-256 |
721e28eab07c3da85c239414e974b8b1b663d6ce5fee0b8bfdd0d4a6eccc0f6a
|
Provenance
The following attestation bundles were made for ciagent-0.10.0-py3-none-any.whl:
Publisher:
release.yml on suniel12/ciagent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ciagent-0.10.0-py3-none-any.whl -
Subject digest:
3e3158f793c6a4f57b905098ec3b1230b3fc6891f0b01c1e111e2451beab906c - Sigstore transparency entry: 2104891298
- Sigstore integration time:
-
Permalink:
suniel12/ciagent@7e2ed7fda2fd71e92d708b352c4afa60e3e98d6d -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/suniel12
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7e2ed7fda2fd71e92d708b352c4afa60e3e98d6d -
Trigger Event:
push
-
Statement type: