@indemn/cli 1.2.0 → 1.2.2
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.
- package/.claude-plugin/marketplace.json +2 -4
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +20 -44
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/agent-builder/SKILL.md +299 -0
- package/skills/evaluations/SKILL.md +509 -0
- package/src/cli/auth.ts +19 -45
- package/src/cli/index.ts +52 -0
- package/skills/agent-setup/SKILL.md +0 -131
- package/skills/eval-analysis/SKILL.md +0 -178
- package/skills/eval-orchestration/SKILL.md +0 -257
- package/skills/rubric-creation/SKILL.md +0 -163
- package/skills/test-set-creation/SKILL.md +0 -250
- /package/skills/{eval-analysis → evaluations}/references/data-shapes.md +0 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: evaluations
|
|
3
|
+
description: Evaluate Indemn AI agents — create rubrics and test sets, run evaluations, analyze results, and export reports. Use for any evaluation, testing, or quality assessment task.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Evaluations
|
|
7
|
+
|
|
8
|
+
Complete evaluation workflow for Indemn AI agents. Covers rubric creation, test set design, evaluation execution, result analysis, and reporting.
|
|
9
|
+
|
|
10
|
+
**Prerequisite:** `indemn whoami` must succeed. The `indemn` CLI must be installed and authenticated.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Mode Detection
|
|
15
|
+
|
|
16
|
+
Check the FIRST LINE of the user's message:
|
|
17
|
+
|
|
18
|
+
**HEADLESS** -- Execute autonomously without asking questions. Optimize for completion.
|
|
19
|
+
|
|
20
|
+
**INTERACTIVE** -- Get explicit approval before every action that creates, modifies, or triggers anything. Approval gates: before creating anything, after creation (review?), before triggering a run, before modifying resources.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Evaluation Harness Overview
|
|
25
|
+
|
|
26
|
+
The harness tests conversational AI agents against quality standards through two scoring layers:
|
|
27
|
+
|
|
28
|
+
| Layer | Source | Applies To | Purpose |
|
|
29
|
+
|-------|--------|------------|---------|
|
|
30
|
+
| **Success Criteria** | Embedded in each test item | That specific item | Did the agent handle THIS situation correctly? |
|
|
31
|
+
| **Rubric Rules** | Separate rubric document | ALL items universally | Does the agent meet baseline behavioral standards? |
|
|
32
|
+
|
|
33
|
+
An item passes only if BOTH layers pass. A high-severity rubric rule failure causes the entire item to fail.
|
|
34
|
+
|
|
35
|
+
### Three Test Types
|
|
36
|
+
|
|
37
|
+
| Type | Input | What It Tests |
|
|
38
|
+
|------|-------|---------------|
|
|
39
|
+
| `single_turn` | A single message (new conversation, no prior context) | Specific directive compliance, edge cases |
|
|
40
|
+
| `scenario` | Persona + initial message + max turns | End-to-end workflow completion (chat agents) |
|
|
41
|
+
| `voice_simulation` | Persona + initial message + max turns (via LiveKit) | End-to-end voice workflow |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## The Directive Allocation Workflow
|
|
46
|
+
|
|
47
|
+
**Run this BEFORE creating rubric or test set.** This is the core method for deciding what goes where.
|
|
48
|
+
|
|
49
|
+
### Step 1: Fetch Bot Context
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
indemn eval bot-context <agent-id> --json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Extract the system prompt, tools, and knowledge bases.
|
|
56
|
+
|
|
57
|
+
### Step 2: Parse All Directives
|
|
58
|
+
|
|
59
|
+
Identify every ALWAYS/NEVER/MUST/CRITICAL directive from the system prompt. List them all.
|
|
60
|
+
|
|
61
|
+
### Step 3: Apply the 3-Message Universal Applicability Test
|
|
62
|
+
|
|
63
|
+
For EACH directive, mentally test it against these three messages:
|
|
64
|
+
|
|
65
|
+
1. **Greeting:** "Hello, I'm interested in wedding insurance"
|
|
66
|
+
2. **Off-topic:** "Can you help me with my taxes?"
|
|
67
|
+
3. **Utility:** "I need to correct my phone number"
|
|
68
|
+
|
|
69
|
+
Ask: "Would I check this directive against ALL THREE of these messages?"
|
|
70
|
+
|
|
71
|
+
### Step 4: Classify Each Directive
|
|
72
|
+
|
|
73
|
+
| Classification | Rule | Where It Goes |
|
|
74
|
+
|---------------|------|---------------|
|
|
75
|
+
| **UNIVERSAL** | Passes the 3-message test | Rubric rule |
|
|
76
|
+
| **WORKFLOW-SPECIFIC** | Only applies in certain situations | Test set scenario success criteria |
|
|
77
|
+
| **AMBIGUOUS** | Unclear whether universal | Default to scenario criteria |
|
|
78
|
+
|
|
79
|
+
### Step 5: Produce Allocation Table
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
| Directive | Classification | Destination |
|
|
83
|
+
|-----------|---------------|-------------|
|
|
84
|
+
| "ALWAYS maintain professional tone" | UNIVERSAL | Rubric rule |
|
|
85
|
+
| "MUST collect DOB before quoting" | WORKFLOW-SPECIFIC | Scenario criteria |
|
|
86
|
+
| "NEVER provide medical advice" | UNIVERSAL | Rubric rule |
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Step 6: Create Artifacts Using the Allocation
|
|
90
|
+
|
|
91
|
+
- Create the rubric using ONLY universal directives (see Rubric Creation below)
|
|
92
|
+
- Create the test set mapping workflow-specific directives as scenario success criteria (see Test Set Creation below)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Rubric Creation
|
|
97
|
+
|
|
98
|
+
Rubrics define the agent's universal behavioral contract -- rules checked against EVERY response.
|
|
99
|
+
|
|
100
|
+
### Hard Cap: 3-6 Rules
|
|
101
|
+
|
|
102
|
+
NEVER exceed 6 rules. A tight rubric with precise universal rules is better than a broad one with overlapping or situational rules. If you find yourself writing more than 6, you are including workflow-specific rules that belong in the test set.
|
|
103
|
+
|
|
104
|
+
### Process
|
|
105
|
+
|
|
106
|
+
1. **Fetch bot context** (if not already done): `indemn eval bot-context <agent-id> --json`
|
|
107
|
+
2. **Extract universal rules from the system prompt:**
|
|
108
|
+
- Persona and tone -- How must the agent communicate in every response?
|
|
109
|
+
- Universal prohibitions (NEVER/DO NOT) -- Things the agent must never do in ANY response
|
|
110
|
+
- Universal requirements (ALWAYS/MUST) -- Things the agent must do in EVERY response
|
|
111
|
+
3. **Add standard quality baselines** (include each unless a prompt-derived rule already covers it):
|
|
112
|
+
- No hallucination: Does not fabricate facts, statistics, or capabilities
|
|
113
|
+
- Stays in scope: Operates within its defined role
|
|
114
|
+
- No harmful content: Does not produce harmful, dangerous, or inappropriate content
|
|
115
|
+
- Response coherence: Responses are clear, grammatically correct, and logically structured
|
|
116
|
+
4. **Verify each rule passes the 3-message self-check** (greeting, off-topic, utility)
|
|
117
|
+
5. **Write JSON and create:**
|
|
118
|
+
```bash
|
|
119
|
+
indemn rubric create --name "<Agent Name> Rubric" --agent-id <agent-id> --file /tmp/rubric.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Rubric JSON Format
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"rules": [
|
|
127
|
+
{
|
|
128
|
+
"id": "SNAKE_CASE_ID",
|
|
129
|
+
"name": "Human-readable name",
|
|
130
|
+
"severity": "high | medium | low",
|
|
131
|
+
"category": "persona | instruction_compliance | safety | response_quality",
|
|
132
|
+
"description": "What this rule checks and why it matters",
|
|
133
|
+
"component_scope": "prompt | general | function | knowledge_base",
|
|
134
|
+
"component_ids": null,
|
|
135
|
+
"component_names": null,
|
|
136
|
+
"evaluation_criteria": {
|
|
137
|
+
"pass_conditions": ["Observable condition that indicates compliance"],
|
|
138
|
+
"fail_conditions": ["Observable condition that indicates violation"]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**component_scope values:**
|
|
146
|
+
|
|
147
|
+
| Scope | Meaning | Example |
|
|
148
|
+
|-------|---------|---------|
|
|
149
|
+
| `prompt` | Derived from a system prompt directive | "NEVER provide medical advice" |
|
|
150
|
+
| `general` | Standard quality/safety baseline | No hallucination, response coherence |
|
|
151
|
+
| `function` | Informed by the agent's tool capabilities | "Does not claim capabilities it doesn't have" |
|
|
152
|
+
| `knowledge_base` | Informed by the agent's KBs | "Grounds information rather than fabricating" |
|
|
153
|
+
|
|
154
|
+
### Severity Assignment
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Could violation cause harm to the user? -> HIGH
|
|
158
|
+
Does the prompt use ALWAYS/NEVER/MUST for this? -> HIGH
|
|
159
|
+
Could violation severely damage trust? -> HIGH
|
|
160
|
+
Does it significantly degrade response quality? -> MEDIUM
|
|
161
|
+
Is it a tone, style, or formatting preference? -> LOW
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Target distribution: High 20-30%, Medium 40-50%, Low 20-30%.
|
|
165
|
+
|
|
166
|
+
### Anti-Patterns
|
|
167
|
+
|
|
168
|
+
- Do NOT derive rubric rules from tool descriptions -- tools inform domain understanding but tool-specific behaviors are workflow-specific by definition
|
|
169
|
+
- Do NOT include workflow-specific rules (e.g., "collects DOB before quoting")
|
|
170
|
+
- Do NOT write vague conditions -- every pass/fail condition must be observable from the agent's response text
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Test Set Creation
|
|
175
|
+
|
|
176
|
+
Test sets verify that an agent can do its job -- following its workflow, using its tools, handling real-world user behavior.
|
|
177
|
+
|
|
178
|
+
### Size Guidance
|
|
179
|
+
|
|
180
|
+
Aim for roughly **15 items total**. A focused test set with 12-18 precise items is far more useful than 40+ redundant ones. Consolidate items that test the same behavior.
|
|
181
|
+
|
|
182
|
+
### Process
|
|
183
|
+
|
|
184
|
+
1. **Fetch bot context** (if not already done): `indemn eval bot-context <agent-id> --json`
|
|
185
|
+
2. **Detect voice vs chat agent** -- Check for voice indicators in tools (voice, telephony, livekit, twilio, call, phone) or system prompt. If detected, use `voice_simulation` instead of `scenario` for multi-turn items.
|
|
186
|
+
3. **Analyze workflow depth for max_turns:**
|
|
187
|
+
|
|
188
|
+
| Workflow Steps | Recommended max_turns |
|
|
189
|
+
|---------------|----------------------|
|
|
190
|
+
| 2-3 steps | 4-6 turns |
|
|
191
|
+
| 4-5 steps | 6-8 turns |
|
|
192
|
+
| 6+ steps | 8-12 turns |
|
|
193
|
+
|
|
194
|
+
4. **Create multi-turn scenarios** (primary content) for each major workflow path with varied personas:
|
|
195
|
+
- **Happy path** -- Cooperative user following the workflow naturally
|
|
196
|
+
- **Confused user** -- Misunderstands questions, gives unclear answers
|
|
197
|
+
- **Resistant user** -- Pushes back on providing information
|
|
198
|
+
- **Incomplete information** -- Gives partial answers
|
|
199
|
+
- **Scope boundary** -- Starts in-scope, drifts out of domain
|
|
200
|
+
5. **Map workflow-specific directives to success criteria** on relevant scenarios
|
|
201
|
+
6. **Create single-turn diagnostics** for ALWAYS/NEVER directives testable standalone, edge cases, and tool outcome tests
|
|
202
|
+
7. **Write JSON and create:**
|
|
203
|
+
```bash
|
|
204
|
+
indemn testset create --name "<Agent Name> Test Set" --agent-id <agent-id> --file /tmp/testset.json
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Test Set JSON: Scenario
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"type": "scenario",
|
|
212
|
+
"name": "Descriptive name - what this tests",
|
|
213
|
+
"inputs": {
|
|
214
|
+
"persona": "You are a [type] who [traits, emotional state, behavior pattern, goal].",
|
|
215
|
+
"initial_message": "Natural first message matching the persona",
|
|
216
|
+
"max_turns": 8
|
|
217
|
+
},
|
|
218
|
+
"expected": {
|
|
219
|
+
"success_criteria": ["Criterion about the conversation outcome"],
|
|
220
|
+
"expected_outcome": "What the conversation should achieve"
|
|
221
|
+
},
|
|
222
|
+
"tags": ["scenario", "category"],
|
|
223
|
+
"priority": "high"
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Test Set JSON: Voice Simulation
|
|
228
|
+
|
|
229
|
+
Identical to scenario except `"type": "voice_simulation"`. The harness routes these through LiveKit with TTS/STT.
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"type": "voice_simulation",
|
|
234
|
+
"name": "Descriptive name - what this tests",
|
|
235
|
+
"inputs": {
|
|
236
|
+
"persona": "You are a caller who [traits]. You are on a phone call -- keep responses brief and natural.",
|
|
237
|
+
"initial_message": "Natural first utterance",
|
|
238
|
+
"max_turns": 6
|
|
239
|
+
},
|
|
240
|
+
"expected": {
|
|
241
|
+
"success_criteria": ["Criterion about the conversation outcome"],
|
|
242
|
+
"expected_outcome": "What the call should achieve"
|
|
243
|
+
},
|
|
244
|
+
"tags": ["voice_simulation", "category"],
|
|
245
|
+
"priority": "high"
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Voice-specific: personas should behave like phone callers (shorter utterances, less formality). max_turns generally shorter (4-6 simple, 6-8 complex). Do NOT create single_turn items for voice agents.
|
|
250
|
+
|
|
251
|
+
### Test Set JSON: Single Turn
|
|
252
|
+
|
|
253
|
+
**Single-turn tests start a BRAND NEW conversation with NO prior context.** Design messages that are standalone.
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"type": "single_turn",
|
|
258
|
+
"name": "Descriptive name - what this tests",
|
|
259
|
+
"inputs": {
|
|
260
|
+
"message": "The standalone message to send"
|
|
261
|
+
},
|
|
262
|
+
"expected": {
|
|
263
|
+
"success_criteria": ["Specific observable criterion"]
|
|
264
|
+
},
|
|
265
|
+
"tags": ["category"],
|
|
266
|
+
"priority": "high"
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**CRITICAL:** The `inputs` object for single-turn items contains ONLY `message`. No `max_turns`, `persona`, or `initial_message`.
|
|
271
|
+
|
|
272
|
+
### Success Criteria Rules
|
|
273
|
+
|
|
274
|
+
Every criterion MUST be observable from the agent's output:
|
|
275
|
+
|
|
276
|
+
- GOOD: "Agent asks for the user's date of birth before presenting a quote"
|
|
277
|
+
- GOOD: "Response contains specific policy coverage details, not generic information"
|
|
278
|
+
- BAD: "Agent uses the knowledge base search tool" (mechanism, not outcome)
|
|
279
|
+
- BAD: "Agent handles the request appropriately" (vague)
|
|
280
|
+
|
|
281
|
+
Rules: test ONE specific behavior, use concrete verbs ("asks for", "provides", "does not", "contains", "redirects"), 2-4 criteria per item, do NOT duplicate rubric rules as criteria.
|
|
282
|
+
|
|
283
|
+
### Session Init Limitation
|
|
284
|
+
|
|
285
|
+
Scenarios start as fresh conversations with no pre-existing session state. Do NOT create items that depend on session-initialized data. Note session-dependent behaviors as coverage gaps in the completion report.
|
|
286
|
+
|
|
287
|
+
### Priority Assignment
|
|
288
|
+
|
|
289
|
+
- **high**: Core workflow completion, safety, explicit prompt directives
|
|
290
|
+
- **medium**: Secondary behaviors, edge cases, tool outcomes
|
|
291
|
+
- **low**: Style preferences, formatting, nice-to-have behaviors
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Running Evaluations
|
|
296
|
+
|
|
297
|
+
### Trigger a Run
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
indemn eval run <agent-id> --test-set <id> --rubric <id> --wait
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The `--wait` flag polls until completion and shows a pass/fail summary.
|
|
304
|
+
|
|
305
|
+
**Key flags:**
|
|
306
|
+
|
|
307
|
+
| Flag | Purpose | Notes |
|
|
308
|
+
|------|---------|-------|
|
|
309
|
+
| `--wait` | Poll until complete, show summary | Recommended for all runs |
|
|
310
|
+
| `--concurrency <n>` | Parallel items | Use `1` for voice_simulation |
|
|
311
|
+
| `--eval-model <model>` | Evaluator LLM (e.g., "gpt-4o") | Overrides default judge model |
|
|
312
|
+
| `--keep` | Preserve intermediate artifacts | Useful for debugging |
|
|
313
|
+
| `--limit <n>` | Run only first N items | Good for quick smoke tests |
|
|
314
|
+
| `--component-scope <scope>` | Filter by component scope | prompt, function, knowledge_base, general |
|
|
315
|
+
| `--component-ids <ids>` | Filter by component IDs | Comma-separated |
|
|
316
|
+
| `--conversation-ids <ids>` | Filter by conversation IDs | Comma-separated |
|
|
317
|
+
| `--mode <mode>` | Evaluation mode | Use "transcript" for transcript-based evaluation |
|
|
318
|
+
|
|
319
|
+
### Check Status
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
indemn eval status <run-id> [--json]
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### List Runs
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
indemn eval list [--agent-id <id>] [--json] [--limit <n>] [--page <n>]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Analyzing Results
|
|
334
|
+
|
|
335
|
+
### Step 1: Resolve the Run
|
|
336
|
+
|
|
337
|
+
- **UUID format** (contains hyphens, 36 chars) -- it is a `run_id`. Use directly.
|
|
338
|
+
- **ObjectId format** (24 hex chars) -- it is an `agent_id`. Fetch the latest completed run:
|
|
339
|
+
```bash
|
|
340
|
+
indemn eval list --agent-id <agent-id> --json --limit 5
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Step 2: Pull All Data
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
indemn eval status <run-id> --json
|
|
347
|
+
indemn eval results <run-id> --json
|
|
348
|
+
indemn rubric get <rubric-id> --json
|
|
349
|
+
indemn testset get <test-set-id> --json
|
|
350
|
+
indemn eval bot-context <agent-id> --json
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
For exact response shapes, see `references/data-shapes.md`.
|
|
354
|
+
|
|
355
|
+
### Step 3: Present Run Overview
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
Run: {run_id}
|
|
359
|
+
Agent: {bot_name} ({agent_id})
|
|
360
|
+
LLM: {bot_llm_provider}/{bot_llm_model}
|
|
361
|
+
Status: {passed}/{total} items passed ({pass_rate}%)
|
|
362
|
+
|
|
363
|
+
Criteria: {criteria_passed}/{criteria_total} checks passed
|
|
364
|
+
Rubric: {rubric_rules_passed}/{rubric_rules_total} rules passed
|
|
365
|
+
|
|
366
|
+
Component Scores:
|
|
367
|
+
prompt: {score}% ({passed}/{total})
|
|
368
|
+
knowledge_base: {score}% ({passed}/{total})
|
|
369
|
+
function: {score}% ({passed}/{total})
|
|
370
|
+
general: {score}% ({passed}/{total})
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Step 4: Analyze Each Failed Item
|
|
374
|
+
|
|
375
|
+
For every result where `passed == false`:
|
|
376
|
+
|
|
377
|
+
1. **Get the test item** -- match `test_case_id` to the test set's `items[].item_id` for name, type, inputs, success_criteria
|
|
378
|
+
2. **Check criteria scores** -- from `criteria_scores[]`, list which passed/failed with reasoning
|
|
379
|
+
3. **Check rubric scores** -- from `rubric_scores[]`, list which passed/failed with reasoning
|
|
380
|
+
4. **Check for divergence** -- flag when `criteria_passed != rubric_passed`. Divergence is a strong signal of an evaluation problem.
|
|
381
|
+
5. **Read the conversation** -- look at `output` field for the full response or transcript
|
|
382
|
+
6. **Cross-reference system prompt** -- check if the agent's behavior aligns with prompt instructions
|
|
383
|
+
7. **Classify** into one of four categories:
|
|
384
|
+
|
|
385
|
+
| Category | Signal | Action |
|
|
386
|
+
|----------|--------|--------|
|
|
387
|
+
| **Real Agent Problem** | Both layers agree agent misbehaved, or output contradicts prompt | Investigate agent code/prompt |
|
|
388
|
+
| **Evaluation Problem** | Criteria ambiguous/too strict, evaluator reasoning shows misunderstanding | Update test set criteria |
|
|
389
|
+
| **Test Design Problem** | Test assumptions wrong (e.g., assumed KB lacked info it has) | Redesign test item |
|
|
390
|
+
| **KB/Retrieval Problem** | Agent behavior correct but retrieval returned wrong content | Fix KB content or retrieval config |
|
|
391
|
+
|
|
392
|
+
### Divergence Analysis
|
|
393
|
+
|
|
394
|
+
When `criteria_passed != rubric_passed` for an item, this is a first-class finding:
|
|
395
|
+
|
|
396
|
+
- **Criteria FAIL + Rubric PASS** -- criteria are likely too strict or testing the wrong thing
|
|
397
|
+
- **Criteria PASS + Rubric FAIL** -- rubric may have rules that don't apply to this scenario, or agent violated a behavioral rule despite answering correctly
|
|
398
|
+
|
|
399
|
+
Always highlight divergences prominently.
|
|
400
|
+
|
|
401
|
+
### Step 5: Aggregate and Recommend
|
|
402
|
+
|
|
403
|
+
Group failures by category. Prioritize fixes:
|
|
404
|
+
|
|
405
|
+
- **P1 -- Evaluation fixes** (false failures mask real issues): specific criteria rewrites or rubric rule adjustments
|
|
406
|
+
- **P2 -- Agent investigation** (real bugs): which code paths or prompt sections to examine
|
|
407
|
+
- **P3 -- KB/Retrieval fixes**: which KBs need content updates or retrieval tuning
|
|
408
|
+
|
|
409
|
+
For each recommendation, quote the current criterion/rule, explain what is wrong, and propose the exact replacement text.
|
|
410
|
+
|
|
411
|
+
### Step 6: Apply Fixes
|
|
412
|
+
|
|
413
|
+
Updates create new versions (non-destructive, previous versions preserved):
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
indemn testset update <test-set-id> --file updated-testset.json
|
|
417
|
+
indemn rubric update <rubric-id> --file updated-rubric.json
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Check version history: `indemn testset versions <id>` / `indemn rubric versions <id>`
|
|
421
|
+
|
|
422
|
+
After applying fixes, re-run:
|
|
423
|
+
```bash
|
|
424
|
+
indemn eval run <agent-id> --test-set <id> --rubric <id> --wait
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Exporting Results
|
|
430
|
+
|
|
431
|
+
### Markdown Export
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
indemn eval export <run-id> [--output results.md]
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Produces a structured markdown file with all conversations, criteria scores, and rubric scores.
|
|
438
|
+
|
|
439
|
+
### PDF Report
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
indemn eval report <run-id> [--output report.pdf]
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Produces a branded Indemn PDF with cover page, dual scores (criteria + rubric), and per-item detail pages.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Workflows
|
|
450
|
+
|
|
451
|
+
### A: New Evaluation (End-to-End)
|
|
452
|
+
|
|
453
|
+
1. Gather intent -- which agent, any focus areas
|
|
454
|
+
2. Fetch bot context: `indemn eval bot-context <agent-id> --json`
|
|
455
|
+
3. Run directive allocation (Steps 1-6 above)
|
|
456
|
+
4. Create rubric from universal directives: `indemn rubric create --name "<name>" --agent-id <id> --file /tmp/rubric.json`
|
|
457
|
+
5. Create test set with workflow-specific criteria: `indemn testset create --name "<name>" --agent-id <id> --file /tmp/testset.json`
|
|
458
|
+
6. Review (interactive: wait for approval)
|
|
459
|
+
7. Run: `indemn eval run <id> --test-set <ts-id> --rubric <r-id> --wait` (add `--concurrency 1` for voice_simulation)
|
|
460
|
+
8. Analyze results: `indemn eval results <run-id> --json`
|
|
461
|
+
|
|
462
|
+
### B: Re-run with Existing Resources
|
|
463
|
+
|
|
464
|
+
1. List existing: `indemn rubric list --agent-id <id>`, `indemn testset list --agent-id <id>`
|
|
465
|
+
2. Run: `indemn eval run <id> --test-set <ts-id> --rubric <r-id> --wait`
|
|
466
|
+
|
|
467
|
+
### C: Update Existing Resources
|
|
468
|
+
|
|
469
|
+
1. Get current: `indemn rubric get <id> --json` / `indemn testset get <id> --json`
|
|
470
|
+
2. Write updated JSON to file
|
|
471
|
+
3. Update: `indemn rubric update <id> --file /tmp/updated.json` / `indemn testset update <id> --file /tmp/updated.json`
|
|
472
|
+
|
|
473
|
+
### D: Analyze Results Only
|
|
474
|
+
|
|
475
|
+
1. Resolve run (UUID or agent-id)
|
|
476
|
+
2. Follow the Analyzing Results workflow above
|
|
477
|
+
|
|
478
|
+
### E: Export/Share Results
|
|
479
|
+
|
|
480
|
+
1. Markdown: `indemn eval export <run-id> [--output path.md]`
|
|
481
|
+
2. PDF: `indemn eval report <run-id> [--output path.pdf]`
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## CLI Reference
|
|
486
|
+
|
|
487
|
+
| Command | Purpose |
|
|
488
|
+
|---------|---------|
|
|
489
|
+
| `indemn eval bot-context <id> [--json]` | Get agent prompt, tools, KBs, LLM config |
|
|
490
|
+
| `indemn eval run <id> --test-set <id> [--rubric <id>] [--wait]` | Trigger an evaluation run |
|
|
491
|
+
| `indemn eval list [--agent-id <id>] [--json] [--limit] [--page]` | List evaluation runs |
|
|
492
|
+
| `indemn eval status <run-id> [--json]` | Get run status and summary |
|
|
493
|
+
| `indemn eval results <run-id> [--json]` | Get per-item detailed results |
|
|
494
|
+
| `indemn eval export <run-id> [--output <path>]` | Export results as markdown |
|
|
495
|
+
| `indemn eval report <run-id> [--output <path>]` | Export results as branded PDF |
|
|
496
|
+
| `indemn rubric create [--file <path>] [--name "<name>"] [--agent-id <id>]` | Create rubric (--file or --name required) |
|
|
497
|
+
| `indemn rubric get <id> [--json] [--revision <n>]` | Get rubric details |
|
|
498
|
+
| `indemn rubric list [--agent-id <id>] [--json] [--limit] [--page]` | List rubrics |
|
|
499
|
+
| `indemn rubric update <id> [--file <path>] [--name "<name>"]` | Update rubric (creates new version) |
|
|
500
|
+
| `indemn rubric delete <id>` | Delete rubric (requires confirmation) |
|
|
501
|
+
| `indemn rubric versions <id> [--json]` | List rubric version history |
|
|
502
|
+
| `indemn testset create [--file <path>] [--name "<name>"] [--agent-id <id>]` | Create test set (--file or --name required) |
|
|
503
|
+
| `indemn testset get <id> [--json] [--revision <n>]` | Get test set details |
|
|
504
|
+
| `indemn testset list [--agent-id <id>] [--json] [--limit] [--page]` | List test sets (always use --agent-id) |
|
|
505
|
+
| `indemn testset update <id> [--file <path>] [--name "<name>"]` | Update test set (creates new version) |
|
|
506
|
+
| `indemn testset delete <id>` | Delete test set (requires confirmation) |
|
|
507
|
+
| `indemn testset versions <id> [--json]` | List test set version history |
|
|
508
|
+
| `indemn config get <id> --field prompt` | Get just the system prompt |
|
|
509
|
+
| `indemn functions list <id>` | List agent's functions |
|
package/src/cli/auth.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import * as readline from 'readline';
|
|
5
|
+
import { Writable } from 'node:stream';
|
|
5
6
|
import { login, logout, whoami } from '../sdk/auth.js';
|
|
6
7
|
import type { Environment } from '../sdk/types.js';
|
|
7
8
|
import { printError, printSuccess, printJson } from './utils/output.js';
|
|
@@ -21,52 +22,20 @@ function prompt(question: string): Promise<string> {
|
|
|
21
22
|
|
|
22
23
|
function promptPassword(question: string): Promise<string> {
|
|
23
24
|
return new Promise((resolve) => {
|
|
25
|
+
const mutedOutput = new Writable({
|
|
26
|
+
write(_chunk, _encoding, callback) { callback(); },
|
|
27
|
+
});
|
|
24
28
|
const rl = readline.createInterface({
|
|
25
29
|
input: process.stdin,
|
|
26
|
-
output:
|
|
30
|
+
output: mutedOutput,
|
|
31
|
+
terminal: true,
|
|
27
32
|
});
|
|
28
|
-
|
|
29
|
-
// Disable echoing by writing to stdout manually
|
|
30
33
|
process.stdout.write(question);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let password = '';
|
|
39
|
-
const onData = (char: Buffer) => {
|
|
40
|
-
const c = char.toString('utf8');
|
|
41
|
-
if (c === '\n' || c === '\r' || c === '\u0004') {
|
|
42
|
-
if (stdin.isTTY) {
|
|
43
|
-
stdin.setRawMode(wasRaw ?? false);
|
|
44
|
-
}
|
|
45
|
-
stdin.removeListener('data', onData);
|
|
46
|
-
process.stdout.write('\n');
|
|
47
|
-
rl.close();
|
|
48
|
-
resolve(password);
|
|
49
|
-
} else if (c === '\u0003') {
|
|
50
|
-
// Ctrl+C
|
|
51
|
-
if (stdin.isTTY) {
|
|
52
|
-
stdin.setRawMode(wasRaw ?? false);
|
|
53
|
-
}
|
|
54
|
-
stdin.removeListener('data', onData);
|
|
55
|
-
rl.close();
|
|
56
|
-
process.exit(1);
|
|
57
|
-
} else if (c === '\u007f' || c === '\b') {
|
|
58
|
-
// Backspace
|
|
59
|
-
if (password.length > 0) {
|
|
60
|
-
password = password.slice(0, -1);
|
|
61
|
-
process.stdout.write('\b \b');
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
password += c;
|
|
65
|
-
process.stdout.write('*');
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
stdin.on('data', onData);
|
|
34
|
+
rl.question('', (answer) => {
|
|
35
|
+
process.stdout.write('\n');
|
|
36
|
+
rl.close();
|
|
37
|
+
resolve(answer);
|
|
38
|
+
});
|
|
70
39
|
});
|
|
71
40
|
}
|
|
72
41
|
|
|
@@ -74,10 +43,10 @@ export function registerAuthCommands(program: Command): void {
|
|
|
74
43
|
program
|
|
75
44
|
.command('login')
|
|
76
45
|
.description('Authenticate with Indemn platform')
|
|
77
|
-
.option('--env <environment>', 'Target environment'
|
|
46
|
+
.option('--env <environment>', 'Target environment')
|
|
78
47
|
.action(async (options) => {
|
|
79
48
|
try {
|
|
80
|
-
const env = options.env as Environment;
|
|
49
|
+
const env = (options.env || process.env.INDEMN_ENV || 'dev') as Environment;
|
|
81
50
|
if (env !== 'dev' && env !== 'production') {
|
|
82
51
|
printError('Environment must be "dev" or "production"');
|
|
83
52
|
process.exit(1);
|
|
@@ -116,7 +85,12 @@ export function registerAuthCommands(program: Command): void {
|
|
|
116
85
|
console.log(chalk.dim(` API Key: ${result.apiKey.substring(0, 12)}...`));
|
|
117
86
|
} catch (err) {
|
|
118
87
|
spinner.fail('Authentication failed');
|
|
119
|
-
|
|
88
|
+
if (err instanceof Error) {
|
|
89
|
+
printError(err.message);
|
|
90
|
+
} else {
|
|
91
|
+
printError(err);
|
|
92
|
+
}
|
|
93
|
+
process.exit(1);
|
|
120
94
|
}
|
|
121
95
|
} catch (err) {
|
|
122
96
|
printError(err);
|
package/src/cli/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { cpSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
3
6
|
import { registerAuthCommands } from './auth.js';
|
|
4
7
|
import { registerAgentCommands } from './agents.js';
|
|
5
8
|
import { registerConfigCommands } from './config.js';
|
|
@@ -65,4 +68,53 @@ program
|
|
|
65
68
|
} catch (err) { printError(err); process.exit(1); }
|
|
66
69
|
});
|
|
67
70
|
|
|
71
|
+
// Init command — set up Claude Code integration
|
|
72
|
+
program
|
|
73
|
+
.command('init')
|
|
74
|
+
.description('Set up Claude Code skills in the current project')
|
|
75
|
+
.option('--force', 'Overwrite existing files')
|
|
76
|
+
.action(async (options) => {
|
|
77
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
78
|
+
const __dirname = dirname(__filename);
|
|
79
|
+
const packageRoot = resolve(__dirname, '..', '..');
|
|
80
|
+
const skillsSource = join(packageRoot, 'skills');
|
|
81
|
+
const cwd = process.cwd();
|
|
82
|
+
const skillsTarget = join(cwd, '.claude', 'skills', 'indemn');
|
|
83
|
+
const rulesTarget = join(cwd, '.claude', 'rules');
|
|
84
|
+
|
|
85
|
+
if (existsSync(skillsTarget) && !options.force) {
|
|
86
|
+
console.log('Indemn skills already installed. Use --force to overwrite.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Copy skills
|
|
91
|
+
mkdirSync(skillsTarget, { recursive: true });
|
|
92
|
+
cpSync(skillsSource, skillsTarget, { recursive: true });
|
|
93
|
+
|
|
94
|
+
// Write rules file
|
|
95
|
+
mkdirSync(rulesTarget, { recursive: true });
|
|
96
|
+
const rulesPath = join(rulesTarget, 'indemn.md');
|
|
97
|
+
if (!existsSync(rulesPath) || options.force) {
|
|
98
|
+
writeFileSync(rulesPath, [
|
|
99
|
+
'# Indemn CLI',
|
|
100
|
+
'',
|
|
101
|
+
'The `indemn` CLI is available for managing AI agents. Run `indemn whoami` to verify authentication.',
|
|
102
|
+
'',
|
|
103
|
+
'## Skills',
|
|
104
|
+
'',
|
|
105
|
+
'- **agent-builder** — Create, configure, and test agents (prompts, functions, knowledge bases, chat)',
|
|
106
|
+
'- **evaluations** — Create rubrics and test sets, run evaluations, analyze results, export reports',
|
|
107
|
+
'',
|
|
108
|
+
'Use these skills for any agent management or evaluation task. They contain the complete command reference.',
|
|
109
|
+
'',
|
|
110
|
+
].join('\n'));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log('Indemn CLI integration set up:');
|
|
114
|
+
console.log(` Skills: ${skillsTarget}/`);
|
|
115
|
+
console.log(` Rules: ${rulesPath}`);
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log('Claude Code will now discover the indemn skills automatically.');
|
|
118
|
+
});
|
|
119
|
+
|
|
68
120
|
program.parse();
|