Classification Modes
Classification Modes
Two pages classify signals: Classify (one signal at a time) and Classify Bulk (a whole CSV or XLSX), each with an unsupervised and a supervised flavor.
Classify (single signal)
Route /classify. If any collection the classifier retrieves from
(Reference, ValidatedSignal, Master) has not been ingested on this
environment, a banner at the top of the page names it. Classification still
runs, but without that evidence its answers are weaker — seed it under
Config → Vector DB (see Context Enrichment).
No banner means all three hold data. The same banner appears on Classify Bulk,
which retrieves from exactly the same collections.
Fill in:
- Description (required) — the signal text, in any language.
- Technology — currently only Hydro is enabled (Wind/Solar are shown as "soon").
- Plant / RDS Top Node (required) — identifies which plant's context (schema, validated-signal history) to use.
- Context hint (optional) — a free-text operator note, e.g. "pump- turbine, unit 2".
- Plant Schema (optional, multi-select) — one or more parsed schemas for this plant (see Plant Schematics Ingestion) to inject as structural context.
- Advanced parameters (collapsible) — model, overall confidence threshold, and the reference/validated-signal similarity thresholds used during retrieval.
Submitting returns:
- The code, in the standard 8-field format.
- A confidence breakdown — an overall score plus a score for each step of the decision pipeline.
- The reasoning — the full classification history showing how the agent arrived at the result.
- Flags — a review suggestion when the agent itself is unsure (e.g. an ambiguous comparison match).
- Similar signals — the reference and previously-validated signals retrieved as supporting evidence.
- Validation errors, if the proposed
(lnclass, do, da, cdc)doesn't match a normatively valid combination.
There's no inline correction here — if the result is wrong, corrections happen through Classify Bulk's supervised flow, which is built around operator review.
Classifying from another program
POST /api/v1/classify runs the same single-signal classification for a
caller that is a program rather than a person at the page. It takes JSON:
| Field | Required | Meaning |
|---|---|---|
description |
yes | The signal text, in any language. |
rds_top_node |
yes | The plant, as listed by GET /api/plants. |
technology |
no | Defaults to hydro. |
plant_hint |
no | A free-text note, like the page's Context hint. |
It uses the page's default model and thresholds. Since a program cannot tick schemas, every parsed schema of the plant is injected as structural context.
The answer is JSON:
| Field | Meaning |
|---|---|
code |
The 8-field code, or null when none could be determined. |
matched |
Whether a reliable match was found among known signals. |
confidence |
level (high, medium, low) and the overall score. |
steps |
For each step of the decision: its label, its reasoning, and whether it was low_confidence. |
validation_errors |
As on the page. |
review_required |
true when an assigned code is absent from the plant schema. |
The similar signals retrieved as evidence and the raw output of each step are not part of the answer.
A classification that ran and found no code is still an answer: status 200
with code: null. When the classification could not run, the status says so
and no code is returned: 422 for a missing or blank field, 503 when the
vector database or the model cannot be reached, 500 for any other failure.
A caller that charges per answer can rely on that difference.
Reaching it needs the same permission as classifying from the page.
Classify Bulk (CSV / XLSX)
Route /bulk_classify, in two steps. Every run is saved automatically — see
History to reopen, edit,
resume, or delete a past run.
Step 1 — Import: pick a .csv or .xlsx file. For CSV, choose its
delimiter (or leave on Auto) — XLSX has no delimiter to set. Optionally cap
the run to a sample of N rows.
Step 2 — Configure the run:
- Pick which column holds the signal description.
- Set technology, plant/RDS top node, and context hint (same as single Classify).
- Choose which extra columns the results table shows (the export always carries every source column).
- Pick a Plant Schema, if relevant.
- Choose the mode:
- Unsupervised — every row streams straight through.
- Supervised — the stream pauses on any row whose confidence falls below a supervised threshold (default 0.90) and waits for you.
- Comparison — pick an existing classification column (e.g. a
full_designationcolumn with codes an operator already assigned outside this tool). Every row is classified as usual, then audited against that existing code using the same knowledge base, plant schema and retrieved evidence — producing a verdict (match / partial / mismatch), a field-by-field diff, and a recommended code. The stream pauses on any row that isn't an exact match and waits for you.
- Set model/threshold parameters, then click Classify.
ID column
Pick the column that identifies each signal in your source system (for example
Address or Tag). It is what lets you match every classified row back to the
original file. The form preselects a column whose values are all present and
unique, preferring names such as id, address or tag; you can change it or
choose none (use row number) — in that case the row number matches the
uploaded file's row unless a row limit or sampling was used, so pick an ID
column instead if you need reliable reconciliation in that case.
If the column you pick has duplicate or empty values, the run does not start
straight away: a warning lists the affected rows. Start anyway runs it and
marks it IDs not unique in History; Choose another column lets you fix the
choice. The ID column is always shown right after # in the results table.
Export
Export CSV and Export XLSX download the run as saved in History: every
row of the uploaded file (rows not classified yet are marked pending) and every
source column, followed by the classification. Ticked output columns choose
what the results table shows; the export always carries every source column,
regardless of which ones are ticked. See Bulk classification history for the
column layout.
Real-time progress
Results stream in via Server-Sent Events as each row finishes — you don't
wait for the whole file. A progress bar and a live "Classifying N/total"
counter track overall progress. Pause takes effect immediately: if a
signal is being classified at that moment, that classification is abandoned
and the signal is left unclassified — it's re-classified when the run
resumes. The run's status becomes paused and everything classified so far
is saved, so closing the page or the browser without pressing Pause has the
same effect. Resume (from History)
picks the run back up on this same page — useful for stepping away mid-run
without losing place.
A resumed page has no configure form, so the settings the run started with are shown instead under Run parameters, a collapsed panel above the results: file and plant, description and ID columns, output columns, mode and its threshold, model, retrieval thresholds, plant schemas and the context hint. They are read-only — the rest of the file is always classified under the settings the run began with.
Retrying a failed row
A row shown in red with an Error tag could not be classified at all — usually for a reason unrelated to the signal, such as a timeout or a rate limit from the model. Resuming does not retry it: the row already has an outcome. Click it instead: the side panel shows the error and a Retry classification button, which classifies that signal again with the run's own settings and conversation, and replaces the row in the table with the result. If it fails again, the row stays red and shows the new error.
A retry is not a correction: nothing is added to the learning store or to the conversation as operator feedback. It is unavailable while the run is still classifying (pause it first), and for a row with an empty description, where there is nothing to classify. The run's ok/errors counts in History are updated; the summary chips at the top of the results keep the totals of the run as it ended.
Arranging the results table
Columns start at the width of what they hold. A run that carries a dozen source columns would otherwise split the space equally and leave each one a couple of characters wide. During a live run the columns size themselves on the first rows and widen for a later row that does not fit; no column sizes itself past 420px unasked, and dragging one is always free of that limit.
Four gestures, all on the column headers:
| Gesture | What it does |
|---|---|
| Drag the right edge of a header | Resizes that column, narrower than its contents if you want — what does not fit is cut off |
| Double-click the right edge | Fits the column to its widest cell, shrinking an over-wide column as readily as widening a cramped one |
| Drag a header by its label | Moves the column; the column you drop on shows which side it will land |
| Double-click a header's label | Restores that column's default width |
Widths and order are remembered per page in your browser, so they survive a reload, a new run and resuming a paused one. They are yours alone — nothing is stored on the server and nothing changes for anyone else. The order is tied to the set of columns it was set for, so a file with different columns starts from the default arrangement rather than a half-matching one.
Widening a column widens the table rather than squeezing its neighbours: the other columns keep the width you gave them and the results scroll sideways. Fitting to contents measures the first 500 rows — enough to size a column sensibly without stalling a long run.
The export is not affected. CSV and XLSX always carry the server's column
order (source columns, then the iec_ results), whatever you do on screen.
The same gestures work on the tables on the History run detail page.
Operator feedback (supervised mode)
When a row's confidence is below the threshold, a feedback modal opens with:
- The signal description and the proposed code (pre-filled and editable — type a different code to correct it directly).
- The full classification detail for that row (reasoning, retrieved evidence) loaded on demand.
- A free-text field for feedback, e.g. explaining why the right code is different.
- Send feedback (apply it and continue) or Skip (continue without correcting).
Whatever you submit is immediately folded into that run's conversation memory — the LLM sees your correction as context for every signal classified afterwards in the same run, so it doesn't repeat the mistake. It's also persisted to the SQLite learning store (see Learning from Corrections).
You can also revisit any already-classified row: click it to open a side panel with a Re-classify form, where you can give feedback or a direct correction after the fact — the correction is folded into the conversation memory the same way.
While a feedback modal is waiting for you, the run stays stopped on that signal until you answer that modal (Send feedback or Skip) or pause the run. Re-classifying an earlier row from the side panel in the meantime corrects that row only: it does not answer the open review, does not close the modal, and does not let the run move on to the next signal.
Comparison mode
Comparison mode reuses the same feedback modal as supervised mode, but the pause condition and the modal content differ:
- The stream pauses only when the audit verdict is partial or mismatch — never on confidence alone.
- The row detail panel (and the modal, loaded from it) shows a "Comparison
with existing classification" section: the existing code, this system's
code, a field-by-field agreement table, the verdict, and a recommended
code with its source (
llm,operator, orhybrid). - The "Classified code" field in the feedback form is pre-filled with the recommended code rather than this system's raw classification — edit or accept it, then Send feedback as usual.
- The results table gets an extra Audit column (✓ match / ~ partial / ✕ mismatch) so you can scan agreement across the whole run at a glance.
- Both CSV and XLSX export gain three extra columns in this mode:
iec_existing_code,iec_audit_verdict,iec_recommended_code.
End of run
A summary popup reports matched / rules / ok / errors counts. From there you can:
- Export the results to CSV or XLSX — see Export above for what the file contains.
- Persist the run's signals into that plant's dedicated collection (see Context Enrichment & Plant Memory) — shown only when a plant was selected and at least one row succeeded.
- Open the Wiki rules / Inferred schema suggestion panels — see Learning from Corrections.