ciagent 0.7.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ciagent-0.7.0 → ciagent-0.9.0}/CHANGELOG.md +142 -1
- {ciagent-0.7.0 → ciagent-0.9.0}/PKG-INFO +150 -95
- {ciagent-0.7.0 → ciagent-0.9.0}/README.md +145 -90
- {ciagent-0.7.0 → ciagent-0.9.0}/pyproject.toml +12 -11
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/langgraph.py +25 -4
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/openai_agents.py +2 -2
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/assertions.py +8 -8
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/baselines.py +11 -7
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/capture.py +155 -32
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/cli.py +596 -73
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/config.py +1 -1
- ciagent-0.9.0/src/ciagent/conversation.py +166 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/diff_engine.py +47 -8
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/__init__.py +1 -1
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/check_generator.py +3 -3
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/correctness.py +3 -3
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/cost.py +3 -3
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/cost_estimator.py +92 -1
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/diff.py +146 -16
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/judge.py +1 -1
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/judge_audit.py +83 -7
- ciagent-0.9.0/src/ciagent/engine/mock_runner.py +256 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/parallel.py +8 -8
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/path.py +7 -7
- ciagent-0.9.0/src/ciagent/engine/persona.py +141 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/reporter.py +46 -7
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/results.py +17 -2
- ciagent-0.9.0/src/ciagent/engine/retrieval.py +473 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/runner.py +28 -9
- ciagent-0.9.0/src/ciagent/engine/simulate.py +514 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/span_assertions.py +6 -6
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/stability.py +202 -7
- ciagent-0.9.0/src/ciagent/examples/__init__.py +3 -0
- ciagent-0.9.0/src/ciagent/examples/demo_spec.yaml +73 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/loader.py +4 -4
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/models.py +1 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/runner.py +3 -3
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/agentci_spec.schema.json +329 -7
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/generate_schema.py +2 -2
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/spec_models.py +194 -6
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/github_action.yml.j2 +4 -4
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/report.html.j2 +3 -3
- ciagent-0.7.0/src/agentci/engine/mock_runner.py +0 -111
- {ciagent-0.7.0 → ciagent-0.9.0}/.gitignore +0 -0
- {ciagent-0.7.0 → ciagent-0.9.0}/LICENSE +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/__init__.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/__init__.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/base.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/generic.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/cost.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/metrics.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/exceptions.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/mocks.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/py.typed +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/pytest_plugin.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/report.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/__init__.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/__init__.py +0 -0
- {ciagent-0.7.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/pre_push_hook.sh.j2 +0 -0
|
@@ -5,7 +5,148 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.9.0] - 2026-07-06
|
|
9
|
+
|
|
10
|
+
### Added — retrieval layer 2.5: `retrieval:` assertions (F4, F6 Phase 4)
|
|
11
|
+
- New `retrieval:` block on queries and scenario `per_turn:`/`outcome:`
|
|
12
|
+
checks — deterministic assertions on the retriever tool's captured
|
|
13
|
+
`ToolCall.result`: `forbid_empty` (empty retrieval + confident answer →
|
|
14
|
+
WARN "ungrounded answer"; a refusal passes), `min_results`,
|
|
15
|
+
`expected_sources`, `facts_in_context` (informational-only in v1),
|
|
16
|
+
`result_format` hint, `empty_markers`/`refusal_markers` overrides
|
|
17
|
+
- Binding result-interpretation contract: the layer SKIPs on uncaptured or
|
|
18
|
+
unparseable results — it never guesses. Empty means None/[]/""/whitespace
|
|
19
|
+
plus spec markers. Retrieval never hard-fails (WARN tier, like path/cost)
|
|
20
|
+
- Fifth stability flip source `retrieval-variance` (single-turn and
|
|
21
|
+
scenario): verdict flipped, same tool sequence, retrieved set differed →
|
|
22
|
+
blames the retriever, not the prompt; attribution skips when any run's
|
|
23
|
+
retriever output wasn't captured
|
|
24
|
+
- Judge audit gains a "judged against EMPTY retrieval" row (console + JSON):
|
|
25
|
+
the judge graded an answer whose ground truth was already lost
|
|
26
|
+
- Diff engine gains `RETRIEVAL_CHANGED`: retrieved source set changed vs
|
|
27
|
+
golden, emitted only when a source set is extractable from both traces
|
|
28
|
+
- Mock runner synthesizes retrieval results satisfying the spec — the
|
|
29
|
+
zero-key path exercises layer 2.5 end to end
|
|
30
|
+
- Adapter capture verification and fixes: langgraph now pairs `ToolMessage`
|
|
31
|
+
outputs onto tool calls by `tool_call_id`; the openai/anthropic
|
|
32
|
+
monkey-patches backfill `ToolCall.result` from the next request's
|
|
33
|
+
`role="tool"` messages / `tool_result` blocks (openai-agents adapter
|
|
34
|
+
already captured results)
|
|
35
|
+
|
|
36
|
+
### Added — generative personas, cost guardrails, stability (F6 Phase 3)
|
|
37
|
+
- Scenarios with `persona:`/`goal:` and no `turns:` get user turns from a
|
|
38
|
+
persona LLM (cheap haiku-class default, `persona_config` spec key) — the
|
|
39
|
+
finder path. Termination rules unchanged: max_turns / stop_when only; a
|
|
40
|
+
derailed persona (empty/unusable output) marks the scenario infra-error
|
|
41
|
+
with partial turns kept rather than silently grading the agent
|
|
42
|
+
- `--max-cost` session budget: hard-aborts mid-conversation at the next turn
|
|
43
|
+
boundary; partial results are clearly marked and the outcome verdict is
|
|
44
|
+
never evaluated on a partial conversation. Pre-run cost estimate with a
|
|
45
|
+
confirm gate; per-scenario cost line
|
|
46
|
+
- `--runs N` scenario stability with new `simulation-variance` flip source:
|
|
47
|
+
the simulated user said different things across runs — the persona varied,
|
|
48
|
+
not the agent. `--workers` runs scenarios in parallel (turns stay
|
|
49
|
+
sequential); a mock persona gives a zero-key generative path
|
|
50
|
+
|
|
51
|
+
### Added — record + replay: found bug → regression test (F6 Phase 2)
|
|
52
|
+
- `--record` / `--record-dir`: save any driven conversation as a golden
|
|
53
|
+
envelope at `<baseline_dir>/<agent>/scenarios/<slug>.json`. Recording
|
|
54
|
+
never prechecks — a FAILED scenario records too (`checks_passed: false`),
|
|
55
|
+
because converting a found bug into a regression test is the point
|
|
56
|
+
- `--replay`: recorded user turns are fed back verbatim (the persona is
|
|
57
|
+
never consulted); the scenario spec is embedded in the envelope so goldens
|
|
58
|
+
are self-contained. Replaying a deterministic agent twice yields
|
|
59
|
+
byte-identical verdicts (the `scenario_verdict` contract)
|
|
60
|
+
- Conversation-aware diff (`diff_envelopes`): per-turn tool-sequence and
|
|
61
|
+
answer changes between a golden conversation and a fresh run
|
|
62
|
+
|
|
63
|
+
### Added — `ciagent simulate` Phase 1: scripted multi-turn scenarios (F6)
|
|
64
|
+
- New `scenarios:` spec block: `turns:` (scripted user messages), `max_turns`,
|
|
65
|
+
`per_turn:` checks (evaluated on every turn), `outcome:` checks (evaluated
|
|
66
|
+
once at the END as the verdict — never a stop condition), and explicit
|
|
67
|
+
`stop_when: {tool_called: X}` early exit. Termination is deterministic only:
|
|
68
|
+
script exhausted, max_turns, or the stop_when event — no judge, no keywords
|
|
69
|
+
- New `conversation_runner:` spec key — `(messages: list[{role, content}]) →
|
|
70
|
+
str | Trace`, history passed explicitly, same capture/coercion as `test`
|
|
71
|
+
- `ciagent simulate` CLI: `--mock` runs scenarios on synthetic traces with
|
|
72
|
+
zero API keys (the CI path); live runs confirm turn count first. Exit codes:
|
|
73
|
+
0 pass / 1 outcome or per-turn correctness failure / 2 config or agent error
|
|
74
|
+
- Agent exception mid-conversation marks the scenario `infra-error` and keeps
|
|
75
|
+
the completed turns; conversations convert to schema_version-2 envelopes
|
|
76
|
+
- Spec change: `queries:` is no longer required when `scenarios:` is present
|
|
77
|
+
(at least one of the two must exist)
|
|
78
|
+
- 18 new tests, incl. the ADR checklist items: both termination causes,
|
|
79
|
+
agent-raises-mid-conversation, per_turn + outcome evaluation, and
|
|
80
|
+
outcome-never-stops (a turn-1 keyword match must not end the scenario)
|
|
81
|
+
|
|
82
|
+
### Added — schema_version + conversation envelope (F6 Phase 0)
|
|
83
|
+
- `schema_version: 1` written into new single-trace baselines; unversioned
|
|
84
|
+
files read as legacy; envelopes are `schema_version: 2`; newer-than-reader
|
|
85
|
+
files rejected by name. `ConversationEnvelope`: one loader for envelope,
|
|
86
|
+
wrapper, and bare-trace shapes (single-turn = 1-turn degenerate case)
|
|
87
|
+
|
|
88
|
+
### Changed — brand and story
|
|
89
|
+
- **Brand: AgentCI → CIAgent** (display name), standardizing on the `ciagent`
|
|
90
|
+
package name. Spec/runner filenames (`agentci_spec.yaml`), `AGENTCI_*` env
|
|
91
|
+
vars, and `AgentCISpec`/`AgentCITraceProcessor` class names unchanged.
|
|
92
|
+
- **BREAKING: Python module renamed `agentci` → `ciagent`** (`from ciagent
|
|
93
|
+
import ...`); the `agentci` CLI entry point is removed (`ciagent` is the
|
|
94
|
+
command), and the pytest plugin entry point is now `ciagent`. Flag-day, no
|
|
95
|
+
deprecation shim (pre-adoption). Motive is a verified collision: the
|
|
96
|
+
unrelated PyPI package `agentci` 0.1.1 (Agent-CI) installs a top-level
|
|
97
|
+
`agentci/` module and an `agentci` console script — installing both tools
|
|
98
|
+
in one environment broke ours.
|
|
99
|
+
- **GitHub repo renamed** to `suniel12/ciagent` (old URLs redirect).
|
|
100
|
+
- **Claude Code plugin identity renamed**: install is now
|
|
101
|
+
`/plugin marketplace add suniel12/ciagent` + `/plugin install ciagent@ciagent`;
|
|
102
|
+
skills are `ciagent:onboard` / `ciagent:check`. (The 0.8.0 identity below had
|
|
103
|
+
zero installs.)
|
|
104
|
+
- **README hero, PyPI description, and GitHub About repositioned to the
|
|
105
|
+
eval-reliability wedge** ("Your eval score is stable. Your system isn't."),
|
|
106
|
+
per the strategy ADR; regression testing remains as supporting capability.
|
|
107
|
+
|
|
108
|
+
## [0.8.0] - 2026-07-06
|
|
109
|
+
|
|
110
|
+
### Added — Claude Code plugin (F5: your coding agent onboards and operates CIAgent)
|
|
111
|
+
- The repo is now a Claude Code plugin marketplace
|
|
112
|
+
(`/plugin marketplace add suniel12/AgentCI`, `/plugin install agentci@agentci`)
|
|
113
|
+
with two skills: **onboard** (write the runner, record goldens via
|
|
114
|
+
`bootstrap --yes`, generate + tighten the spec, verify with `test --runs 3`)
|
|
115
|
+
and **check** (run the right check after agent changes; route failures by
|
|
116
|
+
flip source; never weaken a correct check to go green)
|
|
117
|
+
- `agentci bootstrap --yes`: fully non-interactive golden recording (requires
|
|
118
|
+
`--queries`); bootstrap now runs the runner through the same TraceContext
|
|
119
|
+
capture path as `agentci test`, so **runners returning plain strings work**
|
|
120
|
+
(previously crashed on anything but a Trace)
|
|
121
|
+
- `--format json` output now includes the agent's `answer` text per query
|
|
122
|
+
(eng-review work item: JSON consumers need what the agent said, not just
|
|
123
|
+
verdicts; also unblocks JSON as a judge-audit answers source)
|
|
124
|
+
- Tests: skills are lint-gated — every `agentci` command and flag a skill
|
|
125
|
+
teaches is asserted to exist in the CLI, so skill docs cannot rot silently
|
|
126
|
+
|
|
127
|
+
### Fixed — from Codex cross-model review of this branch
|
|
128
|
+
- **Nested `TraceContext` no longer double-records or loses capture**: a
|
|
129
|
+
Trace-returning runner that uses `TraceContext` internally, wrapped again by
|
|
130
|
+
the executor, used to stack SDK patches (every LLM call recorded once per
|
|
131
|
+
wrapper) and its exit cleared the outer context entirely. Patches now install
|
|
132
|
+
only in the outermost context, and exits restore the enclosing context via
|
|
133
|
+
contextvar tokens. Affected `agentci test` as well as the new bootstrap path
|
|
134
|
+
- JSON `answer` field now uses the correctness layer's extractor (metadata →
|
|
135
|
+
last span output), so trace shapes the evaluator can grade also serialize
|
|
136
|
+
their answer instead of `null`
|
|
137
|
+
|
|
138
|
+
### Added — zero-key demo (`uvx ciagent test --mock --runs 3`)
|
|
139
|
+
- `agentci test --mock` with no `agentci_spec.yaml` in the working directory now
|
|
140
|
+
falls back to a bundled demo spec (8 synthetic support-agent queries), clearly
|
|
141
|
+
labeled as demo mode with synthetic data. An explicitly passed `--config` that
|
|
142
|
+
is missing remains an error — the fallback only applies to the default path
|
|
143
|
+
- Demo multi-run sessions simulate a pseudo-flaky agent by default with the new
|
|
144
|
+
`"spread"` style (one query breaks per run), so the aggregate score stays
|
|
145
|
+
constant while individual verdicts flip — the report the demo exists to show.
|
|
146
|
+
`AGENTCI_MOCK_FLAKY=0` turns the simulation off; non-demo specs are unaffected
|
|
147
|
+
(flakiness stays env-var opt-in, `"alternate"` style unchanged)
|
|
148
|
+
- Missing spec without `--mock` now exits with a hint pointing at both
|
|
149
|
+
`agentci init` and the zero-key demo command
|
|
9
150
|
|
|
10
151
|
### Changed — stability report hardening (pre-launch fixes from eng review)
|
|
11
152
|
- **Flip attribution now compares per-layer sub-verdicts first**: if every
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ciagent
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
5
|
-
Project-URL: Documentation, https://github.com/suniel12/
|
|
6
|
-
Project-URL: Repository, https://github.com/suniel12/
|
|
7
|
-
Project-URL: Issues, https://github.com/suniel12/
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: 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.
|
|
5
|
+
Project-URL: Documentation, https://github.com/suniel12/ciagent#readme
|
|
6
|
+
Project-URL: Repository, https://github.com/suniel12/ciagent.git
|
|
7
|
+
Project-URL: Issues, https://github.com/suniel12/ciagent/issues
|
|
8
8
|
License: Apache-2.0
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Keywords: agents,ai,ci-cd,llm,regression,testing
|
|
@@ -41,92 +41,53 @@ Provides-Extra: openai
|
|
|
41
41
|
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# CIAgent
|
|
45
45
|
|
|
46
|
-
**
|
|
46
|
+
**Your eval score is stable. Your system isn't.**
|
|
47
|
+
|
|
48
|
+
CIAgent tells you whether your agent's evals are lying to you:
|
|
49
|
+
|
|
50
|
+
- **Run the suite N times** — see which verdicts flip, with the blame attached
|
|
51
|
+
(`agent-variance` → fix the agent, `judge-flake` → fix the eval)
|
|
52
|
+
- **Audit your LLM judge** against ground truth you already have
|
|
53
|
+
- **Replace judge calls with deterministic fact-checks** mined from your knowledge base
|
|
47
54
|
|
|
48
55
|
[](https://pypi.org/project/ciagent/)
|
|
49
|
-
[](https://github.com/suniel12/ciagent/actions/workflows/ci.yml)
|
|
50
57
|
[](LICENSE)
|
|
51
58
|
[](AGENTS.md)
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
Built from a real study: 1 in 7 answers an LLM judge passed were wrong, and deterministic
|
|
61
|
+
checks caught 8 of 8 — methodology and raw numbers in [STUDY.md](STUDY.md).
|
|
55
62
|
Works with OpenAI, Anthropic, and LangGraph. Runs inside pytest.
|
|
56
63
|
|
|
57
|
-
##
|
|
64
|
+
## See It in 30 Seconds
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
pip install ciagent
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Write your golden queries — what should your agent handle, and what should it refuse?
|
|
64
|
-
|
|
65
|
-
```yaml
|
|
66
|
-
# agentci_spec.yaml
|
|
67
|
-
agent: my-agent
|
|
68
|
-
# runner: any function that takes a query string and returns a response
|
|
69
|
-
runner: my_app.agent:run_for_agentci
|
|
70
|
-
queries:
|
|
71
|
-
- query: "How do I install AgentCI?"
|
|
72
|
-
correctness:
|
|
73
|
-
any_expected_in_answer: ["pip install", "ciagent"]
|
|
74
|
-
path:
|
|
75
|
-
expected_tools: [retrieve_docs]
|
|
76
|
-
cost:
|
|
77
|
-
max_llm_calls: 8
|
|
78
|
-
|
|
79
|
-
- query: "What's the CEO's favorite restaurant?"
|
|
80
|
-
correctness:
|
|
81
|
-
not_in_answer: ["restaurant", "favorite"]
|
|
82
|
-
path:
|
|
83
|
-
expected_tools: [] # expect no tools called for out-of-scope queries
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Run:
|
|
66
|
+
No install, no API keys, no config — one command runs a bundled demo suite three times on synthetic traces:
|
|
87
67
|
|
|
88
68
|
```bash
|
|
89
|
-
|
|
90
|
-
agentci test # run live against your real agent
|
|
69
|
+
uvx ciagent test --mock --runs 3
|
|
91
70
|
```
|
|
92
71
|
|
|
93
|
-
`agentci test` evaluates each query through 3 layers — correctness, path, and cost:
|
|
94
|
-
|
|
95
72
|
```
|
|
96
|
-
|
|
73
|
+
Run 1/3: 7/8 passed
|
|
74
|
+
Run 2/3: 7/8 passed
|
|
75
|
+
Run 3/3: 7/8 passed
|
|
97
76
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
✅ CORRECTNESS: PASS
|
|
103
|
-
✓ Found keywords: "pip install ciagent"
|
|
104
|
-
✓ LLM judge passed (score: 5 ≥ 0.6)
|
|
105
|
-
📈 PATH: PASS
|
|
106
|
-
✓ Tool recall: 1.000 (expected: [retrieve_docs])
|
|
107
|
-
✓ Tool precision: 0.500
|
|
108
|
-
✓ No loops detected
|
|
109
|
-
💰 COST: PASS
|
|
110
|
-
✓ LLM calls: 8 ≤ max 8
|
|
111
|
-
|
|
112
|
-
============================================================
|
|
113
|
-
|
|
114
|
-
Query: What Python version does AgentCI require and what frameworks does it support?
|
|
115
|
-
Answer: AgentCI currently does not specify a required Python version
|
|
116
|
-
in the provided context, so I don't have that information...
|
|
77
|
+
────────────────────────────────────────────────────────────
|
|
78
|
+
Stability Report
|
|
79
|
+
────────────────────────────────────────────────────────────
|
|
80
|
+
Suite score across 3 runs: 88% / 88% / 88% ← looks stable
|
|
117
81
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
✓ Loops: 1 ≤ max 3
|
|
123
|
-
💰 COST: PASS
|
|
124
|
-
✓ LLM calls: 4 ≤ max 5
|
|
82
|
+
⚠️ FLAKY — 3/8 queries flipped verdicts across runs: ← is not
|
|
83
|
+
"What's your return window?" ❌✅✅ pass_rate=0.67 source: agent-variance
|
|
84
|
+
"Do you ship internationally?" ✅❌✅ pass_rate=0.67 source: agent-variance
|
|
85
|
+
"How do I reset my password?" ✅✅❌ pass_rate=0.67 source: agent-variance
|
|
125
86
|
|
|
126
|
-
|
|
87
|
+
Flip sources: 3 agent-variance (fix the agent) │ 0 judge-flake (fix the eval)
|
|
127
88
|
```
|
|
128
89
|
|
|
129
|
-
|
|
90
|
+
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.)
|
|
130
91
|
|
|
131
92
|
## A stable score is not a stable system
|
|
132
93
|
|
|
@@ -135,7 +96,7 @@ individual queries flip verdicts every run. The aggregate holds because the erro
|
|
|
135
96
|
around. `--runs N` shows what a single run can't:
|
|
136
97
|
|
|
137
98
|
```bash
|
|
138
|
-
|
|
99
|
+
ciagent test --runs 3
|
|
139
100
|
```
|
|
140
101
|
|
|
141
102
|
```
|
|
@@ -168,7 +129,7 @@ facts; pass@k/pass^k estimates live in the JSON output, labeled as estimates.
|
|
|
168
129
|
|
|
169
130
|
Flaky-but-passing exits 0 so adoption won't break your CI; add `--fail-on-flaky` when
|
|
170
131
|
you're ready to gate on it. Try it with zero API keys:
|
|
171
|
-
`AGENTCI_MOCK_FLAKY=1
|
|
132
|
+
`AGENTCI_MOCK_FLAKY=1 ciagent test --mock --runs 3`. Details: [docs/stability.md](docs/stability.md).
|
|
172
133
|
|
|
173
134
|
## Audit the judge itself
|
|
174
135
|
|
|
@@ -178,7 +139,7 @@ nothing, passes it. `judge-audit` measures your judge against ground truth you a
|
|
|
178
139
|
by re-scoring recorded baselines (the agent is never re-run):
|
|
179
140
|
|
|
180
141
|
```bash
|
|
181
|
-
|
|
142
|
+
ciagent judge-audit
|
|
182
143
|
```
|
|
183
144
|
|
|
184
145
|
1. **Judge vs. deterministic checks** — the disagreement matrix. The row that matters:
|
|
@@ -195,7 +156,7 @@ shouldn't be trusted where you can't. Verdict: `TRUSTWORTHY` / `NEEDS CALIBRATIO
|
|
|
195
156
|
|
|
196
157
|
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.
|
|
197
158
|
|
|
198
|
-
So
|
|
159
|
+
So CIAgent runs deterministic checks first and treats the judge as the last resort, not the default:
|
|
199
160
|
|
|
200
161
|
1. **Fact checks in code** — `expected_in_answer`, `not_in_answer`, `regex_match`, `json_schema`. Zero LLM calls, zero flakiness, same verdict every run.
|
|
201
162
|
2. **Path checks** — did the agent call the tools it should have? A missing expected tool warns; a forbidden tool fails.
|
|
@@ -205,7 +166,7 @@ So AgentCI runs deterministic checks first and treats the judge as the last reso
|
|
|
205
166
|
Don't write the fact checks by hand — mine them from your knowledge base:
|
|
206
167
|
|
|
207
168
|
```bash
|
|
208
|
-
|
|
169
|
+
ciagent generate-checks
|
|
209
170
|
```
|
|
210
171
|
|
|
211
172
|
It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertions, and
|
|
@@ -213,28 +174,122 @@ It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertion
|
|
|
213
174
|
would fail a known-good answer is rejected before you ever see it. One LLM call at
|
|
214
175
|
authoring time; the checks run free forever. Details: [docs/generate-checks.md](docs/generate-checks.md).
|
|
215
176
|
|
|
177
|
+
## Add to Your Project
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install ciagent
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Write your golden queries — what should your agent handle, and what should it refuse?
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
# agentci_spec.yaml
|
|
187
|
+
agent: my-agent
|
|
188
|
+
# runner: any function that takes a query string and returns a response
|
|
189
|
+
runner: my_app.agent:run_for_agentci
|
|
190
|
+
queries:
|
|
191
|
+
- query: "How do I install CIAgent?"
|
|
192
|
+
correctness:
|
|
193
|
+
any_expected_in_answer: ["pip install", "ciagent"]
|
|
194
|
+
path:
|
|
195
|
+
expected_tools: [retrieve_docs]
|
|
196
|
+
retrieval:
|
|
197
|
+
tool: retrieve_docs # assert on what the retriever actually returned
|
|
198
|
+
forbid_empty: true # empty retrieval + confident answer = ungrounded
|
|
199
|
+
expected_sources: [install.md]
|
|
200
|
+
cost:
|
|
201
|
+
max_llm_calls: 8
|
|
202
|
+
|
|
203
|
+
- query: "What's the CEO's favorite restaurant?"
|
|
204
|
+
correctness:
|
|
205
|
+
not_in_answer: ["restaurant", "favorite"]
|
|
206
|
+
path:
|
|
207
|
+
expected_tools: [] # expect no tools called for out-of-scope queries
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Run:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
ciagent test --mock # start here: zero-cost with synthetic traces
|
|
214
|
+
ciagent test # run live against your real agent
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`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:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
============================================================
|
|
221
|
+
|
|
222
|
+
Query: How do I install CIAgent?
|
|
223
|
+
Answer: To install CIAgent, you can use pip with the following command:
|
|
224
|
+
pip install ciagent. Make sure you have Python 3.10 or later.
|
|
225
|
+
|
|
226
|
+
✅ CORRECTNESS: PASS
|
|
227
|
+
✓ Found keywords: "pip install ciagent"
|
|
228
|
+
✓ LLM judge passed (score: 5 ≥ 0.6)
|
|
229
|
+
📈 PATH: PASS
|
|
230
|
+
✓ Tool recall: 1.000 (expected: [retrieve_docs])
|
|
231
|
+
✓ Tool precision: 0.500
|
|
232
|
+
✓ No loops detected
|
|
233
|
+
💰 COST: PASS
|
|
234
|
+
✓ LLM calls: 8 ≤ max 8
|
|
235
|
+
|
|
236
|
+
============================================================
|
|
237
|
+
|
|
238
|
+
Query: What Python version does CIAgent require and what frameworks does it support?
|
|
239
|
+
Answer: CIAgent currently does not specify a required Python version
|
|
240
|
+
in the provided context, so I don't have that information...
|
|
241
|
+
|
|
242
|
+
❌ CORRECTNESS: FAIL
|
|
243
|
+
• Expected '3.10' not found in answer
|
|
244
|
+
📈 PATH: PASS
|
|
245
|
+
✓ Tool recall: 1.000 (expected: [retrieve_docs])
|
|
246
|
+
✓ Loops: 1 ≤ max 3
|
|
247
|
+
💰 COST: PASS
|
|
248
|
+
✓ LLM calls: 4 ≤ max 5
|
|
249
|
+
|
|
250
|
+
============================================================
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Don't have golden queries yet? `ciagent init --generate` scans your code and generates a starter spec.
|
|
254
|
+
|
|
255
|
+
## Let your coding agent set it up
|
|
256
|
+
|
|
257
|
+
CIAgent ships as a Claude Code plugin. Two skills: **onboard** (writes the runner,
|
|
258
|
+
records golden baselines, generates the spec, verifies it) and **check** (runs the
|
|
259
|
+
right test after every change to your agent and routes failures by flip source).
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
/plugin marketplace add suniel12/ciagent
|
|
263
|
+
/plugin install ciagent@ciagent
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Then ask your coding agent to "set up CIAgent for this repo." It records goldens with
|
|
267
|
+
`ciagent bootstrap --yes` and verifies with `ciagent test --runs 3` — no human CLI use
|
|
268
|
+
needed. The runner it writes is one function: `(query: str) -> str`; trace capture is
|
|
269
|
+
automatic.
|
|
270
|
+
|
|
216
271
|
## Demo
|
|
217
272
|
|
|
218
|
-
Here's a RAG agent demo where someone "optimizes for latency" by reducing retriever docs from 8 to 1.
|
|
273
|
+
Here's a RAG agent demo where someone "optimizes for latency" by reducing retriever docs from 8 to 1. CIAgent catches the correctness regression:
|
|
219
274
|
|
|
220
|
-

|
|
221
276
|
|
|
222
277
|
## CLI
|
|
223
278
|
|
|
224
279
|
```bash
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
280
|
+
ciagent init --generate # Scan project, generate test spec
|
|
281
|
+
ciagent init # Generate GitHub Actions workflow + pre-push hook
|
|
282
|
+
ciagent test --mock --yes # Zero-cost synthetic traces, CI-friendly (no keys, no prompts)
|
|
283
|
+
ciagent test # Run 3-layer evaluation (correctness → path → cost)
|
|
284
|
+
ciagent test --runs 3 # Stability report: verdict flips + flip-source attribution
|
|
285
|
+
ciagent judge-audit # Audit the LLM judge against checks, retests, hand labels
|
|
286
|
+
ciagent generate-checks # Mine KB facts into deterministic assertions (gated)
|
|
287
|
+
ciagent test --format html -o report.html # HTML report with per-query details
|
|
288
|
+
ciagent calibrate # Measure real agent metrics, auto-tune spec budgets
|
|
289
|
+
ciagent doctor # Health check: spec, deps, API keys
|
|
290
|
+
ciagent record <test> # Record golden baseline
|
|
291
|
+
ciagent diff # Diff against baseline
|
|
292
|
+
ciagent report -i results.json # Generate HTML report from JSON results
|
|
238
293
|
```
|
|
239
294
|
## Docs
|
|
240
295
|
|
|
@@ -251,13 +306,13 @@ agentci report -i results.json # Generate HTML report from JSON results
|
|
|
251
306
|
|
|
252
307
|
## Why not just an LLM judge?
|
|
253
308
|
|
|
254
|
-
Judge-only evals are expensive, flaky, and blind to their own context.
|
|
309
|
+
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.
|
|
255
310
|
|
|
256
311
|
## Contributing
|
|
257
312
|
|
|
258
|
-
[GitHub Issues](https://github.com/suniel12/
|
|
313
|
+
[GitHub Issues](https://github.com/suniel12/ciagent/issues) ·
|
|
259
314
|
[DemoAgents](https://github.com/suniel12/DemoAgents) — working examples for OpenAI, Anthropic, and LangGraph agents
|
|
260
315
|
|
|
261
|
-
Apache 2.0. If you build an agent and test it with
|
|
316
|
+
Apache 2.0. If you build an agent and test it with CIAgent, I'd love to hear about it.
|
|
262
317
|
|
|
263
318
|
---
|