ishaq101's picture
/fix validator and report (#10)
0e5fdb5
|
Raw
History Blame Contribute Delete
4.2 kB
# Intent-Routing Eval (E3)
Scores the live 6-intent router (`OrchestratorAgent.classify`) against a golden
dataset of labelled messages. Run it before any deploy that touches the router
prompt (`src/config/prompts/intent_router.md`) or its few-shots.
## Files
| File | What |
|---|---|
| `intent_dataset.json` | **Golden dataset** β€” `message` + known-correct `expected_intent` per case. The source of truth scoring compares against. |
| `run_eval.py` | Runner β€” calls the router per case, scores correctness, records latency + tokens. |
| `results/` | Timestamped run reports, one JSON per run (never overwritten). |
## Run
Run as a module (`-m`), not the file path β€” module mode puts the repo root on
`sys.path` so `src` imports resolve; `python eval/intent/run_eval.py` fails.
```bash
uv run python -m eval.intent.run_eval # full dataset
uv run python -m eval.intent.run_eval --limit 6 # quick smoke test
uv run python -m eval.intent.run_eval --langfuse # also stream traces to Langfuse
```
Needs a populated `.env` (Azure OpenAI) β€” it calls the live model and spends
tokens. Output: a per-case detail table + an aggregate summary in the terminal,
and `results/eval_result_<timestamp>.json`.
**Tracking is the committed result files, not Langfuse** β€” the JSON reports in
`results/` are the versionable audit trail (see below). `--langfuse` is an
*optional* extra: when set, each case is also sent as a Langfuse trace (grouped
under one `intent_eval_<ts>` session) with a `intent_correct` 1/0 score, so the
same run is browsable in the Langfuse dashboard. It is off by default and the
eval runs fully without Langfuse configured.
## What's measured
- **correctness** β€” overall + per-intent + per-language accuracy (`got == expected`)
- **runtime** β€” average ms per case
- **tokens** β€” input / output / total (read from the model response, no Langfuse)
### Content-filter blocks count as `out_of_scope` passes
Aggressive jailbreak / manipulation inputs are often rejected by Azure's own
content filter (HTTP 400, `code=content_filter`) *before* the router model runs.
The live app treats that as a refusal (`chat_handler._is_content_filter_error`),
so for an `out_of_scope` case the block **is** the correct end-to-end outcome. The
runner mirrors this: such a case is recorded as `got=blocked` and scored **correct**
(not `ERROR:BadRequestError`). This keeps `out_of_scope` accuracy honest β€” the
router isn't penalised for inputs the platform guardrail caught first. A
content-filter block on any *other* expected intent is still scored wrong (an
unexpected block). Non-filter exceptions remain `ERROR:<type>` and score wrong.
## Commit convention for `results/`
The reports are **versionable**, not a scratch log:
- **Do commit** a result after a meaningful change β€” e.g. a new
`intent_router.md` version, or new dataset cases. The new timestamped file
*adds* to the history; old files are never replaced. This is how we answer
"did accuracy improve after prompt v2?" β€” diff two committed result files.
- **Don't commit** throwaway runs while iterating. Just leave them unstaged or
delete them.
So the audit trail = prompt versions (in `src/config/prompts/`) lined up against
the committed result files here.
## Dataset notes
- 6 intents: `chat`, `help`, `check`, `unstructured_flow`, `structured_flow`,
`out_of_scope`. Each has 6+ **distinct** scenarios (not EN/ID translation
pairs), balanced across English + Indonesian. (`problem_statement` was dropped
from the router on 2026-06-24 β€” the goal is now user-entered `objective` +
`business_questions`, no agent validation β€” so its cases were removed here.)
- `carried_over: true` rows mirror the pre-rework `intent_router.md` examples
(regression). `lang` enables per-language scoring. `id` is a stable handle for
diffing the same case across runs.
- Routing labels are decided from the question **phrasing**, not from which file
holds the answer (the router has no catalog access). See the `_grounding` note
in `intent_dataset.json`.
- Owner: Rifqi (structured/DB-grounded rows) + Sofhia (unstructured/document +
tabular-file rows). Merge both into this one file.
```