Measured against the same two benchmarks used for every prior backend swap this project has made: - Urgency noul: von 0.9995 (vs. laya's 0.77, kev's inverted 0.17) -- essentially matches Jev's own claimed ~0.999, and correctly separates calm (0.059) from urgent. - costslayer's L1 task-scope Choice classification (external stress test, costslayer itself unchanged): von scored 0.737 average, actually *behind* laya's 0.77-0.80, with a real 25% under-classification rate. No backend wins both axes. Swapping anyway because Noul was this project's clearest weak spot (kev inverted it, laya was only middling), and because von ships its own /v1/systemone HTTP server -- removes the need for a custom wrapper (serve_laya.py deleted; laya had no server of its own). README's Known Limitations section now documents the Choice weakness plainly rather than only reporting the win. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
133 lines
5.1 KiB
Markdown
133 lines
5.1 KiB
Markdown
# sysone
|
|
|
|
A local, self-hosted typed-decision classifier. Instead of generating text,
|
|
it takes a `state` (some text or data) and a set of typed `questions`, and
|
|
returns calibrated probabilities:
|
|
|
|
- **Noul** — a yes/no question, answered as P(true).
|
|
- **Choice** — pick one of several labeled options, answered as a probability
|
|
per option.
|
|
- **Score** — place the input on an ordered scale, answered as a continuous
|
|
position on that scale plus a probability per level.
|
|
|
|
No chat model, no text generation, no hallucination risk — the model can
|
|
only ever return a number or a label from a set you defined. Runs entirely
|
|
on this machine: no API key, no external service, no per-call cost.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
sysone/ (this repo, stdlib-only, no dependencies)
|
|
types.py Choice / Score / Noul question specs + result types
|
|
client.py Classifier.invoke(state, questions) -> ClassifierResponse
|
|
|
|
│ HTTP POST /v1/systemone
|
|
▼
|
|
|
|
von serve (external process, ~/work/von/.venv; not part of this repo)
|
|
von-sdk's own built-in HTTP server -- speaks /v1/systemone natively,
|
|
no wrapper needed.
|
|
→ localhost:8011
|
|
|
|
von-1.0.0 (395M-param ModernBERT encoder, Apache 2.0, runs on MPS)
|
|
```
|
|
|
|
## Setup
|
|
|
|
**If the model server is already running**, skip to Usage. Check:
|
|
|
|
```bash
|
|
curl -s localhost:8011/v1/systemone -o /dev/null -w '%{http_code}\n' \
|
|
-H 'content-type: application/json' \
|
|
-d '{"state":"x","model":"von","questions":{"q":{"type":"noul","instructions":"x"}}}'
|
|
```
|
|
|
|
**From scratch:**
|
|
|
|
```bash
|
|
mkdir -p ~/work/von && cd ~/work/von
|
|
uv venv --python 3.12 # von-sdk requires Python >=3.12
|
|
uv pip install von-sdk
|
|
```
|
|
|
|
Run the server:
|
|
|
|
```bash
|
|
~/work/von/.venv/bin/von serve --port 8011 --backend modernbert --device mps
|
|
```
|
|
|
|
First run downloads the `von-1.0.0` checkpoint from Hugging Face (~1.5GB)
|
|
and serves on MPS (Apple Silicon GPU). Leave it running in its own terminal,
|
|
or background it. It's a plain background process, not a managed service —
|
|
if the machine restarts, re-run the command above. First startup after a
|
|
restart takes a few seconds to load the model; each request after that is
|
|
20-50ms.
|
|
|
|
## Usage
|
|
|
|
```python
|
|
from sysone import Classifier, Choice, Noul, Score
|
|
|
|
classifier = Classifier() # defaults to http://localhost:8011
|
|
|
|
response = classifier.invoke(
|
|
state="Our production database is down and customers can't check out.",
|
|
questions={
|
|
"urgent": Noul(instructions="Does this need attention right now?"),
|
|
"team": Choice(instructions="Which team should handle this?",
|
|
options=["billing", "technical", "sales"]),
|
|
"severity": Score(instructions="How severe is this?",
|
|
levels=["minor", "moderate", "major", "critical"]),
|
|
},
|
|
)
|
|
|
|
response.nouls["urgent"].noul # -> probability, e.g. 0.94
|
|
response.choices["team"].best # -> "technical"
|
|
response.choices["team"].probs # -> {"billing": 0.03, "technical": 0.92, "sales": 0.05}
|
|
response.scores["severity"].score # -> continuous index into `levels`, e.g. 2.6
|
|
```
|
|
|
|
Run the example and smoke test:
|
|
|
|
```bash
|
|
python3 examples/support_ticket.py # a worked example
|
|
python3 smoke_test.py # noul/choice/score sanity checks
|
|
```
|
|
|
|
## Wire format (`/v1/systemone`)
|
|
|
|
| Type | Request `criteria` | Response fields |
|
|
|---|---|---|
|
|
| `noul` | none | `noul`: P(yes) |
|
|
| `choice` | `{option: description}` | `choice`, `probabilities` (by option key), `confidence` |
|
|
| `score` | `[level_0, level_1, ...]` (low → high) | `score` (0-indexed position into levels), `legend`, `probabilities` (by index), `confidence` |
|
|
|
|
## Known limitations
|
|
|
|
- **Noul and Choice are not equally strong.** This client's own testing found
|
|
yes/no (Noul) judgments to be sharp and well-directed even on pragmatic,
|
|
non-factual questions (e.g. urgency). Multi-way Choice judgments on
|
|
fine-grained, domain-specific categories (tested: classifying software
|
|
tasks by scope) were noticeably weaker, with a real tendency to
|
|
under-predict how much a task actually requires. If your use case leans
|
|
heavily on Choice with many closely-related options, verify against your
|
|
own labeled examples before trusting it.
|
|
- **Raw confidence isn't necessarily calibrated for your task.** The
|
|
`confidence` field this client returns is the model's raw output for
|
|
whatever domain it was trained on. Fitting your own temperature scalar on
|
|
a held-out set is worth doing before using confidence for anything
|
|
threshold-based (e.g. "auto-act above 0.85, else escalate").
|
|
- **High-cardinality choice questions are untested here.** All examples in
|
|
this repo use ≤5 options; large option sets are unverified.
|
|
- **English-only.** Not exercised with non-English input.
|
|
|
|
## Possible next steps
|
|
|
|
- Fine-tune or otherwise adapt the Choice head for domain-specific
|
|
multi-category tasks if that's your primary use case.
|
|
- Fit per-question-type temperature scaling on a held-out set before relying
|
|
on `confidence` for threshold-based decisions.
|
|
- A LangChain-style wrapper and middleware (model routing, tool-call
|
|
guardrails) built on top of this classifier are both natural extensions,
|
|
not implemented here — this repo is the core classifier only.
|