c6b11a6d1d
Two defects found probing the new source on the box. "кто ты" was answered from one of his notes. The self source sat below memory and notes, which match by proximity and have no idea the subject is her. It belongs above all three: a question about her has no answer in his data either. And PhraseQuery opens every answer with "вот что я нашла: ", which is deliberate — it marks the answer as a lookup. Her own description is the one subject she did not look up, so this is PhraseSelf instead, same read-only discipline and its own opener. The Stub reads the description out as it stands, which needs no fallback: it is already her voice.
117 lines
5.4 KiB
Go
117 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"regexp"
|
|
|
|
"github.com/kami/maven/internal/phraser"
|
|
)
|
|
|
|
// A question about her — "что ты умеешь", "кто ты" — used to have no answer at
|
|
// all (Vikunja #555). It reached the personal boundary, which claimed it as his
|
|
// and said "не знаю — не нашла у тебя такой записи", because the boundary knows
|
|
// two sides and this is neither: her own description is not his data and it is
|
|
// not the world's either. Letting it past the boundary is no better, because
|
|
// then SearXNG answers about somebody else's assistant.
|
|
//
|
|
// The description does NOT live in the note store. Notes are his. A note about
|
|
// her sitting in his index would come back for "что я записал", would be fed to
|
|
// the digestion worker as something he said, and would be recalled by vector
|
|
// proximity for questions that are not about her at all. It is her own text, so
|
|
// it lives here, in one place, and it is the only copy.
|
|
//
|
|
// This source sits ABOVE the boundary, because a question about her never had
|
|
// an answer below it.
|
|
|
|
// selfDescription — what she is and what this box actually does. Frozen text,
|
|
// and the one rule for editing it: name only what is really wired. Anything
|
|
// that depends on config — the house, the LAN, the feeds, telegram, search — is
|
|
// named as depending on what he allowed, never claimed outright. Inventing a
|
|
// capability here is the same defect as inventing a fact, and it is worse than
|
|
// silence because he would plan around it.
|
|
//
|
|
// Written in her own voice, feminine, addressing him informally, because it is
|
|
// handed to the phraser as the evidence for the answer and the phraser will
|
|
// keep the words it is given.
|
|
const selfDescription = `Я Мэйвен, твоя помощница. Я живу на твоём сервере, ` +
|
|
`и наружу уходит только поисковый запрос — больше ничего.
|
|
|
|
Что я делаю сама: запоминаю, что ты мне говоришь, и потом отвечаю на вопросы ` +
|
|
`об этом; веду заметки; ставлю напоминания; читаю твой календарь и задачи; ` +
|
|
`отвечаю на вопросы о мире — сначала поиском, а если сети нет, то по ` +
|
|
`офлайновой энциклопедии.
|
|
|
|
Что зависит от того, что ты мне разрешил: дом, локальная сеть, ленты, ` +
|
|
`список покупок, погода, телеграм. Если что-то из этого не настроено, я ` +
|
|
`скажу об этом прямо, а не буду выдумывать ответ.
|
|
|
|
Говорю по-русски и по-английски.`
|
|
|
|
// selfSeeds — the questions this source claims. Scoring data like every other
|
|
// topic set: editing one moves the recogniser and has to be re-measured against
|
|
// TestONNXTopics.
|
|
//
|
|
// All of them are about HER — what she is, what she can do, who made her. The
|
|
// neighbouring set is topicAttend, "что требует внимания", which asks about the
|
|
// state of his things; the two share almost nothing but the second person.
|
|
var selfSeeds = []string{
|
|
"что ты умеешь",
|
|
"что ты можешь делать",
|
|
"кто ты такая",
|
|
"расскажи о себе",
|
|
"какие у тебя возможности",
|
|
// Added after measuring: it won self by 0.0002, under the margin, and the
|
|
// floor does not carry it — "способна" names no verb the floor matches.
|
|
"на что ты способна",
|
|
"чем ты можешь помочь",
|
|
"what can you do",
|
|
"who are you",
|
|
}
|
|
|
|
// selfFloor — the offline floor, for a handler with no embedder or a turn whose
|
|
// vector never got computed. Narrow on purpose, like every other floor here: it
|
|
// answers only when the seeds cannot, and a broad guess made blind is worse
|
|
// than a narrow one.
|
|
//
|
|
// Go's \b is ASCII-only and never fires next to a Cyrillic letter, so the
|
|
// Russian patterns spell the boundary out.
|
|
var selfPatterns = []*regexp.Regexp{
|
|
regexp.MustCompile(`(?i)(^|[^\p{L}\p{N}])ты\s+(умеешь|можешь)([^\p{L}\p{N}]|$)`),
|
|
regexp.MustCompile(`(?i)(^|[^\p{L}\p{N}])кто\s+ты([^\p{L}\p{N}]|$)`),
|
|
regexp.MustCompile(`(?i)(^|[^\p{L}\p{N}])(расскажи|поведай)\s+о\s+себе([^\p{L}\p{N}]|$)`),
|
|
regexp.MustCompile(`(?i)\bwhat\s+can\s+you\s+do\b`),
|
|
regexp.MustCompile(`(?i)\bwho\s+are\s+you\b`),
|
|
}
|
|
|
|
func selfFloor(utterance string) bool {
|
|
for _, re := range selfPatterns {
|
|
if re.MatchString(utterance) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// querySelf answers a question about her from selfDescription. The description
|
|
// goes through the phraser as evidence so the answer is shaped to what he
|
|
// asked — "что ты умеешь" and "кто ты" want different halves of it — and falls
|
|
// back to the text itself, which is already readable, if the model is down.
|
|
func (h *reactiveHandler) querySelf(ctx context.Context, t *queryTurn) (string, bool) {
|
|
if !h.turnIsAbout(ctx, t, topicSelf, selfFloor) {
|
|
return "", false
|
|
}
|
|
var reply string
|
|
if h.phraser != nil {
|
|
var err error
|
|
reply, err = h.phraser.PhraseSelf(ctx, t.dec.Utterance, selfDescription)
|
|
if err != nil {
|
|
log.Printf("voice: phrase self: %v", err)
|
|
}
|
|
}
|
|
if reply == "" {
|
|
reply = phraser.Q(phraser.QueryFound, map[string]string{"text": selfDescription})
|
|
}
|
|
return reply, true
|
|
}
|