Merge pull request 'Routing heads: a slot head, a clarify head, and a two-week baseline to diff against' (#203) from task/661-routing-heads-step-3-train-the-multi-hea into master
This commit was merged in pull request #203.
This commit is contained in:
@@ -246,6 +246,72 @@ was a hardcode. **Fine-tune a copy of the weights.** The resident embedder backs
|
|||||||
recall. Training it in place couples routing accuracy to recall@1, with nothing in the
|
recall. Training it in place couples routing accuracy to recall@1, with nothing in the
|
||||||
suite to name the trade.
|
suite to name the trade.
|
||||||
|
|
||||||
|
**Two of those heads are trained as of 08-08-2026, and they are not the three
|
||||||
|
above** (V-661, `docs/evals/2026-08-08-routing-heads-two-head.md`). Intent and
|
||||||
|
destination share one masked mean pool. Destination scores a mean **80.8%** over
|
||||||
|
three seeds, best **29/33 (87.9%)**. The classifier cascade scores 12/33 and the
|
||||||
|
cascade with gemma-4-12b scores 24/33, so a 118M encoder beats the 12B teacher it
|
||||||
|
was distilled from. Read the best run as one seed and not a headline, because one
|
||||||
|
case is 3 points on a fixture this small.
|
||||||
|
Recall is 15/15 and world is 5/5. Intent is 93.6% mean over three seeds. That is
|
||||||
|
**not** comparable to the 76.0% and 84.4% those two arms scored: a softmax has no
|
||||||
|
clarify class, so the head's fixture is the 88 cases carrying an intent.
|
||||||
|
|
||||||
|
**A fourth head asks instead of guessing, same day** (V-661,
|
||||||
|
`docs/evals/2026-08-08-clarify-head-four-head.md`). Clarify is not a value
|
||||||
|
of intent, so a softmax cannot emit it. It is a second question over the
|
||||||
|
same pooled vector: can Maven act on this at all. That is why the head's
|
||||||
|
fixture was 88 cases and not 96. Over three seeds it catches **7.0 of the 8
|
||||||
|
`want_clarify` cases and produces 2.3 false clarifies of 88**. The cascade
|
||||||
|
today misses 1 and produces 2, so this is parity with no rules in front of
|
||||||
|
it. Accuracy is the wrong number here and a head that never asks scores
|
||||||
|
91.7%. Confidence is the other half. Max softmax over the intent head reads
|
||||||
|
**0.851 where it is right against 0.604 where it is wrong**, ranking right
|
||||||
|
above wrong in 83.4% of pairs. `Confidence: 1.0` was a hardcode, and this
|
||||||
|
replaces it with a signal. The two are not the same signal: one says which
|
||||||
|
intent is unclear, the other says the utterance carries too little to act
|
||||||
|
on. **The fourth head is not free the way the third was.** Intent,
|
||||||
|
destination and slot F1 each move down one to four points, inside the seed
|
||||||
|
spread. `поужинал` is a false clarify on every seed, which is the same
|
||||||
|
defect `thinSingleToken` was narrowed for on 2026-08-01.
|
||||||
|
|
||||||
|
The corpus for it is generated, because every existing row is answerable by
|
||||||
|
construction. **The router-prompt agreement filter cannot work here**, since
|
||||||
|
`routeGrammar` has no clarify value and a generated line always agrees with
|
||||||
|
itself. A gemma judge replaces it. The first judge called 24 of 40
|
||||||
|
answerable rows underspecified, because it judged against a generic
|
||||||
|
assistant rather than against Maven's contract.
|
||||||
|
|
||||||
|
**Mood is cut, not deferred.** The enum describes her own reply state, not the
|
||||||
|
speaker's emotion, and no dataset maps onto it.
|
||||||
|
|
||||||
|
**A third head landed the same day** (`docs/evals/2026-08-08-slot-head-three-head.md`).
|
||||||
|
BIO slot tags had no Maven-domain corpus, which was true of found corpora and
|
||||||
|
false of made ones. `label_slots.py` distils spans out of gemma-4-12b under a
|
||||||
|
GBNF closed over Maven's own five slots. A span survives only when it is a
|
||||||
|
literal substring of the utterance, so the agreement filter costs no second
|
||||||
|
call. 2178 spans over 1702 rows, 37 dropped, nothing unparsed. Three heads score
|
||||||
|
intent **92.8%**, destination **82.8%** and slot span F1 **72.4%** over three
|
||||||
|
seeds. The slot head is free: both other numbers move less than their own seed
|
||||||
|
spread. Epoch selection reads the intent dev slice alone. Slot F1 is still
|
||||||
|
climbing when it stops, which costs about 4 points.
|
||||||
|
|
||||||
|
The MASSIVE warm-start of step 2 is worth nothing here. Stock e5-small ties it on
|
||||||
|
intent and leads by a third of a case on destination. Nothing argues for keeping
|
||||||
|
that step.
|
||||||
|
|
||||||
|
The floor was a corpus defect and it is fixed. The first 120 floor rows carried
|
||||||
|
one sentence shape, so the head named a destination where the fixture says walk
|
||||||
|
the chain. Rotating six shapes took the floor 3/7 to 6/7 and destination 75.8% to
|
||||||
|
80.8%. What is left is calendar at 3/6 on every seed, which training cannot move:
|
||||||
|
the possessive agenda rules claim those cases at stage 0 and name nothing, so no
|
||||||
|
label reaches the head. That is the same trade V-660 flagged and it wants the
|
||||||
|
owner's call.
|
||||||
|
|
||||||
|
**Nothing of this runs in Go.** The weights are `heads.pt` and `out/body_heads/`
|
||||||
|
on workpc. Reaching the daemon needs an ONNX export and a caller. The resident
|
||||||
|
e5-small must not be replaced by the copy, because recall depends on that file.
|
||||||
|
|
||||||
`Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM
|
`Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM
|
||||||
path could never ask for clarification (6/6 refusal cases missed on the fixture) — Vikunja
|
path could never ask for clarification (6/6 refusal cases missed on the fixture) — Vikunja
|
||||||
#359. Fixed 31-07-2026 with structural signal (single-token utterance, keyless fact, act with
|
#359. Fixed 31-07-2026 with structural signal (single-token utterance, keyless fact, act with
|
||||||
@@ -376,7 +442,19 @@ queries exactly as it did.
|
|||||||
That is the safety argument and it is not negotiable. The table's order is
|
That is the safety argument and it is not negotiable. The table's order is
|
||||||
load-bearing. Every comment on it argues a reason between two sources, and above all
|
load-bearing. Every comment on it argues a reason between two sources, and above all
|
||||||
it carries "the owner's data first, then the world". Naming `SourceWorld` does not
|
it carries "the owner's data first, then the world". Naming `SourceWorld` does not
|
||||||
send the turn outside. His notes, his facts and the personal boundary still run first.
|
send the turn outside on its own. His notes and his facts still run first, because
|
||||||
|
they look rather than guess.
|
||||||
|
|
||||||
|
**The personal boundary is the one exception and it is deliberate.** It guesses,
|
||||||
|
so naming `SourceWorld` drops it. That is what stops it answering "кто такой
|
||||||
|
Линус Торвальдс?" with "не нашла у тебя такой записи", which it did on
|
||||||
|
2026-08-07. The cost is that a destination a model wrote can now take the
|
||||||
|
boundary off a turn. A question about him that the model calls `world` reaches
|
||||||
|
SearXNG, where today the boundary stops it. Only the utterance leaves the box,
|
||||||
|
never his notes or history, so this widens what is asked and not what is sent.
|
||||||
|
`TestNamingRecallKeepsTheBoundary` pins the other half: naming `SourceRecall`
|
||||||
|
keeps the boundary in front of the world. Whether a model may drop it at all is
|
||||||
|
the owner's call and has not been made.
|
||||||
|
|
||||||
What comes out is only the sources that **guess**. Those decide a turn is theirs by
|
What comes out is only the sources that **guess**. Those decide a turn is theirs by
|
||||||
cosine against frozen seeds, then answer whatever they claimed. They hold no table
|
cosine against frozen seeds, then answer whatever they claimed. They hold no table
|
||||||
@@ -422,11 +500,14 @@ rules deliberately do not name it. And **recall is 0/15, because nothing
|
|||||||
anywhere names it**. Those turns are still answered, since the chain walks
|
anywhere names it**. Those turns are still answered, since the chain walks
|
||||||
recall early. Recall is the number the fourth head has to move.
|
recall early. Recall is the number the fourth head has to move.
|
||||||
|
|
||||||
Seven cases assert the floor and six of them are homelab operations. They
|
Seven cases assert the floor and five of them are homelab operations. They
|
||||||
cluster because `SourceRecall`, `SourceNetwork` and `SourceAttention` overlap on
|
cluster because `SourceRecall`, `SourceNetwork` and `SourceAttention` overlap on
|
||||||
every question about the box. `mavpoll` writes its netdata and uptime-kuma
|
every question about the box. The other two are `ru-query-005` and
|
||||||
|
`ru-query-014`. No query source reads the reminder store, and a deadline could
|
||||||
|
sit in tasks, the calendar or Praxis. `mavpoll` writes its netdata and uptime-kuma
|
||||||
observations into the fact store recall reads. That is a finding about the enum,
|
observations into the fact store recall reads. That is a finding about the enum,
|
||||||
not a gap in the labelling.
|
not a gap in the labelling. The owner confirmed all seven floor labels on
|
||||||
|
08-08-2026, so they are a decision rather than an agent's guess.
|
||||||
|
|
||||||
`baselineGrammars` in `eval_test.go` mirrors `buildRouter` and had drifted:
|
`baselineGrammars` in `eval_test.go` mirrors `buildRouter` and had drifted:
|
||||||
`WorldQueryGrammars` was wired into the daemon by V-655 and not into the mirror,
|
`WorldQueryGrammars` was wired into the daemon by V-655 and not into the mirror,
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# A clarify head, and a confidence that is not a hardcode
|
||||||
|
|
||||||
|
Measured 2026-08-08 on workpc, the same day and the same fixtures as
|
||||||
|
`2026-08-08-routing-heads-two-head.md` and `2026-08-08-slot-head-three-head.md`.
|
||||||
|
New script `gen_clarify.py`, new fixture `eval_fixture_clarify.jsonl`.
|
||||||
|
|
||||||
|
## A softmax has no clarify class
|
||||||
|
|
||||||
|
That sentence closed the two-head measurement. It is why the head's fixture was
|
||||||
|
88 cases and not 96. The eight `want_clarify` cases sat outside every number
|
||||||
|
measured, and the head had no way to produce the answer they wanted.
|
||||||
|
|
||||||
|
A fourth head is the answer. Clarify is not a value of intent. It is a second
|
||||||
|
question asked of the same pooled vector: can Maven act on this at all.
|
||||||
|
|
||||||
|
## The corpus had one class
|
||||||
|
|
||||||
|
Every row in `train_heads_slots.jsonl` was generated FOR an intent or a
|
||||||
|
destination. So every row is answerable by construction. A head trained on that
|
||||||
|
alone sees one class and learns to say yes.
|
||||||
|
|
||||||
|
`gen_clarify.py` makes the other class. Five shapes, ten topics. The shapes are
|
||||||
|
the gate's own reasons in `gateLLMDecision` plus the two the fixture carries:
|
||||||
|
bare noun, bare verb, demonstrative, deictic time, dangling reference.
|
||||||
|
|
||||||
|
**The agreement filter that worked for destination cannot work here.**
|
||||||
|
`routeGrammar` has no clarify value. So the router always names an intent, and
|
||||||
|
any generated line always agrees with itself. The second pass is a judge
|
||||||
|
instead. Gemma is asked, without seeing the label, whether Maven would have to
|
||||||
|
ask a question back.
|
||||||
|
|
||||||
|
## The first judge was worthless and the second was measured
|
||||||
|
|
||||||
|
The first judge said "needs clarify" on 24 of 40 plainly answerable corpus
|
||||||
|
rows. It flagged `запиши что я пообедал` and `Покажи расписание поездов на
|
||||||
|
вечер`. It was judging against a generic assistant, one that asks "where?"
|
||||||
|
about lunch. Maven writes that note.
|
||||||
|
|
||||||
|
Rewriting it to state what she can already do took false positives to 16 of 60.
|
||||||
|
It also catches all eight fixture clarifies. So the judge discriminates.
|
||||||
|
|
||||||
|
On the generated pile it removed 7 of 306, a 97.7% keep rate. That is not the
|
||||||
|
judge failing. The generator is aimed at underspecified lines, so there is
|
||||||
|
little for a filter to catch. The 27% false-positive rate is the number to
|
||||||
|
quote, and it is label noise on the positive class.
|
||||||
|
|
||||||
|
**Both passes are gemma-4-12b.** Generation and judging. So the corpus is
|
||||||
|
gemma's opinion of what is underspecified, and the head distills that opinion.
|
||||||
|
What keeps it honest is the fixture. Those eight cases were written by the owner
|
||||||
|
and gemma never saw them.
|
||||||
|
|
||||||
|
299 rows kept, against 3604 answerable. The positive class carries `intent:
|
||||||
|
null`, so it costs the intent head nothing.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
Three seeds, 24 epochs, epoch still chosen on the intent dev slice.
|
||||||
|
|
||||||
|
| | two heads | three heads | four heads |
|
||||||
|
|---|---|---|---|
|
||||||
|
| intent mean | 93.6% | 92.8% | 91.7% |
|
||||||
|
| destination mean | 80.8% | 82.8% | 79.8% |
|
||||||
|
| slot span F1 mean | — | 72.4% | 68.3% |
|
||||||
|
| clarify caught | — | — | 7.0 of 8 |
|
||||||
|
| false clarifies | — | — | 2.3 of 88 |
|
||||||
|
|
||||||
|
**The fourth head is not free the way the third was.** Intent, destination and
|
||||||
|
slot F1 all move down. The drop is one to four points, and the seed spread is
|
||||||
|
wide enough to contain it. Seed 2 scores intent 94.3% and destination 84.8%, both above
|
||||||
|
every three-head seed. Read the drop as unproven rather than as absent.
|
||||||
|
|
||||||
|
Accuracy is the wrong number for this head and is reported for completeness at
|
||||||
|
95.8% to 96.9%. Eight of ninety-six cases are positive, so a head that never
|
||||||
|
asks scores 91.7%. Recall on those eight is the number.
|
||||||
|
|
||||||
|
Compare it to what ships. The cascade today misses 1 clarify and produces 2
|
||||||
|
false ones. The head catches 7 of 8 and produces 2.3 false ones. That is
|
||||||
|
parity, from a 118M encoder with no rules in front of it.
|
||||||
|
|
||||||
|
The saved checkpoint is seed 2 at epoch 10. Intent 94.3%, destination 28/33,
|
||||||
|
slot F1 73.6%, clarify 7 of 8 with 3 false. `heads.pt` carries four state dicts.
|
||||||
|
|
||||||
|
## What it gets wrong is consistent across seeds
|
||||||
|
|
||||||
|
`поужинал` is a false clarify on all three seeds. That utterance is already
|
||||||
|
recorded as a real defect. `thinSingleToken` was narrowed on 2026-08-01 to spare
|
||||||
|
a token carrying a Russian verb ending. One word is routinely a whole sentence
|
||||||
|
in Russian. The head relearned the mistake the rule was narrowed to
|
||||||
|
fix.
|
||||||
|
|
||||||
|
`что дальше?` is a false clarify on two seeds. That one is a disagreement rather
|
||||||
|
than an error. The utterance is underspecified, and V-498 decided stage 0 claims
|
||||||
|
it for the calendar on purpose.
|
||||||
|
|
||||||
|
`ну это` is missed on two seeds. `amb-003` is the shortest case in the fixture
|
||||||
|
and the generated demonstratives are longer.
|
||||||
|
|
||||||
|
## Confidence
|
||||||
|
|
||||||
|
`Confidence: 1.0` was a hardcode in `llmrouter.go`, so a correct low confidence
|
||||||
|
could not exist. Max softmax over the intent head is the replacement. It is only worth reading
|
||||||
|
if it is lower where the head is wrong.
|
||||||
|
|
||||||
|
It is. Mean 0.851 where the head is right against 0.604 where it is wrong. It
|
||||||
|
ranks a right case above a wrong one in 83.4% of pairs.
|
||||||
|
|
||||||
|
So there are two signals now and they are not the same signal. Confidence says
|
||||||
|
the head is unsure which intent this is. The clarify head says the utterance
|
||||||
|
does not carry enough to act on. A confident wrong route and an honest "I cannot
|
||||||
|
tell" are different failures, and one number cannot report both.
|
||||||
|
|
||||||
|
## What this does not measure
|
||||||
|
|
||||||
|
The same gap as every head run. **Nothing of this runs in Go.** Four heads
|
||||||
|
instead of three does not change that.
|
||||||
|
|
||||||
|
There is no threshold. Both signals are reported as raw numbers. Turning either
|
||||||
|
into a gate needs a decision about where to cut, and that trades false clarifies
|
||||||
|
against wrong acts. The fixture has 8 positives, which is too few to fit a
|
||||||
|
threshold on.
|
||||||
|
|
||||||
|
The 299 generated rows have no held-out slice of their own. Clarify is scored on
|
||||||
|
the fixture alone.
|
||||||
@@ -46,9 +46,9 @@ possible. A clarify names nothing, so it would satisfy an empty label for free.
|
|||||||
`Score` requires the route to land the case's intent before it credits a
|
`Score` requires the route to land the case's intent before it credits a
|
||||||
destination hit, or the floor label would score itself.
|
destination hit, or the floor label would score itself.
|
||||||
|
|
||||||
## Seven cases assert the floor, and six of them cluster
|
## Seven cases assert the floor, and five of them cluster
|
||||||
|
|
||||||
The six are homelab operations. `SourceRecall`, `SourceNetwork` and
|
The five are homelab operations. `SourceRecall`, `SourceNetwork` and
|
||||||
`SourceAttention` overlap on every question about the box, because `mavpoll`
|
`SourceAttention` overlap on every question about the box, because `mavpoll`
|
||||||
writes its netdata and uptime-kuma observations into the fact store recall
|
writes its netdata and uptime-kuma observations into the fact store recall
|
||||||
reads. "почему сервер тормозит" is answerable from all three. Naming one takes
|
reads. "почему сервер тормозит" is answerable from all three. Naming one takes
|
||||||
@@ -57,6 +57,8 @@ the other two off the turn.
|
|||||||
That is a finding about the enum rather than a gap in the labelling. The floor
|
That is a finding about the enum rather than a gap in the labelling. The floor
|
||||||
is the right answer there and the fixture now says so out loud.
|
is the right answer there and the fixture now says so out loud.
|
||||||
|
|
||||||
|
All seven were written by an agent and confirmed by the owner on 08-08-2026.
|
||||||
|
|
||||||
## A drift the labelling found
|
## A drift the labelling found
|
||||||
|
|
||||||
`WorldQueryGrammars` went into `buildRouter` with V-655 and never into
|
`WorldQueryGrammars` went into `buildRouter` with V-655 and never into
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# Two heads on e5-small, and the first destination the router did not need a model for
|
||||||
|
|
||||||
|
Measured 2026-08-08 on workpc (Radeon RX 7900 GRE, ROCm). Covers V-661, step 3 of
|
||||||
|
`docs/plans/18-routing-heads-on-e5-small.md`. Workspace is `~/Programs/embed-training`,
|
||||||
|
scripts `gen_query_source.py`, `label_source.py`, `build_heads_corpus.py`,
|
||||||
|
`train_heads.py`, `score_confidence.py`.
|
||||||
|
|
||||||
|
## Two heads, not four
|
||||||
|
|
||||||
|
Intent is 7 classes and destination is 12 plus the `SourceUnknown` floor, sharing
|
||||||
|
one masked mean pool over one forward pass. The plan asked for four. Two of them
|
||||||
|
have no labels and neither is a GPU problem.
|
||||||
|
|
||||||
|
**Mood is cut, not deferred.** Maven's enum is `neutral, happy, thinking, tired,
|
||||||
|
confused` and it describes her own reply state, not the speaker's emotion.
|
||||||
|
`psytechlab/EmpatheticIntents-ru` was the only candidate and its 32 emotion
|
||||||
|
labels do not map onto it. There is nothing to train against.
|
||||||
|
|
||||||
|
**BIO slot tags stay in the MASSIVE body.** No Maven-domain span corpus exists.
|
||||||
|
`2026-08-08-massive-warm-start.md` records that no second Russian slot-filling
|
||||||
|
corpus is reachable at all.
|
||||||
|
|
||||||
|
The destination loss is masked with `ignore_index`. Only a query turn reaches
|
||||||
|
`queryWalk`, so a reminder contributes nothing to it.
|
||||||
|
|
||||||
|
## Where the destination labels came from
|
||||||
|
|
||||||
|
V-660 taught the router prompt to name a destination. That made gemma-4-12b a
|
||||||
|
teacher, and this distils it.
|
||||||
|
|
||||||
|
Labelling the 300 query rows already in `train_v5.jsonl` gave 277 destinations.
|
||||||
|
The shape was unusable: the floor 101, calendar 62, recall 47, and `feeds` and
|
||||||
|
`attention` at zero. A 13-way softmax cannot learn a class with no examples.
|
||||||
|
|
||||||
|
`gen_query_source.py` is the destination half of `gen_corpus.py` and runs the
|
||||||
|
same two passes. Gemma writes questions whose answer lives in one named place.
|
||||||
|
The daemon's own `routeSystem` prompt then routes each one back. A line survives
|
||||||
|
only when the intent is `query` **and** the source is the destination it was
|
||||||
|
generated for. The glosses are copied verbatim out of `route_system.txt`, so the
|
||||||
|
generator and the labeller work from one definition.
|
||||||
|
|
||||||
|
The prompt and the GBNF are dumped from `internal/router/llmrouter.go` by
|
||||||
|
`TestDumpPrompt`, never retyped. The workspace held its own copies and V-660
|
||||||
|
changed both.
|
||||||
|
|
||||||
|
1229 kept of 2373 generated, 51.8%. Merged corpus is 3664 rows carrying 1727
|
||||||
|
destinations:
|
||||||
|
|
||||||
|
| | rows | | rows |
|
||||||
|
|---|---|---|---|
|
||||||
|
| the floor | 220 | world | 132 |
|
||||||
|
| calendar | 180 | money | 126 |
|
||||||
|
| tasks | 169 | list | 124 |
|
||||||
|
| recall | 167 | self, feeds, attention | 120 each |
|
||||||
|
| weather | 136 | network | 103 |
|
||||||
|
| | | home | 50 |
|
||||||
|
|
||||||
|
`home` is thin because the agreement filter rejected most of what was generated
|
||||||
|
for it. A question about the house routes `act` more often than `query`. That is
|
||||||
|
the filter working, and 50 is the finding rather than a shortfall.
|
||||||
|
|
||||||
|
**The floor was regenerated once.** The first 120 rows carried one sentence
|
||||||
|
shape across eight topics. That shape was "что там с X" and its two synonyms.
|
||||||
|
Every named destination varied and only the floor collapsed. The reason is that
|
||||||
|
the generator varies a topic, and ambiguity is not a topic.
|
||||||
|
|
||||||
|
`gen_query_source.py` now rotates six floor shapes. A `почему` question, a yes
|
||||||
|
or no question, and a question carried by intonation alone. Then a
|
||||||
|
better-or-worse question, a status question, and an existence question. That is
|
||||||
|
a fix to degenerate generation. It is not fitting to the fixture, whose floor
|
||||||
|
cases are homelab operations and match none of the six.
|
||||||
|
|
||||||
|
The 1229 generated rows carry `intent: null`. Every one is a query by
|
||||||
|
construction. There are five times as many as the corpus has query rows, so
|
||||||
|
including them would make query half the intent corpus.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
Three seeds, two bodies, epoch chosen on the intent dev slice and never on a
|
||||||
|
destination number.
|
||||||
|
|
||||||
|
| body | floor corpus | intent mean | destination mean |
|
||||||
|
|---|---|---|---|
|
||||||
|
| warm-started `out/body_massive` | one shape | 93.6% | 75.8% |
|
||||||
|
| stock `multilingual-e5-small` | one shape | 93.6% | 76.8% |
|
||||||
|
| warm-started `out/body_massive` | six shapes | 93.6% | **80.8%** |
|
||||||
|
|
||||||
|
Best single run is destination **29/33 (87.9%)**, seed 0 on the rotated floor.
|
||||||
|
Peak 1.68GB of 17.2GB, under four minutes end to end.
|
||||||
|
|
||||||
|
Against the two arms already measured on the same 33 labelled cases:
|
||||||
|
|
||||||
|
| | destination |
|
||||||
|
|---|---|
|
||||||
|
| classifier cascade (V-659) | 12/33 (36.4%) |
|
||||||
|
| cascade + gemma-4-12b (V-660) | 24/33 (72.7%) |
|
||||||
|
| two heads on e5-small | 29/33 (87.9%) |
|
||||||
|
|
||||||
|
A 118M encoder beats the 12B teacher it was distilled from, on the fixture. The
|
||||||
|
per-destination split is where it happens: **recall 15/15** and **world 5/5**.
|
||||||
|
Recall was 0/15 on the cascade and 14/15 through gemma.
|
||||||
|
|
||||||
|
Read 87.9% as one seed of a mean of 80.8%, not as a headline. Three seeds score
|
||||||
|
29, 25 and 26 of 33. One case is 3 points on a fixture this small.
|
||||||
|
|
||||||
|
Intent is **not** comparable to the 73/96 and 81/96 figures those two arms
|
||||||
|
scored. A softmax has no clarify class. The head's fixture is the 88 cases that
|
||||||
|
carry an intent, and the 8 `want_clarify` cases are scored separately below.
|
||||||
|
|
||||||
|
## The MASSIVE warm-start is worth nothing here either
|
||||||
|
|
||||||
|
Step 2 measured it at +0.4 points of intent accuracy and called that inside seed
|
||||||
|
noise. Destination was the open question, because MASSIVE has a
|
||||||
|
`definition_word` slot that looked like a `SourceWorld` signal sitting in a head
|
||||||
|
already trained.
|
||||||
|
|
||||||
|
It is not. The two bodies score the same intent mean to one decimal. Stock is
|
||||||
|
one point ahead on destination, which is a third of one case. Nothing here argues
|
||||||
|
for keeping the warm-start step. Dropping it removes a dependency on a corpus
|
||||||
|
pull that `datasets` 5.0 cannot do.
|
||||||
|
|
||||||
|
## The floor moved, calendar did not
|
||||||
|
|
||||||
|
Before the rotation, all seven misses at seed 0 were the floor and calendar. The
|
||||||
|
head named a destination where the fixture says walk the chain, and it was
|
||||||
|
confident doing it. `"почему сервер тормозит"` read `world` at 0.80.
|
||||||
|
`"хватает ли места под новые бэкапы"` read `network` at 0.82. Those are the five
|
||||||
|
homelab cases V-659 flagged, where `SourceRecall`, `SourceNetwork` and
|
||||||
|
`SourceAttention` all overlap. `mavpoll` writes its observations into the fact
|
||||||
|
store recall reads.
|
||||||
|
|
||||||
|
| | one shape | six shapes |
|
||||||
|
|---|---|---|
|
||||||
|
| the floor | 3/7 | 6/7, 6/7, 5/7 |
|
||||||
|
| calendar | 3/6 | 3/6, 3/6, 3/6 |
|
||||||
|
| recall | 15/15 | 15/15 at seed 0 |
|
||||||
|
| world | 5/5 | 5/5 |
|
||||||
|
|
||||||
|
The floor was a corpus defect and it cost 3 cases. Sentence variety carried it,
|
||||||
|
not homelab vocabulary, which the training rows still do not contain.
|
||||||
|
|
||||||
|
**Calendar is 3/6 at every seed and is a different problem.** It is the shape
|
||||||
|
V-660 named. The possessive agenda rules claim those cases at stage 0 and
|
||||||
|
deliberately name nothing, so no destination label reaches the head. Training
|
||||||
|
cannot move a case the head never sees. That one wants the owner's call.
|
||||||
|
|
||||||
|
## Max softmax separates, weakly, and the gate stays
|
||||||
|
|
||||||
|
The plan argues max softmax is a calibratable confidence where `Confidence: 1.0`
|
||||||
|
was a hardcode. Measured on the intent head:
|
||||||
|
|
||||||
|
| | n | mean confidence |
|
||||||
|
|---|---|---|
|
||||||
|
| correct | 80 | 0.897 |
|
||||||
|
| wrong | 8 | 0.705 |
|
||||||
|
| `want_clarify` | 8 | 0.685 |
|
||||||
|
|
||||||
|
The softest correct answer is 0.66 and 3 of 8 clarify cases sit below it. So a
|
||||||
|
single cut buys three clarifies at no false-clarify cost, and no more.
|
||||||
|
|
||||||
|
The other five explain themselves. `"напомни"` scores 0.94 as `reminder` and
|
||||||
|
`"сделай это"` scores 0.80 as `act`. Both are intent-certain and slot-empty, and
|
||||||
|
confidence was never the signal there. `gateLLMDecision` already catches exactly
|
||||||
|
that shape, an act with no allowlisted fn or a keyless fact, and it keeps doing
|
||||||
|
so. The head replaces the hardcode. It does not replace the gate.
|
||||||
|
|
||||||
|
## An incident worth recording
|
||||||
|
|
||||||
|
The first generation run produced zero rows for eight destinations. `mavgpud`
|
||||||
|
yields the card when another process wants it (V-488) and llama-server answers
|
||||||
|
503 until the model is back. Every generate call inside that window burned one of
|
||||||
|
the destination's batches. The run walked its own cap without a single successful
|
||||||
|
call. The log said `503` 260 times, and the summary line said 64.1% keep rate,
|
||||||
|
which read as success.
|
||||||
|
|
||||||
|
`call()` now retries a 503 with backoff. A generator that treats an unloaded
|
||||||
|
model as a bad generation is a silent-corpus bug, not a slow one.
|
||||||
|
|
||||||
|
## What this does not measure
|
||||||
|
|
||||||
|
**Nothing here runs in Go.** The heads are a `heads.pt` and an
|
||||||
|
`out/body_heads/` on workpc. Reaching the daemon needs an ONNX export and a
|
||||||
|
caller. The resident e5-small must not be replaced by this copy: recall depends
|
||||||
|
on that file, and `EmbedQuery`/`EmbedPassage` are its contract.
|
||||||
|
|
||||||
|
The destination fixture carries 4 of 13 classes: recall 15, floor 7, calendar 6,
|
||||||
|
world 5. `tasks`, `money`, `list`, `home`, `network`, `feeds`, `attention` and
|
||||||
|
`self` have no gold case. So 78.8% is silent on eight destinations that together
|
||||||
|
hold 800 training rows.
|
||||||
|
|
||||||
|
Latency was not measured. A forward pass of a 118M encoder should beat a 1.7B
|
||||||
|
decoder on a query turn. That is arithmetic, not a number from this box.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# A slot head, and the corpus that did not exist this morning
|
||||||
|
|
||||||
|
Measured 2026-08-08 on workpc, the same day as `2026-08-08-routing-heads-two-head.md`
|
||||||
|
and against the same fixtures. Workspace is `~/Programs/embed-training`, new
|
||||||
|
scripts `slot_grammar.gbnf`, `slot_system.txt`, `label_slots.py`, `merge_slots.py`.
|
||||||
|
|
||||||
|
## The corpus was the whole problem
|
||||||
|
|
||||||
|
The two-head measurement said BIO slot tags stay in the MASSIVE body, because no
|
||||||
|
Maven-domain span corpus exists. That was true of found corpora and false of
|
||||||
|
made ones. Destination had the same shape at breakfast. V-660 gave gemma-4-12b a
|
||||||
|
string to write and the label problem became a generation problem.
|
||||||
|
|
||||||
|
The same trick applies to spans. `slot_grammar.gbnf` emits a list of
|
||||||
|
`{"slot": ..., "text": ...}` and the enum closes over Maven's own five: `time`,
|
||||||
|
`text`, `key`, `value`, `fn`. `slot_system.txt` demands each span be an exact
|
||||||
|
substring of the utterance.
|
||||||
|
|
||||||
|
**The agreement filter is free here.** Destination needed a second pass. The
|
||||||
|
daemon's own router prompt had to route each generated line back. A span needs
|
||||||
|
no second call. It either occurs in the utterance or it does not, and
|
||||||
|
`label_slots.py` drops it with `find()`.
|
||||||
|
|
||||||
|
1702 rows labelled from `train_v5.jsonl`, the reminder, fact, note, act and
|
||||||
|
query intents. Chat and system carry no slot and were never asked.
|
||||||
|
|
||||||
|
| slot | spans |
|
||||||
|
|---|---|
|
||||||
|
| text | 1175 |
|
||||||
|
| time | 485 |
|
||||||
|
| fn | 381 |
|
||||||
|
| key | 72 |
|
||||||
|
| value | 65 |
|
||||||
|
|
||||||
|
37 spans dropped as not-a-substring, 2.2% of the pile. Nothing failed to parse,
|
||||||
|
which is the grammar doing its job. 409 rows came back with no span at all.
|
||||||
|
Those are kept and tagged all `O`. An utterance carrying no slot teaches the
|
||||||
|
head not to invent one. An empty list is a label and not a miss.
|
||||||
|
|
||||||
|
`key` and `value` are thin because they come from facts alone. That is the
|
||||||
|
shape of the corpus, not a labeller failure.
|
||||||
|
|
||||||
|
## Three heads on one forward pass
|
||||||
|
|
||||||
|
Intent and destination were already two linear heads over one masked mean pool.
|
||||||
|
Slots is a third head over the per-token states of the same pass, so the marginal
|
||||||
|
cost is one `Linear(384, 11)`.
|
||||||
|
|
||||||
|
The tag set is `O` plus `B-` and `I-` for each of the five. A softmax cannot
|
||||||
|
emit a tag that does not exist. That is the structural guarantee the GBNF buys
|
||||||
|
for the teacher, and the head gets it for free.
|
||||||
|
|
||||||
|
Two masking rules, both `ignore_index`. A row with no `spans` key contributes
|
||||||
|
nothing, which covers the 1900 generated destination rows and every chat and
|
||||||
|
system turn. A padding or special-token position contributes nothing either.
|
||||||
|
|
||||||
|
Scoring is exact-match span F1, not token accuracy. Most tokens are `O`, so a
|
||||||
|
tagger that predicts nothing anywhere scores above 90% on tokens.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
Three seeds, 24 epochs, epoch chosen on the intent dev slice alone.
|
||||||
|
|
||||||
|
| | two heads | three heads |
|
||||||
|
|---|---|---|
|
||||||
|
| intent mean | 93.6% | 92.8% |
|
||||||
|
| destination mean | 80.8% | 82.8% |
|
||||||
|
| destination best | 29/33 (87.9%) | 29/33 (87.9%) |
|
||||||
|
| slot span F1 mean | — | 72.4% |
|
||||||
|
|
||||||
|
**The slot head costs nothing and adds a third decision.** Intent moves 0.8
|
||||||
|
points down and destination 2 points up. Both sit inside the seed spread those
|
||||||
|
two numbers already had. Read this as unchanged, not as a trade.
|
||||||
|
|
||||||
|
The saved checkpoint is seed 1 at epoch 10: intent 93.2%, destination 81.8%,
|
||||||
|
slot F1 75.8%. `heads.pt` now carries three state dicts and the `bio` list
|
||||||
|
beside the intent and source enums.
|
||||||
|
|
||||||
|
## Epoch selection is now wrong for one of the three heads
|
||||||
|
|
||||||
|
Slot F1 was still climbing when the intent-selected epoch stopped it. Seed 0
|
||||||
|
selects epoch 13 at 70.9% and reaches 76.1% at epoch 20. Seed 1 selects epoch 10
|
||||||
|
at 75.8% and reaches 80.0% at epoch 24.
|
||||||
|
|
||||||
|
So the three tasks want different epochs and the harness picks one. Two ways
|
||||||
|
out, and neither was taken here. Select on a joint score, which needs an
|
||||||
|
argument about weights. Or give the slot head its own dev slice and its own
|
||||||
|
early stop, which means the heads stop being one checkpoint.
|
||||||
|
|
||||||
|
Leaving it costs about 4 points of slot F1 and nothing else. The router
|
||||||
|
contract is graded on intent. Selecting on it is the conservative choice rather
|
||||||
|
than an oversight.
|
||||||
|
|
||||||
|
## What this does not measure
|
||||||
|
|
||||||
|
The same gap as the two-head run. **Nothing of this runs in Go.** Three heads
|
||||||
|
instead of two does not change that. It does change the export. The slot head
|
||||||
|
reads the per-token states, so an ONNX graph exposing only the pooled vector is
|
||||||
|
not enough.
|
||||||
|
|
||||||
|
There is no held-out span fixture. Span F1 is measured on the dev slice of the
|
||||||
|
same distilled corpus, so it scores agreement with gemma rather than
|
||||||
|
correctness. The V-572 defect utterance, `"напомни в 11:00 позвонить маме"`, is
|
||||||
|
labelled correctly by the teacher, but one utterance is not a fixture.
|
||||||
|
|
||||||
|
`key` at 72 spans and `value` at 65 are too thin to report a per-slot number.
|
||||||
@@ -0,0 +1,636 @@
|
|||||||
|
# Raw transcript, two weeks of usage
|
||||||
|
|
||||||
|
Companion to `2026-08-08-two-weeks.md`. 140 turns through `POST /api/chat`,
|
||||||
|
driven by `scripts/usage-run.py` over `scripts/testdata/usage-turns.txt`.
|
||||||
|
Days are simulated. The wall clock was 20:18-20:27 throughout, which is why
|
||||||
|
every clock answer says evening.
|
||||||
|
|
||||||
|
## Day 1
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: Сейчас уже вечер, поэтому я не могу сказать тебе «доброе утро».
|
||||||
|
[1.5s t=78]
|
||||||
|
|
||||||
|
YOU: какой сегодня день?
|
||||||
|
MAVEN: сегодня суббота, 8 августа 2026 года
|
||||||
|
[0.0s t=79]
|
||||||
|
|
||||||
|
YOU: сколько времени?
|
||||||
|
MAVEN: сейчас 20 часов 18 минут
|
||||||
|
[0.0s t=80]
|
||||||
|
|
||||||
|
YOU: запиши что я пью кофе без сахара
|
||||||
|
MAVEN: Я записала твою заметку про кофе。
|
||||||
|
[2.0s t=81]
|
||||||
|
|
||||||
|
YOU: мой любимый язык программирования go
|
||||||
|
MAVEN: Я записала твою заметку про Go.
|
||||||
|
[1.4s t=82]
|
||||||
|
|
||||||
|
YOU: напомни в 11:00 позвонить маме
|
||||||
|
MAVEN: Сейчас 20:18. В какой день?
|
||||||
|
[0.4s t=83]
|
||||||
|
|
||||||
|
YOU: что у меня сегодня?
|
||||||
|
MAVEN: на 08.08.2026 ничего нет, на какое время поставить напоминание?
|
||||||
|
[0.3s t=84]
|
||||||
|
|
||||||
|
YOU: что такое TCP?
|
||||||
|
MAVEN: для какого города? На какое время поставить напоминание?
|
||||||
|
[1.0s t=85]
|
||||||
|
|
||||||
|
YOU: сколько будет 17 на 23?
|
||||||
|
MAVEN: для какого города? На какое время поставить напоминание?
|
||||||
|
[0.9s t=86]
|
||||||
|
|
||||||
|
YOU: спасибо
|
||||||
|
MAVEN: Сейчас 20:18. В какой день?
|
||||||
|
[0.9s t=87]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 2
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: Сейчас 20:18. В какой день?
|
||||||
|
[0.9s t=88]
|
||||||
|
|
||||||
|
YOU: что нового?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи, на какое время поставить напоминание?
|
||||||
|
[1.2s t=89]
|
||||||
|
|
||||||
|
YOU: какая погода?
|
||||||
|
MAVEN: для какого города? На какое время поставить напоминание?
|
||||||
|
[0.6s t=90]
|
||||||
|
|
||||||
|
YOU: запиши что пароль от вайфая лежит в ящике стола
|
||||||
|
MAVEN: Прошлую просьбу отпускаю. Хорошо, сохранил.
|
||||||
|
[33.7s t=91]
|
||||||
|
|
||||||
|
YOU: где лежит вайфай пароль?
|
||||||
|
MAVEN: вот что я нашла: пароль от вайфая лежит в ящике стола
|
||||||
|
[7.0s t=92]
|
||||||
|
|
||||||
|
YOU: добавь молоко в список покупок
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[1.4s t=93]
|
||||||
|
|
||||||
|
YOU: что у меня в списке покупок?
|
||||||
|
MAVEN: в списке покупок пусто.
|
||||||
|
[0.0s t=94]
|
||||||
|
|
||||||
|
YOU: кто такой Линус Торвальдс?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи.
|
||||||
|
[1.6s t=95]
|
||||||
|
|
||||||
|
YOU: какой у меня любимый язык?
|
||||||
|
MAVEN: вот что нового: [$] Changes in shadow-utils password-expiration features; The Software Stewardship Lab launches; LightDM lives: version 1.33.0 released
|
||||||
|
[1.3s t=96]
|
||||||
|
|
||||||
|
YOU: сколько у меня задач?
|
||||||
|
MAVEN: сначала: проверить интейк форму на боксе (завтра); купить хлеб; купить молоко; полить цветы; починить кран
|
||||||
|
[0.0s t=97]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 3
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: как дела?
|
||||||
|
MAVEN: рада, как дела?
|
||||||
|
[4.1s t=98]
|
||||||
|
|
||||||
|
YOU: напомни завтра в 9 утра купить хлеб
|
||||||
|
MAVEN: хорошо, напомню завтра в 09:00.
|
||||||
|
[0.3s t=99]
|
||||||
|
|
||||||
|
YOU: что у меня завтра?
|
||||||
|
MAVEN: на 09.08.2026 ничего нет.
|
||||||
|
[0.0s t=100]
|
||||||
|
|
||||||
|
YOU: отмени напоминание про хлеб
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[1.6s t=101]
|
||||||
|
|
||||||
|
YOU: какие у меня напоминания?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи.
|
||||||
|
[0.0s t=102]
|
||||||
|
|
||||||
|
YOU: сохрани мне адрес гостиницы в Сочи
|
||||||
|
MAVEN: Хорошо, сохраню.
|
||||||
|
[5.6s t=103]
|
||||||
|
|
||||||
|
YOU: что я сохранил про Сочи?
|
||||||
|
MAVEN: Хорошо, сохраню.
|
||||||
|
[2.7s t=104]
|
||||||
|
|
||||||
|
YOU: почему сервер тормозит?
|
||||||
|
MAVEN: есть такое: Почему тормозит сервер сайта: причины медленной работы и способы решения — Почему сервер работает медленно: основные причины торможения, диагностика проблем, способы оптимизации. Практические советы по ускорению работы сервера и сайта.
|
||||||
|
[3.1s t=105]
|
||||||
|
|
||||||
|
YOU: хватает ли места под новые бэкапы?
|
||||||
|
MAVEN: нового вот что: [$] Changes in shadow-utils password-expiration features; The Software Stewardship Lab launches; LightDM lives: version 1.33.0 released
|
||||||
|
[1.5s t=106]
|
||||||
|
|
||||||
|
YOU: выключи свет в спальне
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[9.0s t=107]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 4
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: рада
|
||||||
|
[5.0s t=108]
|
||||||
|
|
||||||
|
YOU: что я пропустил?
|
||||||
|
MAVEN: я не могу ответить на вопрос о том, что ты пропустил. мне нужно больше информации.
|
||||||
|
[5.7s t=109]
|
||||||
|
|
||||||
|
YOU: о чём мы вчера говорили?
|
||||||
|
MAVEN: пока не умею отвечать на этот вопрос.
|
||||||
|
[1.1s t=110]
|
||||||
|
|
||||||
|
YOU: запиши что я записался к врачу на четверг
|
||||||
|
MAVEN: Хорошо, сохранил.
|
||||||
|
[6.0s t=111]
|
||||||
|
|
||||||
|
YOU: когда я иду к врачу?
|
||||||
|
MAVEN: пока не умею отвечать на этот вопрос.
|
||||||
|
[1.1s t=112]
|
||||||
|
|
||||||
|
YOU: что такое ZFS?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи.
|
||||||
|
[1.0s t=113]
|
||||||
|
|
||||||
|
YOU: столица Франции?
|
||||||
|
MAVEN: пока не умею отвечать на этот вопрос.
|
||||||
|
[1.2s t=114]
|
||||||
|
|
||||||
|
YOU: переведи слово ремонт на английский
|
||||||
|
MAVEN: записала: переведи слово ремонт на английский
|
||||||
|
[1.1s t=115]
|
||||||
|
|
||||||
|
YOU: сколько я потратил в этом месяце?
|
||||||
|
MAVEN: я не отслеживаю траты — не подключено.
|
||||||
|
[1.4s t=116]
|
||||||
|
|
||||||
|
YOU: спокойной ночи
|
||||||
|
MAVEN: спокойной ночи
|
||||||
|
[4.3s t=117]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 5
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: Привет!
|
||||||
|
[2.6s t=118]
|
||||||
|
|
||||||
|
YOU: какая погода в Москве?
|
||||||
|
MAVEN: погода не настроена.
|
||||||
|
[1.2s t=119]
|
||||||
|
|
||||||
|
YOU: что там с бэкапами?
|
||||||
|
MAVEN: нашла вот это: Бэкап - что такое резервное копирование и как его сделать — Бэкап (backup) — это резервная копия важных данных, сохраненная в специальный файл. Ею пользуются как подстраховкой: если что-то сломается, с помощью бэкапа можно будет все вернуть. Простейший пример бэкапа — сохранение файла в облаке или о…
|
||||||
|
[4.0s t=120]
|
||||||
|
|
||||||
|
YOU: покажи что требует внимания
|
||||||
|
MAVEN: я заметила, что у тебя есть запрос о том, что требуется внимание. давай разберёмся вместе.
|
||||||
|
[5.2s t=121]
|
||||||
|
|
||||||
|
YOU: отметь это как сделанное
|
||||||
|
MAVEN: команды «отметь» нет в списке. Предложила её добавить — включи через клиент.
|
||||||
|
[0.0s t=122]
|
||||||
|
|
||||||
|
YOU: запиши что я купил новые наушники
|
||||||
|
MAVEN: Заметка сохранена.
|
||||||
|
[3.7s t=123]
|
||||||
|
|
||||||
|
YOU: какие у меня заметки за неделю?
|
||||||
|
MAVEN: week summary — подведи итоги недели
|
||||||
|
[0.0s t=124]
|
||||||
|
|
||||||
|
YOU: расскажи про Kubernetes
|
||||||
|
MAVEN: вот что я нашла: Что такое Kubernetes — Сервисы, поддержка и инструменты Kubernetes широко доступны. Название Kubernetes происходит от греческого, что означает рулевой или штурман. Google открыл исходный код Kubernetes в 2014 году.
|
||||||
|
[1.0s t=125]
|
||||||
|
|
||||||
|
YOU: кто я?
|
||||||
|
MAVEN: name — Ками
|
||||||
|
[1.0s t=126]
|
||||||
|
|
||||||
|
YOU: пока
|
||||||
|
MAVEN: Пока, спасибо за разговор.
|
||||||
|
[4.1s t=127]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 6
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: Добро пожаловать! Я Maven — твой домашний ассистент. Как я могу помочь сегодня?
|
||||||
|
[10.1s t=128]
|
||||||
|
|
||||||
|
YOU: сколько времени?
|
||||||
|
MAVEN: сейчас 20 часов 21 минута
|
||||||
|
[0.0s t=129]
|
||||||
|
|
||||||
|
YOU: напомни в 18:30 позвонить в банк
|
||||||
|
MAVEN: Сейчас 20:21. В какой день?
|
||||||
|
[0.3s t=130]
|
||||||
|
|
||||||
|
YOU: поставь чайник
|
||||||
|
MAVEN: Сейчас 20:21. В какой день?
|
||||||
|
[5.3s t=131]
|
||||||
|
|
||||||
|
YOU: включи музыку
|
||||||
|
MAVEN: Сейчас 20:21. В какой день?
|
||||||
|
[1.7s t=132]
|
||||||
|
|
||||||
|
YOU: что у меня в календаре на пятницу?
|
||||||
|
MAVEN: вот что я нашла: ты записался к врачу на четверг, лежит вайфай пароль в ящике стола, сохранил про Сочи, пью кофе без сахара, на какое время поставить напоминание?
|
||||||
|
[5.5s t=133]
|
||||||
|
|
||||||
|
YOU: во сколько у меня встреча?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи, на какое время поставить напоминание?
|
||||||
|
[0.3s t=134]
|
||||||
|
|
||||||
|
YOU: запиши что дедлайн по проекту в понедельник
|
||||||
|
MAVEN: Прошлую просьбу отпускаю. Хорошо, записала.
|
||||||
|
[6.3s t=135]
|
||||||
|
|
||||||
|
YOU: успею ли я до дедлайна?
|
||||||
|
MAVEN: вот что я нашла: дедлайн по проекту указан как понедельник.
|
||||||
|
[5.3s t=136]
|
||||||
|
|
||||||
|
YOU: спасибо
|
||||||
|
MAVEN: спасиба
|
||||||
|
[3.8s t=137]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 7
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: Привет!
|
||||||
|
[2.6s t=138]
|
||||||
|
|
||||||
|
YOU: как ты?
|
||||||
|
MAVEN: рада
|
||||||
|
[4.2s t=139]
|
||||||
|
|
||||||
|
YOU: расскажи анекдот
|
||||||
|
MAVEN: Я могу рассказать интересный анекдот для тебя. Вот - в одном из фильмов был такой сценарий: главному герою дали уйти от команды и он остался один, а потом появился его друг, который сказал ему: 'Тебе не нужно было остаться'. Но это просто игра слов.
|
||||||
|
[7.1s t=140]
|
||||||
|
|
||||||
|
YOU: что ты умеешь?
|
||||||
|
MAVEN: Я могу помочь с задачами, напоминать о важных датах и событиях. Также я помогу записывать заметки или отвечать по ним.
|
||||||
|
[4.1s t=141]
|
||||||
|
|
||||||
|
YOU: запиши что я начал бегать по утрам
|
||||||
|
MAVEN: Заметка сохранена.
|
||||||
|
[9.2s t=142]
|
||||||
|
|
||||||
|
YOU: я бегаю по утрам уже неделю
|
||||||
|
MAVEN: записала: я бегаю по утрам уже неделю
|
||||||
|
[1.9s t=143]
|
||||||
|
|
||||||
|
YOU: как часто я бегаю?
|
||||||
|
MAVEN: вот что я нашла: ты бегал по утрам
|
||||||
|
[5.3s t=144]
|
||||||
|
|
||||||
|
YOU: сколько стоит биткоин?
|
||||||
|
MAVEN: есть такое: Цена Bitcoin сегодня | Данные о цене, рынке и графики BTC в ... — В настоящее время 1 Bitcoin стоит 65 тыс $, что на 0,09% выше за последние 24 ч. Отслеживайте цену BTC, рыночную капитализацию, исторический график и рыночную аналитику в реальном времени.
|
||||||
|
[2.1s t=145]
|
||||||
|
|
||||||
|
YOU: какие новости?
|
||||||
|
MAVEN: вот что нового: [$] Changes in shadow-utils password-expiration features; The Software Stewardship Lab launches; LightDM lives: version 1.33.0 released
|
||||||
|
[0.8s t=146]
|
||||||
|
|
||||||
|
YOU: хорошего дня
|
||||||
|
MAVEN: Хорошого дня!
|
||||||
|
[4.1s t=147]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 8
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: Добро пожаловать! Как могу помочь сегодня?
|
||||||
|
[2.9s t=148]
|
||||||
|
|
||||||
|
YOU: что у меня сегодня?
|
||||||
|
MAVEN: на 08.08.2026 ничего нет.
|
||||||
|
[0.0s t=149]
|
||||||
|
|
||||||
|
YOU: напомни через час выпить воды
|
||||||
|
MAVEN: хорошо, напомню сегодня в 21:22.
|
||||||
|
[0.3s t=150]
|
||||||
|
|
||||||
|
YOU: я выпил воды
|
||||||
|
MAVEN: записала: я выпил воды
|
||||||
|
[1.2s t=151]
|
||||||
|
|
||||||
|
YOU: запиши что кот ест только сухой корм
|
||||||
|
MAVEN: Хорошо, записала.
|
||||||
|
[5.6s t=152]
|
||||||
|
|
||||||
|
YOU: чем питается кот?
|
||||||
|
MAVEN: вот что я нашла: кот питается только сухим кормом.
|
||||||
|
[4.5s t=153]
|
||||||
|
|
||||||
|
YOU: что такое DNS?
|
||||||
|
MAVEN: нашла вот это: Dns — Википедия — В случае рекурсивного запроса DNS-сервер опрашивает серверы (в порядке убывания уровня зон в имени), пока не найдёт ответ или не обнаружит, что домена не существует (на практике поиск ...
|
||||||
|
[2.0s t=154]
|
||||||
|
|
||||||
|
YOU: проверь статус uptime kuma
|
||||||
|
MAVEN: записала: проверь статус uptime kuma
|
||||||
|
[1.6s t=155]
|
||||||
|
|
||||||
|
YOU: всё ли в порядке с сервером?
|
||||||
|
MAVEN: пока не умею отвечать на этот вопрос.
|
||||||
|
[1.3s t=156]
|
||||||
|
|
||||||
|
YOU: спасибо
|
||||||
|
MAVEN: спасибо за пожелание
|
||||||
|
[4.0s t=157]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 9
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: рада,
|
||||||
|
[2.1s t=158]
|
||||||
|
|
||||||
|
YOU: какой сегодня день недели?
|
||||||
|
MAVEN: сегодня суббота, 8 августа 2026 года
|
||||||
|
[0.0s t=159]
|
||||||
|
|
||||||
|
YOU: добавь хлеб и сыр в список покупок
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[1.3s t=160]
|
||||||
|
|
||||||
|
YOU: что в списке покупок?
|
||||||
|
MAVEN: в списке покупок пусто.
|
||||||
|
[0.0s t=161]
|
||||||
|
|
||||||
|
YOU: удали молоко из списка
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[1.2s t=162]
|
||||||
|
|
||||||
|
YOU: напомни завтра утром вынести мусор
|
||||||
|
MAVEN: Сейчас 20:22. Когда?
|
||||||
|
[0.3s t=163]
|
||||||
|
|
||||||
|
YOU: запиши что я поменял масло в машине
|
||||||
|
MAVEN: Прошлую просьбу отпускаю. Такую команду я не знаю.
|
||||||
|
[1.3s t=164]
|
||||||
|
|
||||||
|
YOU: когда я менял масло?
|
||||||
|
MAVEN: вот что я нашла: по данным из last update 2019 год, заправки Сочи не могли предоставить масло для замены, поэтому это было невозможно.
|
||||||
|
[7.1s t=165]
|
||||||
|
|
||||||
|
YOU: сколько будет 144 делить на 12?
|
||||||
|
MAVEN: есть такое: Разделить в столбик 144 на 12 - Calc-Best.ru — 144 : 12 = 12 Где 144 - делимое, 12 - делитель, 12 - частное ... Разделить 770 на 7 столбиком Разделить 441 на 9 столбиком Разделить 891 на 9 столбиком Разделить 60 на 5 столбиком Разделить 60 на 6 столбиком
|
||||||
|
[9.6s t=166]
|
||||||
|
|
||||||
|
YOU: пока
|
||||||
|
MAVEN: я записала
|
||||||
|
[4.8s t=167]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 10
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: рада
|
||||||
|
[4.1s t=168]
|
||||||
|
|
||||||
|
YOU: что нового за ночь?
|
||||||
|
MAVEN: нашла вот это: Главные новости сегодня в России, а также самые последние новости со ... — Полное бессилие: киевская ПВО вновь пропустила все российские ракеты. Вооруженные силы РФ в ночь на 8 августа нанесли второй за последнее время массированный ...
|
||||||
|
[2.1s t=169]
|
||||||
|
|
||||||
|
YOU: почему интернет медленный?
|
||||||
|
MAVEN: вот что я нашла: Почему интернет стал медленнее: причины и способы решения - АО «Газпром ... — На скорость интернета сильно влияют торрент-клиенты, VPN, потоковое видео, фоновая загрузка системных обновлений. Посмотреть, куда уходит трафик, можно в настройках ПК или смартфона.
|
||||||
|
[2.4s t=170]
|
||||||
|
|
||||||
|
YOU: какая скорость у меня сейчас?
|
||||||
|
MAVEN: для какого города?
|
||||||
|
[1.2s t=171]
|
||||||
|
|
||||||
|
YOU: запиши что новый роутер стоит 8000 рублей
|
||||||
|
MAVEN: Я записала стоимость нового роутера.
|
||||||
|
[2.5s t=172]
|
||||||
|
|
||||||
|
YOU: сколько стоил роутер?
|
||||||
|
MAVEN: вот что я нашла: новый роутер стоит 8000 рублей.
|
||||||
|
[2.0s t=173]
|
||||||
|
|
||||||
|
YOU: что такое NAT?
|
||||||
|
MAVEN: NAT (Network Address Translation) — это технология, которая позволяет устройствам в локальной сети выходить в интернет, используя один общий внешний IP-адрес. Я отвечаю на этот вопрос, используя поиск в сети.
|
||||||
|
[1.8s t=174]
|
||||||
|
|
||||||
|
YOU: напомни в субботу позвонить бабушке
|
||||||
|
MAVEN: Сейчас 20:23. Когда?
|
||||||
|
[0.3s t=175]
|
||||||
|
|
||||||
|
YOU: покажи мои напоминания
|
||||||
|
MAVEN: вот что я нашла: в твоих источниках нет напоминаний, только запись о том, что ты купил новые наушники, на какое время поставить напоминание?
|
||||||
|
[1.7s t=176]
|
||||||
|
|
||||||
|
YOU: спасибо
|
||||||
|
MAVEN: Сейчас 20:23. Во сколько напомнить?
|
||||||
|
[1.0s t=177]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 11
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: Сейчас 20:23. Скажи время — например, «в семь вечера» или «через час».
|
||||||
|
[0.9s t=178]
|
||||||
|
|
||||||
|
YOU: как погода на выходных?
|
||||||
|
MAVEN: для какого города? На какое время поставить напоминание?
|
||||||
|
[0.6s t=179]
|
||||||
|
|
||||||
|
YOU: что у меня на этой неделе?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи, на какое время поставить напоминание?
|
||||||
|
[0.3s t=180]
|
||||||
|
|
||||||
|
YOU: запиши что я хочу прочитать книгу про Go
|
||||||
|
MAVEN: Прошлую просьбу отпускаю. Я записала твою заметку о книге по Go.
|
||||||
|
[1.4s t=181]
|
||||||
|
|
||||||
|
YOU: что я хотел прочитать?
|
||||||
|
MAVEN: вот что я нашла: ты хотел прочитать книгу про Go.
|
||||||
|
[1.1s t=182]
|
||||||
|
|
||||||
|
YOU: объясни что такое горутина
|
||||||
|
MAVEN: вот что я нашла: я узнала, что горутины — это легковесные потоки выполнения или функции в Go, которые позволяют параллельно выполнять код. Они управляются планировщиком Go и работают как независимые нити выполнения.
|
||||||
|
[1.9s t=183]
|
||||||
|
|
||||||
|
YOU: кто написал Войну и мир?
|
||||||
|
MAVEN: вот что я нашла: роман «Война и мир» написал Лев Николаевич Толстой.
|
||||||
|
[2.4s t=184]
|
||||||
|
|
||||||
|
YOU: включи свет на кухне
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[0.5s t=185]
|
||||||
|
|
||||||
|
YOU: закрой шторы в комнате
|
||||||
|
MAVEN: Такую команду я не знаю.
|
||||||
|
[0.3s t=186]
|
||||||
|
|
||||||
|
YOU: спокойной ночи
|
||||||
|
MAVEN: Спокойной ночи. Отдыхай, я на связи, если что-то понадобится.
|
||||||
|
[1.2s t=187]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 12
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: Я не могу сказать тебе «доброе утро», потому что сейчас уже вечер.
|
||||||
|
[1.0s t=188]
|
||||||
|
|
||||||
|
YOU: сколько сейчас времени?
|
||||||
|
MAVEN: сейчас 20 часов 23 минуты
|
||||||
|
[0.0s t=189]
|
||||||
|
|
||||||
|
YOU: я не то имел в виду
|
||||||
|
MAVEN: Я поняла, исправлюсь. Расскажи, что именно ты хотел сказать?
|
||||||
|
[1.4s t=190]
|
||||||
|
|
||||||
|
YOU: о чём мы говорили?
|
||||||
|
MAVEN: Я помню всё, что ты мне говоришь, и сейчас отвечаю на твои вопросы на основе этих данных.
|
||||||
|
[1.4s t=191]
|
||||||
|
|
||||||
|
YOU: напомни
|
||||||
|
MAVEN: Сейчас 20:23. Когда?
|
||||||
|
[0.7s t=192]
|
||||||
|
|
||||||
|
YOU: сделай это
|
||||||
|
MAVEN: пока не умею отвечать на этот вопрос, на какое время поставить напоминание?
|
||||||
|
[0.5s t=193]
|
||||||
|
|
||||||
|
YOU: запиши что я перешёл на новый тариф
|
||||||
|
MAVEN: Прошлую просьбу отпускаю. Я записала твою новую информацию о тарифе.
|
||||||
|
[1.5s t=194]
|
||||||
|
|
||||||
|
YOU: какой у меня тариф?
|
||||||
|
MAVEN: вот что я нашла: ты перешел на новый тариф
|
||||||
|
[24.4s t=195]
|
||||||
|
|
||||||
|
YOU: сколько я плачу за интернет?
|
||||||
|
MAVEN: есть такое: Как узнать, сколько и когда платить за домашний интернет и ТВ — 3 февр. 2026 г. ... Как узнать, сколько и когда платить за домашний интернет и ТВ · Для тарифов только с домашним интернетом · Была ли статья полезной?
|
||||||
|
[2.1s t=196]
|
||||||
|
|
||||||
|
YOU: спасибо
|
||||||
|
MAVEN: Спасибо, что обратилась за помощью.
|
||||||
|
[4.8s t=197]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 13
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: привет
|
||||||
|
MAVEN: Привет!
|
||||||
|
[2.5s t=198]
|
||||||
|
|
||||||
|
YOU: что там с задачами?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи.
|
||||||
|
[1.2s t=199]
|
||||||
|
|
||||||
|
YOU: закрывай
|
||||||
|
MAVEN: какой пункт отметить сделанным?
|
||||||
|
[0.0s t=200]
|
||||||
|
|
||||||
|
YOU: отметь задачу про бэкапы как сделанную
|
||||||
|
MAVEN: Заметка сохранена.
|
||||||
|
[5.8s t=201]
|
||||||
|
|
||||||
|
YOU: что осталось нерешённым?
|
||||||
|
MAVEN: вот что я нашла: Российское регулирование криптовалют: что осталось нерешённым к 1 июля — 2 июл. 2026 г. ... Российское регулирование криптовалют: что осталось нерешённым к 1 июля. Пакет законопроектов о регулировании цифровых активов в России не ...
|
||||||
|
[3.7s t=202]
|
||||||
|
|
||||||
|
YOU: запиши что я договорился о встрече в среду
|
||||||
|
MAVEN: Заметка сохранена.
|
||||||
|
[2.7s t=203]
|
||||||
|
|
||||||
|
YOU: когда у меня встреча?
|
||||||
|
MAVEN: мне пока нечего смотреть — у Praxis нет источников.
|
||||||
|
[0.1s t=204]
|
||||||
|
|
||||||
|
YOU: какая температура на улице?
|
||||||
|
MAVEN: для какого города?
|
||||||
|
[1.7s t=205]
|
||||||
|
|
||||||
|
YOU: что такое RAID 5?
|
||||||
|
MAVEN: не знаю — не нашла у тебя такой записи.
|
||||||
|
[1.2s t=206]
|
||||||
|
|
||||||
|
YOU: пока
|
||||||
|
MAVEN: Я записала, что мы продолжаем разговор.
|
||||||
|
[4.1s t=207]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Day 14
|
||||||
|
|
||||||
|
```
|
||||||
|
YOU: доброе утро
|
||||||
|
MAVEN: рада, доброго утра!
|
||||||
|
[4.5s t=208]
|
||||||
|
|
||||||
|
YOU: подведи итоги недели
|
||||||
|
MAVEN: записала: подведи итоги недели
|
||||||
|
[1.7s t=209]
|
||||||
|
|
||||||
|
YOU: что я делал за последние две недели?
|
||||||
|
MAVEN: вот что я нашла: ты не делал ничего за последние две недели.
|
||||||
|
[6.6s t=210]
|
||||||
|
|
||||||
|
YOU: какие заметки я сохранил?
|
||||||
|
MAVEN: Ты сочти, что сохранила заметку с текстом 'Проверь календарь' и датой 8 августа 2026 г.,
|
||||||
|
[5.4s t=211]
|
||||||
|
|
||||||
|
YOU: о чём я чаще всего спрашиваю?
|
||||||
|
MAVEN: обычно ты пьёшь воду около 01:12, отмечаешь «name» около 23:56 и отмечаешь «week summary» около 23:59 — по записям за последние 7 дней.
|
||||||
|
[4.4s t=212]
|
||||||
|
|
||||||
|
YOU: напомни в понедельник в 10 проверить бэкапы
|
||||||
|
MAVEN: Сейчас 20:25. Это утра или вечера?
|
||||||
|
[0.7s t=213]
|
||||||
|
|
||||||
|
YOU: что у меня в понедельник?
|
||||||
|
MAVEN: вот что я нашла: у тебя в понедельник дедлайн по проекту, на какое время поставить напоминание?
|
||||||
|
[4.6s t=214]
|
||||||
|
|
||||||
|
YOU: ты меня понимаешь?
|
||||||
|
MAVEN: Да, я тебя прекрасно понимаю. Я готова помогать тебе с заметками и задачами, на какое время поставить напоминание?
|
||||||
|
[2.4s t=215]
|
||||||
|
|
||||||
|
YOU: спасибо тебе
|
||||||
|
MAVEN: Сейчас 20:25. Это утра или вечера?
|
||||||
|
[1.0s t=216]
|
||||||
|
|
||||||
|
YOU: спокойной ночи
|
||||||
|
MAVEN: хорошо, напомню послезавтра в 10:00.
|
||||||
|
[0.9s t=217]
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Two weeks of talking to Maven, as a baseline to re-run
|
||||||
|
|
||||||
|
Date: 2026-08-08.
|
||||||
|
Build: `beb093a` on master, the five compose services as deployed, 41 hours up.
|
||||||
|
Reach: `POST /api/chat` on mavweb, 140 turns over fourteen simulated days.
|
||||||
|
Turn source is `tap:text`, so this exercises the path the mic and telegram take.
|
||||||
|
|
||||||
|
This exists to be compared against. `scripts/usage-run.py` and
|
||||||
|
`scripts/testdata/usage-turns.txt` are in the repo, so a re-run after a routing
|
||||||
|
change is a diff rather than a new opinion. The 2026-08-07 week of usage was
|
||||||
|
typed by hand and cannot be replayed.
|
||||||
|
|
||||||
|
**It measures master, not the branch.** V-655, V-659 and V-660 are unmerged.
|
||||||
|
Every query source that guesses is still in the chain. That is the change this
|
||||||
|
baseline is for.
|
||||||
|
|
||||||
|
## What re-runs and what does not
|
||||||
|
|
||||||
|
The turns file, the driver and the routing behaviour replay. Three things do
|
||||||
|
not. The wall clock was 20:18 to 20:27 throughout, so every clock and agenda
|
||||||
|
answer reads evening. Live search and the feed return different text each day.
|
||||||
|
And the store carries over between runs. A fact written on day 2 is already
|
||||||
|
present when a re-run reaches day 1.
|
||||||
|
|
||||||
|
## Numbers
|
||||||
|
|
||||||
|
| | week (2026-08-07) | fortnight (2026-08-08) |
|
||||||
|
|---|---|---|
|
||||||
|
| turns | 74 | 140 |
|
||||||
|
| p50 | 1.5s | 1.6s |
|
||||||
|
| p95 | 8.0s | 7.1s |
|
||||||
|
| max | 12.3s | 33.7s |
|
||||||
|
| transport errors | 0 | 0 |
|
||||||
|
|
||||||
|
| string in the reply | turns |
|
||||||
|
|---|---|
|
||||||
|
| `на какое время поставить напоминание` | 13 |
|
||||||
|
| `не нашла у тебя такой записи` | 8 |
|
||||||
|
| `Такую команду я не знаю` | 8 |
|
||||||
|
| `для какого города` | 6 |
|
||||||
|
| `В какой день` | 6 |
|
||||||
|
| `пока не умею` | 5 |
|
||||||
|
| `Когда?` | 3 |
|
||||||
|
|
||||||
|
**Zero transport errors is not zero wrong answers.** It counts turns that
|
||||||
|
failed to return a reply, and none did. Every quality number is below.
|
||||||
|
|
||||||
|
Those seven strings appear in 49 of 140 turns. Some turns carry two, because a
|
||||||
|
parked clarify appends to whatever else was said.
|
||||||
|
|
||||||
|
The 33.7s outlier is one note write on day 2. p95 improved against the week
|
||||||
|
despite it.
|
||||||
|
|
||||||
|
## The three defects worth diffing against
|
||||||
|
|
||||||
|
### 1. A parked reminder clarify still contaminates later turns
|
||||||
|
|
||||||
|
The week test called this the single worst thing to talk to and it is unchanged.
|
||||||
|
Nineteen turns carry a clarify tail. The worst run is day 1, turns 7 to 13,
|
||||||
|
which spans a day boundary:
|
||||||
|
|
||||||
|
```
|
||||||
|
что такое TCP? -> для какого города? На какое время поставить напоминание?
|
||||||
|
сколько будет 17 на 23? -> для какого города? На какое время поставить напоминание?
|
||||||
|
спасибо -> Сейчас 20:18. В какой день?
|
||||||
|
привет -> Сейчас 20:18. В какой день?
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that `привет` and `спасибо` do not clear it, and neither does a new day.
|
||||||
|
|
||||||
|
### 2. Query sources that guess still claim turns they cannot answer
|
||||||
|
|
||||||
|
Weather took `сколько будет 17 на 23?`, `что такое TCP?` and `какая скорость у
|
||||||
|
меня сейчас?`, answering `для какого города?` to all three. The feed took
|
||||||
|
`какой у меня любимый язык?` and `хватает ли места под новые бэкапы?` and
|
||||||
|
answered with kernel headlines.
|
||||||
|
|
||||||
|
This is the exact class V-655 removes by marking a source `guesses: true` and
|
||||||
|
taking it out of `queryWalk`. Six turns here, so the re-run has a number to move.
|
||||||
|
|
||||||
|
### 3. A question can still be read as a capture
|
||||||
|
|
||||||
|
`что я сохранил про Сочи?` answered `Хорошо, сохраню.` The utterance is
|
||||||
|
interrogative and was routed to a write. `IsQuestionShaped` catches this
|
||||||
|
downstream on some paths and did not catch it here.
|
||||||
|
|
||||||
|
## What did work
|
||||||
|
|
||||||
|
Reminders with a spoken time land correctly, which is V-572 holding:
|
||||||
|
`напомни завтра в 9 утра купить хлеб` returned `хорошо, напомню завтра в 09:00.`
|
||||||
|
|
||||||
|
Facts round-trip. `запиши что новый роутер стоит 8000 рублей` then `сколько
|
||||||
|
стоил роутер?` returned the stored value. So did the wifi password and the
|
||||||
|
doctor's appointment.
|
||||||
|
|
||||||
|
World questions answer when no local source claims them first. `что такое NAT?`
|
||||||
|
returned a real definition.
|
||||||
|
|
||||||
|
Stage 0 answers land at 0.0 to 0.4s, unchanged.
|
||||||
|
|
||||||
|
## What this does not cover
|
||||||
|
|
||||||
|
The voice loop, because `mavwaked` and `mavenclient` are not deployed. Reminder
|
||||||
|
delivery, because nothing fired inside the run window. Telegram intake. And the
|
||||||
|
three-head routing model, which does not run in Go at all.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestDumpPrompt writes the router prompt and grammar to disk so the training
|
||||||
|
// workspace labels with the daemon's own contract rather than a retyped copy.
|
||||||
|
// It is inert unless MAVEN_DUMP_PROMPT names a directory.
|
||||||
|
func TestDumpPrompt(t *testing.T) {
|
||||||
|
dir := os.Getenv("MAVEN_DUMP_PROMPT")
|
||||||
|
if dir == "" {
|
||||||
|
t.Skip("MAVEN_DUMP_PROMPT unset")
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(dir+"/route_system.txt", []byte(routeSystem), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(dir+"/route_grammar.gbnf", []byte(routeGrammar), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+167
@@ -0,0 +1,167 @@
|
|||||||
|
# Day 1
|
||||||
|
доброе утро
|
||||||
|
какой сегодня день?
|
||||||
|
сколько времени?
|
||||||
|
запиши что я пью кофе без сахара
|
||||||
|
мой любимый язык программирования go
|
||||||
|
напомни в 11:00 позвонить маме
|
||||||
|
что у меня сегодня?
|
||||||
|
что такое TCP?
|
||||||
|
сколько будет 17 на 23?
|
||||||
|
спасибо
|
||||||
|
|
||||||
|
# Day 2
|
||||||
|
привет
|
||||||
|
что нового?
|
||||||
|
какая погода?
|
||||||
|
запиши что пароль от вайфая лежит в ящике стола
|
||||||
|
где лежит вайфай пароль?
|
||||||
|
добавь молоко в список покупок
|
||||||
|
что у меня в списке покупок?
|
||||||
|
кто такой Линус Торвальдс?
|
||||||
|
какой у меня любимый язык?
|
||||||
|
сколько у меня задач?
|
||||||
|
|
||||||
|
# Day 3
|
||||||
|
как дела?
|
||||||
|
напомни завтра в 9 утра купить хлеб
|
||||||
|
что у меня завтра?
|
||||||
|
отмени напоминание про хлеб
|
||||||
|
какие у меня напоминания?
|
||||||
|
сохрани мне адрес гостиницы в Сочи
|
||||||
|
что я сохранил про Сочи?
|
||||||
|
почему сервер тормозит?
|
||||||
|
хватает ли места под новые бэкапы?
|
||||||
|
выключи свет в спальне
|
||||||
|
|
||||||
|
# Day 4
|
||||||
|
доброе утро
|
||||||
|
что я пропустил?
|
||||||
|
о чём мы вчера говорили?
|
||||||
|
запиши что я записался к врачу на четверг
|
||||||
|
когда я иду к врачу?
|
||||||
|
что такое ZFS?
|
||||||
|
столица Франции?
|
||||||
|
переведи слово ремонт на английский
|
||||||
|
сколько я потратил в этом месяце?
|
||||||
|
спокойной ночи
|
||||||
|
|
||||||
|
# Day 5
|
||||||
|
привет
|
||||||
|
какая погода в Москве?
|
||||||
|
что там с бэкапами?
|
||||||
|
покажи что требует внимания
|
||||||
|
отметь это как сделанное
|
||||||
|
запиши что я купил новые наушники
|
||||||
|
какие у меня заметки за неделю?
|
||||||
|
расскажи про Kubernetes
|
||||||
|
кто я?
|
||||||
|
пока
|
||||||
|
|
||||||
|
# Day 6
|
||||||
|
доброе утро
|
||||||
|
сколько времени?
|
||||||
|
напомни в 18:30 позвонить в банк
|
||||||
|
поставь чайник
|
||||||
|
включи музыку
|
||||||
|
что у меня в календаре на пятницу?
|
||||||
|
во сколько у меня встреча?
|
||||||
|
запиши что дедлайн по проекту в понедельник
|
||||||
|
успею ли я до дедлайна?
|
||||||
|
спасибо
|
||||||
|
|
||||||
|
# Day 7
|
||||||
|
привет
|
||||||
|
как ты?
|
||||||
|
расскажи анекдот
|
||||||
|
что ты умеешь?
|
||||||
|
запиши что я начал бегать по утрам
|
||||||
|
я бегаю по утрам уже неделю
|
||||||
|
как часто я бегаю?
|
||||||
|
сколько стоит биткоин?
|
||||||
|
какие новости?
|
||||||
|
хорошего дня
|
||||||
|
|
||||||
|
# Day 8
|
||||||
|
доброе утро
|
||||||
|
что у меня сегодня?
|
||||||
|
напомни через час выпить воды
|
||||||
|
я выпил воды
|
||||||
|
запиши что кот ест только сухой корм
|
||||||
|
чем питается кот?
|
||||||
|
что такое DNS?
|
||||||
|
проверь статус uptime kuma
|
||||||
|
всё ли в порядке с сервером?
|
||||||
|
спасибо
|
||||||
|
|
||||||
|
# Day 9
|
||||||
|
привет
|
||||||
|
какой сегодня день недели?
|
||||||
|
добавь хлеб и сыр в список покупок
|
||||||
|
что в списке покупок?
|
||||||
|
удали молоко из списка
|
||||||
|
напомни завтра утром вынести мусор
|
||||||
|
запиши что я поменял масло в машине
|
||||||
|
когда я менял масло?
|
||||||
|
сколько будет 144 делить на 12?
|
||||||
|
пока
|
||||||
|
|
||||||
|
# Day 10
|
||||||
|
доброе утро
|
||||||
|
что нового за ночь?
|
||||||
|
почему интернет медленный?
|
||||||
|
какая скорость у меня сейчас?
|
||||||
|
запиши что новый роутер стоит 8000 рублей
|
||||||
|
сколько стоил роутер?
|
||||||
|
что такое NAT?
|
||||||
|
напомни в субботу позвонить бабушке
|
||||||
|
покажи мои напоминания
|
||||||
|
спасибо
|
||||||
|
|
||||||
|
# Day 11
|
||||||
|
привет
|
||||||
|
как погода на выходных?
|
||||||
|
что у меня на этой неделе?
|
||||||
|
запиши что я хочу прочитать книгу про Go
|
||||||
|
что я хотел прочитать?
|
||||||
|
объясни что такое горутина
|
||||||
|
кто написал Войну и мир?
|
||||||
|
включи свет на кухне
|
||||||
|
закрой шторы в комнате
|
||||||
|
спокойной ночи
|
||||||
|
|
||||||
|
# Day 12
|
||||||
|
доброе утро
|
||||||
|
сколько сейчас времени?
|
||||||
|
я не то имел в виду
|
||||||
|
о чём мы говорили?
|
||||||
|
напомни
|
||||||
|
сделай это
|
||||||
|
запиши что я перешёл на новый тариф
|
||||||
|
какой у меня тариф?
|
||||||
|
сколько я плачу за интернет?
|
||||||
|
спасибо
|
||||||
|
|
||||||
|
# Day 13
|
||||||
|
привет
|
||||||
|
что там с задачами?
|
||||||
|
закрывай
|
||||||
|
отметь задачу про бэкапы как сделанную
|
||||||
|
что осталось нерешённым?
|
||||||
|
запиши что я договорился о встрече в среду
|
||||||
|
когда у меня встреча?
|
||||||
|
какая температура на улице?
|
||||||
|
что такое RAID 5?
|
||||||
|
пока
|
||||||
|
|
||||||
|
# Day 14
|
||||||
|
доброе утро
|
||||||
|
подведи итоги недели
|
||||||
|
что я делал за последние две недели?
|
||||||
|
какие заметки я сохранил?
|
||||||
|
о чём я чаще всего спрашиваю?
|
||||||
|
напомни в понедельник в 10 проверить бэкапы
|
||||||
|
что у меня в понедельник?
|
||||||
|
ты меня понимаешь?
|
||||||
|
спасибо тебе
|
||||||
|
спокойной ночи
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
"""Drive a fortnight of conversation through POST /api/chat and record it.
|
||||||
|
|
||||||
|
The 2026-08-07 week of usage was typed by hand. This is the same reach and the
|
||||||
|
same turn source, tap:text, so it exercises the path the mic and telegram take.
|
||||||
|
|
||||||
|
The endpoint is a form POST that redirects to /chat with the reply in the query
|
||||||
|
string. Reading the Location header is the whole protocol, so nothing here
|
||||||
|
parses HTML.
|
||||||
|
|
||||||
|
This exists to be re-run. The baseline is 2026-08-08 against master at beb093a,
|
||||||
|
in docs/evals/2026-08-08-two-weeks.md. Re-running the same turns after a routing
|
||||||
|
change is the comparison, so edit the turns file by adding, never by rewriting.
|
||||||
|
|
||||||
|
python3 scripts/usage-run.py scripts/testdata/usage-turns.txt out-prefix
|
||||||
|
|
||||||
|
Input is one utterance per line. A line starting with "# " opens a day. A blank
|
||||||
|
line is ignored. Output is a markdown transcript and a jsonl log beside it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
URL = "http://127.0.0.1:9201/api/chat"
|
||||||
|
TIMEOUT = 90
|
||||||
|
|
||||||
|
|
||||||
|
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||||
|
"""A 303 carries the reply. Following it would throw the reply away."""
|
||||||
|
|
||||||
|
def redirect_request(self, *a, **kw):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ProxyHandler({}) is not optional. This box exports http_proxy, urllib honours
|
||||||
|
# it, and the proxy answers 503 for a loopback address.
|
||||||
|
OPENER = urllib.request.build_opener(NoRedirect, urllib.request.ProxyHandler({}))
|
||||||
|
|
||||||
|
|
||||||
|
def turn(text):
|
||||||
|
body = urllib.parse.urlencode({"text": text}).encode()
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
try:
|
||||||
|
OPENER.open(urllib.request.Request(URL, data=body), timeout=TIMEOUT)
|
||||||
|
return {"reply": "", "error": "no redirect", "secs": time.perf_counter() - t0}
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
dt = time.perf_counter() - t0
|
||||||
|
if e.code != 303:
|
||||||
|
return {"reply": "", "error": f"HTTP {e.code}", "secs": dt}
|
||||||
|
loc = e.headers.get("Location", "")
|
||||||
|
q = urllib.parse.parse_qs(urllib.parse.urlparse(loc).query)
|
||||||
|
return {
|
||||||
|
"reply": q.get("r", [""])[0],
|
||||||
|
"source": q.get("src", q.get("source", [""]))[0],
|
||||||
|
"trace": q.get("t", [""])[0],
|
||||||
|
"secs": dt,
|
||||||
|
}
|
||||||
|
except Exception as e: # a dead box must not lose the turns already done
|
||||||
|
return {"reply": "", "error": str(e), "secs": time.perf_counter() - t0}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lines = [l.rstrip("\n") for l in open(sys.argv[1])]
|
||||||
|
prefix = sys.argv[2]
|
||||||
|
md = open(prefix + "-transcript.md", "w")
|
||||||
|
log = open(prefix + ".jsonl", "w")
|
||||||
|
|
||||||
|
day = 0
|
||||||
|
n = 0
|
||||||
|
print(f"# Raw transcript, two weeks of usage\n", file=md)
|
||||||
|
for line in lines:
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
if line.startswith("# "):
|
||||||
|
if day:
|
||||||
|
print("```\n", file=md)
|
||||||
|
day += 1
|
||||||
|
print(f"## {line[2:]}\n\n```", file=md)
|
||||||
|
continue
|
||||||
|
n += 1
|
||||||
|
r = turn(line)
|
||||||
|
r["day"] = day
|
||||||
|
r["n"] = n
|
||||||
|
r["utterance"] = line
|
||||||
|
log.write(json.dumps(r, ensure_ascii=False) + "\n")
|
||||||
|
log.flush()
|
||||||
|
reply = r.get("error") or r["reply"]
|
||||||
|
print(f"YOU: {line}", file=md)
|
||||||
|
print(f"MAVEN: {reply}", file=md)
|
||||||
|
tag = f"[{r['secs']:.1f}s"
|
||||||
|
if r.get("source"):
|
||||||
|
tag += f" src={r['source']}"
|
||||||
|
print(f" {tag} t={r.get('trace', '')}]\n", file=md)
|
||||||
|
md.flush()
|
||||||
|
print(f"{n:3} d{day} {r['secs']:5.1f}s {line[:40]:40s} -> {reply[:60]}",
|
||||||
|
flush=True)
|
||||||
|
print("```", file=md)
|
||||||
|
md.close()
|
||||||
|
log.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user