ciagent 0.8.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.8.0 → ciagent-0.9.0}/CHANGELOG.md +101 -1
- {ciagent-0.8.0 → ciagent-0.9.0}/PKG-INFO +123 -112
- {ciagent-0.8.0 → ciagent-0.9.0}/README.md +118 -107
- {ciagent-0.8.0 → ciagent-0.9.0}/pyproject.toml +12 -12
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/langgraph.py +25 -4
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/openai_agents.py +2 -2
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/assertions.py +8 -8
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/baselines.py +11 -7
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/capture.py +120 -17
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/cli.py +532 -73
- {ciagent-0.8.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.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/diff_engine.py +47 -8
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/__init__.py +1 -1
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/check_generator.py +3 -3
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/correctness.py +3 -3
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/cost.py +3 -3
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/cost_estimator.py +92 -1
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/diff.py +146 -16
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/judge.py +1 -1
- {ciagent-0.8.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.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/parallel.py +8 -8
- {ciagent-0.8.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.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/reporter.py +36 -7
- {ciagent-0.8.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.8.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.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/span_assertions.py +6 -6
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/stability.py +202 -7
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/examples/demo_spec.yaml +3 -3
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/loader.py +4 -4
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/models.py +1 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/runner.py +3 -3
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/agentci_spec.schema.json +329 -7
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/generate_schema.py +2 -2
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/spec_models.py +194 -6
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/github_action.yml.j2 +4 -4
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/report.html.j2 +3 -3
- ciagent-0.8.0/src/agentci/engine/mock_runner.py +0 -129
- {ciagent-0.8.0 → ciagent-0.9.0}/.gitignore +0 -0
- {ciagent-0.8.0 → ciagent-0.9.0}/LICENSE +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/__init__.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/__init__.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/base.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/adapters/generic.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/cost.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/engine/metrics.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/examples/__init__.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/exceptions.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/mocks.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/py.typed +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/pytest_plugin.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/report.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/schema/__init__.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/__init__.py +0 -0
- {ciagent-0.8.0/src/agentci → ciagent-0.9.0/src/ciagent}/templates/pre_push_hook.sh.j2 +0 -0
|
@@ -5,9 +5,109 @@ 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
|
+
## [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
|
+
|
|
8
108
|
## [0.8.0] - 2026-07-06
|
|
9
109
|
|
|
10
|
-
### Added — Claude Code plugin (F5: your coding agent onboards and operates
|
|
110
|
+
### Added — Claude Code plugin (F5: your coding agent onboards and operates CIAgent)
|
|
11
111
|
- The repo is now a Claude Code plugin marketplace
|
|
12
112
|
(`/plugin marketplace add suniel12/AgentCI`, `/plugin install agentci@agentci`)
|
|
13
113
|
with two skills: **onboard** (write the runner, record goldens via
|
|
@@ -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,17 +41,24 @@ 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
|
|
@@ -82,80 +89,6 @@ Suite score across 3 runs: 88% / 88% / 88% ← looks stable
|
|
|
82
89
|
|
|
83
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.)
|
|
84
91
|
|
|
85
|
-
## Add to Your Project
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
pip install ciagent
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Write your golden queries — what should your agent handle, and what should it refuse?
|
|
92
|
-
|
|
93
|
-
```yaml
|
|
94
|
-
# agentci_spec.yaml
|
|
95
|
-
agent: my-agent
|
|
96
|
-
# runner: any function that takes a query string and returns a response
|
|
97
|
-
runner: my_app.agent:run_for_agentci
|
|
98
|
-
queries:
|
|
99
|
-
- query: "How do I install AgentCI?"
|
|
100
|
-
correctness:
|
|
101
|
-
any_expected_in_answer: ["pip install", "ciagent"]
|
|
102
|
-
path:
|
|
103
|
-
expected_tools: [retrieve_docs]
|
|
104
|
-
cost:
|
|
105
|
-
max_llm_calls: 8
|
|
106
|
-
|
|
107
|
-
- query: "What's the CEO's favorite restaurant?"
|
|
108
|
-
correctness:
|
|
109
|
-
not_in_answer: ["restaurant", "favorite"]
|
|
110
|
-
path:
|
|
111
|
-
expected_tools: [] # expect no tools called for out-of-scope queries
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Run:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
agentci test --mock # start here: zero-cost with synthetic traces
|
|
118
|
-
agentci test # run live against your real agent
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`agentci test` evaluates each query through 3 layers — correctness, path, and cost:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
============================================================
|
|
125
|
-
|
|
126
|
-
Query: How do I install AgentCI?
|
|
127
|
-
Answer: To install AgentCI, you can use pip with the following command:
|
|
128
|
-
pip install ciagent. Make sure you have Python 3.10 or later.
|
|
129
|
-
|
|
130
|
-
✅ CORRECTNESS: PASS
|
|
131
|
-
✓ Found keywords: "pip install ciagent"
|
|
132
|
-
✓ LLM judge passed (score: 5 ≥ 0.6)
|
|
133
|
-
📈 PATH: PASS
|
|
134
|
-
✓ Tool recall: 1.000 (expected: [retrieve_docs])
|
|
135
|
-
✓ Tool precision: 0.500
|
|
136
|
-
✓ No loops detected
|
|
137
|
-
💰 COST: PASS
|
|
138
|
-
✓ LLM calls: 8 ≤ max 8
|
|
139
|
-
|
|
140
|
-
============================================================
|
|
141
|
-
|
|
142
|
-
Query: What Python version does AgentCI require and what frameworks does it support?
|
|
143
|
-
Answer: AgentCI currently does not specify a required Python version
|
|
144
|
-
in the provided context, so I don't have that information...
|
|
145
|
-
|
|
146
|
-
❌ CORRECTNESS: FAIL
|
|
147
|
-
• Expected '3.10' not found in answer
|
|
148
|
-
📈 PATH: PASS
|
|
149
|
-
✓ Tool recall: 1.000 (expected: [retrieve_docs])
|
|
150
|
-
✓ Loops: 1 ≤ max 3
|
|
151
|
-
💰 COST: PASS
|
|
152
|
-
✓ LLM calls: 4 ≤ max 5
|
|
153
|
-
|
|
154
|
-
============================================================
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Don't have golden queries yet? `agentci init --generate` scans your code and generates a starter spec.
|
|
158
|
-
|
|
159
92
|
## A stable score is not a stable system
|
|
160
93
|
|
|
161
94
|
Run the identical eval three times and you can get 96% / 95% / 96% — rock solid — while
|
|
@@ -163,7 +96,7 @@ individual queries flip verdicts every run. The aggregate holds because the erro
|
|
|
163
96
|
around. `--runs N` shows what a single run can't:
|
|
164
97
|
|
|
165
98
|
```bash
|
|
166
|
-
|
|
99
|
+
ciagent test --runs 3
|
|
167
100
|
```
|
|
168
101
|
|
|
169
102
|
```
|
|
@@ -196,7 +129,7 @@ facts; pass@k/pass^k estimates live in the JSON output, labeled as estimates.
|
|
|
196
129
|
|
|
197
130
|
Flaky-but-passing exits 0 so adoption won't break your CI; add `--fail-on-flaky` when
|
|
198
131
|
you're ready to gate on it. Try it with zero API keys:
|
|
199
|
-
`AGENTCI_MOCK_FLAKY=1
|
|
132
|
+
`AGENTCI_MOCK_FLAKY=1 ciagent test --mock --runs 3`. Details: [docs/stability.md](docs/stability.md).
|
|
200
133
|
|
|
201
134
|
## Audit the judge itself
|
|
202
135
|
|
|
@@ -206,7 +139,7 @@ nothing, passes it. `judge-audit` measures your judge against ground truth you a
|
|
|
206
139
|
by re-scoring recorded baselines (the agent is never re-run):
|
|
207
140
|
|
|
208
141
|
```bash
|
|
209
|
-
|
|
142
|
+
ciagent judge-audit
|
|
210
143
|
```
|
|
211
144
|
|
|
212
145
|
1. **Judge vs. deterministic checks** — the disagreement matrix. The row that matters:
|
|
@@ -223,7 +156,7 @@ shouldn't be trusted where you can't. Verdict: `TRUSTWORTHY` / `NEEDS CALIBRATIO
|
|
|
223
156
|
|
|
224
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.
|
|
225
158
|
|
|
226
|
-
So
|
|
159
|
+
So CIAgent runs deterministic checks first and treats the judge as the last resort, not the default:
|
|
227
160
|
|
|
228
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.
|
|
229
162
|
2. **Path checks** — did the agent call the tools it should have? A missing expected tool warns; a forbidden tool fails.
|
|
@@ -233,7 +166,7 @@ So AgentCI runs deterministic checks first and treats the judge as the last reso
|
|
|
233
166
|
Don't write the fact checks by hand — mine them from your knowledge base:
|
|
234
167
|
|
|
235
168
|
```bash
|
|
236
|
-
|
|
169
|
+
ciagent generate-checks
|
|
237
170
|
```
|
|
238
171
|
|
|
239
172
|
It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertions, and
|
|
@@ -241,44 +174,122 @@ It extracts hard facts (prices, rates, SKUs, "30 days") as variant-set assertion
|
|
|
241
174
|
would fail a known-good answer is rejected before you ever see it. One LLM call at
|
|
242
175
|
authoring time; the checks run free forever. Details: [docs/generate-checks.md](docs/generate-checks.md).
|
|
243
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
|
+
|
|
244
255
|
## Let your coding agent set it up
|
|
245
256
|
|
|
246
|
-
|
|
257
|
+
CIAgent ships as a Claude Code plugin. Two skills: **onboard** (writes the runner,
|
|
247
258
|
records golden baselines, generates the spec, verifies it) and **check** (runs the
|
|
248
259
|
right test after every change to your agent and routes failures by flip source).
|
|
249
260
|
|
|
250
261
|
```
|
|
251
|
-
/plugin marketplace add suniel12/
|
|
252
|
-
/plugin install
|
|
262
|
+
/plugin marketplace add suniel12/ciagent
|
|
263
|
+
/plugin install ciagent@ciagent
|
|
253
264
|
```
|
|
254
265
|
|
|
255
|
-
Then ask your coding agent to "set up
|
|
256
|
-
`
|
|
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
|
|
257
268
|
needed. The runner it writes is one function: `(query: str) -> str`; trace capture is
|
|
258
269
|
automatic.
|
|
259
270
|
|
|
260
271
|
## Demo
|
|
261
272
|
|
|
262
|
-
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:
|
|
263
274
|
|
|
264
|
-

|
|
265
276
|
|
|
266
277
|
## CLI
|
|
267
278
|
|
|
268
279
|
```bash
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
|
282
293
|
```
|
|
283
294
|
## Docs
|
|
284
295
|
|
|
@@ -295,13 +306,13 @@ agentci report -i results.json # Generate HTML report from JSON results
|
|
|
295
306
|
|
|
296
307
|
## Why not just an LLM judge?
|
|
297
308
|
|
|
298
|
-
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.
|
|
299
310
|
|
|
300
311
|
## Contributing
|
|
301
312
|
|
|
302
|
-
[GitHub Issues](https://github.com/suniel12/
|
|
313
|
+
[GitHub Issues](https://github.com/suniel12/ciagent/issues) ·
|
|
303
314
|
[DemoAgents](https://github.com/suniel12/DemoAgents) — working examples for OpenAI, Anthropic, and LangGraph agents
|
|
304
315
|
|
|
305
|
-
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.
|
|
306
317
|
|
|
307
318
|
---
|