a Russian act reaches a tool, and the seeds stop disagreeing (V-633)
Three tangled defects, fixed together because each one hid the others. DefaultActMatcher matched an exact English prefix and internal/tool.Matcher delegated straight to it, so no Russian utterance could reach a tool: 55 of the 69 lines in models/seeds/act.txt routed to IntentAct and fell to proposeGap. Tools now carry spoken aliases from deploy/mavend.json, matched as exact leading tokens, longest phrase first. Config data, not a stem pattern in code. The comment claiming "the production matcher is fuzzy" was false and is gone. Seven lines were exact duplicates inside models/seeds/query.txt, each one a second identical vector double-weighting its region. "как дела у сервера" carried both a query and a system label. It leaves system.txt, because replySystem's stats arm answers "системная статистика пока не подключена." and always did. The mode inventory records that shape as act.tool.hoststats rather than a system mode. Fixture unchanged at 69/91, and it cannot see any of this: no host-stat case and no Russian act in it. TestActMatcherAliases is the coverage. docs/evals/2026-08-06-russian-acts-reach-tools.md has the numbers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117tgnmbgZpHVV3XSNw8Qua
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Russian acts reach tools
|
||||
|
||||
**06-08-2026. V-633.** Measured with `TestONNXBaseline`, 91-case RU routing fixture,
|
||||
classifier plus the ONNX embedder. No LLM arm in this run.
|
||||
|
||||
## What was wrong
|
||||
|
||||
Three defects, tangled enough that fixing one alone would have looked like progress.
|
||||
|
||||
**No Russian utterance could reach a tool.** `DefaultActMatcher` in
|
||||
`internal/router/slots.go` matched an exact English prefix, and `internal/tool.Matcher`
|
||||
delegated straight to it. Its comment claimed "the production matcher is fuzzy, this is the
|
||||
scaffold floor". There is no other matcher, and `DefaultGrammars` is the only place
|
||||
`Slots.Fn` is set at stage 0, so the floor was the ceiling. Measured with a throwaway
|
||||
matcher test over the seeds:
|
||||
|
||||
```text
|
||||
"покажи статус nginx" ok=false "restart nginx" ok=true fn=restart
|
||||
"сколько места на диске" ok=false "disk" ok=true fn=disk
|
||||
"свободная память" ok=false "uptime" ok=true fn=uptime
|
||||
"перезагрузи роутер" ok=false
|
||||
```
|
||||
|
||||
55 of the 69 lines in `models/seeds/act.txt` routed to `IntentAct` and then fell to
|
||||
`proposeGap`. Praxis was never affected: `PraxisGrammars` fills `Slots.Fn` itself.
|
||||
|
||||
**Seven lines were duplicated inside `models/seeds/query.txt`.** A duplicate is a second
|
||||
identical vector, so it double-weights its region in nearest-neighbour scoring.
|
||||
|
||||
```text
|
||||
сколько человек дома
|
||||
кто сейчас дома
|
||||
какая загрузка процессора
|
||||
сколько свободного места на диске
|
||||
какой ip адрес у сервера
|
||||
какая версия софта
|
||||
сколько оперативной памяти свободно
|
||||
```
|
||||
|
||||
**`как дела у сервера` carried two labels**, in `query.txt:13` and `system.txt:9`. One
|
||||
string, two identical vectors, disagreeing about the answer.
|
||||
|
||||
## The change
|
||||
|
||||
Tools carry spoken aliases as config data, in `deploy/mavend.json`. They are not a Russian
|
||||
stem pattern in code, which CLAUDE.md forbids. They are not on the tool row either. An
|
||||
ad-hoc tool enabled through `/tools` has no aliases and needs none.
|
||||
|
||||
Aliases and names compete in one table, longest phrase first, so "перезагрузи контейнер"
|
||||
beats "перезагрузи" and "docker-restart" is not shadowed by "restart". Matching is on exact
|
||||
leading tokens rather than lemmas. `перезагрузи роутер` is a command and `перезагрузил
|
||||
роутер` is a fact, and a lemma cannot tell the two apart. That is the trap
|
||||
`cmd/mavend/quiet_toggle.go` documents for `говори`.
|
||||
|
||||
The seven duplicates are gone, and `как дела у сервера` stays in `query.txt` only. It left
|
||||
`system.txt` because system cannot answer it: `replySystem`'s
|
||||
память/загрузк/аптайм arm returns "системная статистика пока не подключена." and always
|
||||
did. That arm is a stub, not a mode, so the mode inventory now lists the shape as
|
||||
`act.tool.hoststats`.
|
||||
|
||||
## Result
|
||||
|
||||
**69/91, 75.8% full, unchanged.** Clarify counts unchanged at 0 false and 8 missed.
|
||||
|
||||
Nothing moved, and that is the honest number. The fixture holds no host-stat case and no
|
||||
Russian act that reaches a tool, so it cannot see either fix. The new coverage is
|
||||
`TestActMatcherAliases`, which asserts the twelve utterances above plus the two refusals.
|
||||
|
||||
## What this does not fix
|
||||
|
||||
Argument quality. `статус sshd` reaches `systemctl status sshd`, but `логи nginx` reaches
|
||||
`journalctl -n 50 -u nginx` only because the tool's argv prefix ends in `-u`. An alias whose
|
||||
remainder is a Russian noun ("перезагрузи роутер") hands `systemctl restart роутер` a target
|
||||
that does not exist. Free text still reaches an argv, which is the resolution rule the
|
||||
ecosystem contract states for Hexis and not yet true here.
|
||||
|
||||
The fixture cannot measure any of this. That is the observability gap V-629 is for.
|
||||
Reference in New Issue
Block a user