package router import "regexp" // WorldQueryGrammars — stage-0 rules for the two question shapes that name the // world in their own words, and say so plainly enough that no scorer is needed // (V-655). // // They exist because of what happens when nothing deterministic claims these. // Measured on the box on 2026-08-07 (docs/evals/2026-08-07-week-of-usage.md, // section 4): "что такое TCP?" and "сколько будет 17 на 23?" were both answered // "для какого города?", and "кто такой Линус Торвальдс?" was answered "не знаю — // не нашла у тебя такой записи". None of those three is about him, about the // weather, or about anything on this box. // // The mechanism is the destination, not the answer. Naming SourceWorld does not // send the turn outside and does not skip a single source that looks something // up: his notes, his facts and the personal boundary all still run first, in the // order they always did. What it does is stop the sources that claim on seed // similarity from taking the turn on the way past. Weather cannot claim a // question about a protocol once the utterance has said which side it is on. // // Both patterns are spelled out here rather than drawn from internal/lexicon, // which is the same call the agenda rules made: these are interrogative FRAMES // of two words, not a closed class of single words, and the lexicon holds // classes. Nothing here is a stem pattern over open vocabulary — the variable // part of each rule is the topic, and the rule reads none of it. func WorldQueryGrammars() []Grammar { return []Grammar{ { // "что такое X", "кто такой X". A request for what a thing or a // person IS, which his own data can answer and usually cannot. // // The topic is deliberately not captured into Slots.Text. Every // source below reads the utterance, "что такое TCP?" is already the // best query string for it, and the agenda rules make the same call // for the same reason. Name: "definition-query", Pattern: definitionQueryPattern, Build: queryTo(SourceWorld), }, { // "сколько будет 17 на 23", "сколько будет 2+2". Arithmetic, which // the metasearch answers and no local source holds. The digits are // what make it arithmetic: "сколько будет гостей" names no number // and is a question about his evening. Name: "arithmetic-query", Pattern: arithmeticQueryPattern, Build: queryTo(SourceWorld), }, } } // definitionQueryPattern — anchored at the start, because "напомни узнать что // такое TCP" is a reminder that happens to contain the frame. // // (\s|[?!.]|$) and not \b: Go's \b is ASCII-only and never fires after a // Cyrillic letter, so the ASCII form silently matches nothing. The agenda rules // carry the same note. var definitionQueryPattern = regexp.MustCompile( `(?i)^\s*(что\s+так(ое|ая)|кто\s+так(ой|ая|ие)|what\s+is|who\s+is)(\s|[?!.]|$)`) // arithmeticQueryPattern — the ask, then a digit somewhere after it. Loose on // what sits between them on purpose: the operator is spoken half a dozen ways // ("на", "умножить на", "плюс", "+") and reading them is the calculator's job, // not this rule's. All this decides is which side of the boundary the turn is // on. var arithmeticQueryPattern = regexp.MustCompile( `(?i)^\s*(сколько\s+будет|посчитай|вычисли|how\s+much\s+is)\s.*\d`) // queryTo builds a stage-0 query Decision that names where the answer lives. // // The utterance travels intact and no slot is filled, which is the same // contract agendaQueryBuild has: confidence 1.0 on the intent and the // destination, and every source below still decides for itself whether it has // an answer. Naming a destination narrows who may guess. It promises nothing. func queryTo(dest Source) func([]string) (Decision, bool) { return func([]string) (Decision, bool) { return Decision{ Stage: 0, Intent: IntentQuery, Confidence: 1.0, Source: dest, }, true } }