Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96d97e8964 | |||
| 02f6e8ad4a |
@@ -681,8 +681,11 @@ world questions, so she needs to read external sources. What replaces it:
|
|||||||
returned "Перехват TCP-соединения". "кто написал Войну и мир" returned an episode of
|
returned "Перехват TCP-соединения". "кто написал Войну и мир" returned an episode of
|
||||||
Doctor Who. `kiwix.Topic` drops the narrative request, the interrogative and a verb
|
Doctor Who. `kiwix.Topic` drops the narrative request, the interrogative and a verb
|
||||||
behind one. `kiwix.TitlePath` tries the exact article first, since a ZIM is addressable
|
behind one. `kiwix.TitlePath` tries the exact article first, since a ZIM is addressable
|
||||||
by title and a wrong title is a 404. Four of eight questions reach the right article
|
by title and a wrong title is a 404. Five of eight questions reach the right article
|
||||||
where they did not, two were already right, and nothing regressed. Both apply on the
|
where they did not, one was already right, and nothing regressed. The title needs its
|
||||||
|
leading capital, so `TitleCandidates` tries the spoken form and then the capitalized
|
||||||
|
one. **"столица Франции" is answered by a title redirect to Париж**, which is the case
|
||||||
|
the 2026-08-05 measurement named as unreachable by any lexical signal. Both apply on the
|
||||||
verbatim path alone. The rewriter already reduces a question, and reducing twice takes
|
verbatim path alone. The rewriter already reduces a question, and reducing twice takes
|
||||||
the topic off its input.
|
the topic off its input.
|
||||||
`Response.Empty()` is the whole gate and there is no quality threshold in front of it:
|
`Response.Empty()` is the whole gate and there is no quality threshold in front of it:
|
||||||
|
|||||||
@@ -938,10 +938,12 @@ func (h *reactiveHandler) queryKiwix(ctx context.Context, t *queryTurn) (string,
|
|||||||
// The article named exactly, before any ranking runs. A ZIM is
|
// The article named exactly, before any ranking runs. A ZIM is
|
||||||
// addressable by title and a wrong title is a 404, so this either
|
// addressable by title and a wrong title is a 404, so this either
|
||||||
// answers or costs one request that says nothing.
|
// answers or costs one request that says nothing.
|
||||||
page, err := h.kiwix.client.Article(ctxK, kiwix.TitlePath(book, topic), h.kiwix.runes)
|
for _, cand := range kiwix.TitleCandidates(topic) {
|
||||||
if err == nil && page.Text != "" {
|
page, err := h.kiwix.client.Article(ctxK, kiwix.TitlePath(book, cand), h.kiwix.runes)
|
||||||
log.Printf("voice: kiwix: %q in %q → title hit %q", topic, book, page.Title)
|
if err == nil && page.Text != "" {
|
||||||
return h.kiwixReply(ctx, t, page.Title, page.Text)
|
log.Printf("voice: kiwix: %q in %q → title hit %q", topic, book, page.Title)
|
||||||
|
return h.kiwixReply(ctx, t, page.Title, page.Text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pattern = topic
|
pattern = topic
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ A ZIM is also addressable by title, which nothing here used. `/A/Франция`
|
|||||||
exact title is safe to try first: it either answers or costs one request that
|
exact title is safe to try first: it either answers or costs one request that
|
||||||
says nothing.
|
says nothing.
|
||||||
|
|
||||||
|
The title has to carry its capital. `/A/фотосинтез` is a 404 and
|
||||||
|
`/A/Фотосинтез` is a 200. The spoken form is tried first anyway, so a title
|
||||||
|
that begins lowercase on purpose keeps its chance.
|
||||||
|
|
||||||
## What shipped, measured
|
## What shipped, measured
|
||||||
|
|
||||||
`kiwix.Topic` drops the narrative request, the interrogative and a verb sitting
|
`kiwix.Topic` drops the narrative request, the interrogative and a verb sitting
|
||||||
@@ -71,25 +75,31 @@ would take the topic off the rewriter's input.
|
|||||||
| question | before | after |
|
| question | before | after |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| что такое TCP? | Перехват TCP-соединения | **TCP** (by title) |
|
| что такое TCP? | Перехват TCP-соединения | **TCP** (by title) |
|
||||||
| что такое фотосинтез | C4-фотосинтез | **Фотосинтез** |
|
| что такое фотосинтез | C4-фотосинтез | **Фотосинтез** (by title) |
|
||||||
| кто такой Линус Торвальдс? | Tux | **Торвальдс, Линус** (by title) |
|
| кто такой Линус Торвальдс? | Tux | **Торвальдс, Линус** (by title) |
|
||||||
| кто написал Войну и мир | Радуйся, мир (Доктор Кто) | **Война и мир** |
|
| кто написал Войну и мир | Радуйся, мир (Доктор Кто) | **Война и мир** |
|
||||||
| что такое чёрная дыра | Чёрная дыра | Чёрная дыра |
|
| столица Франции | Список столиц Олимпийских игр | **Париж** (by title) |
|
||||||
|
| что такое чёрная дыра | Чёрная дыра | Чёрная дыра (by title) |
|
||||||
| почему небо голубое | Город золотой | Под небом голубым… (фильм) |
|
| почему небо голубое | Город золотой | Под небом голубым… (фильм) |
|
||||||
| почему трава зелёная | Сено | Зелень |
|
| почему трава зелёная | Сено | Зелень |
|
||||||
| столица Франции | Список столиц Олимпийских игр | Список столиц Олимпийских игр |
|
|
||||||
|
|
||||||
Four questions reach the right article where they did not. Two were already
|
Five questions reach the right article where they did not. One was already
|
||||||
right and stay right. Nothing regressed.
|
right and stays right. Nothing regressed.
|
||||||
|
|
||||||
|
"столица Франции" is the surprise. The 2026-08-05 measurement named it as the
|
||||||
|
case a quality gate must not break, because the answer is Париж and that word
|
||||||
|
is not in the question. The ZIM holds a title redirect, so asking for the
|
||||||
|
article titled "Столица Франции" returns Париж. Retrieval by title reaches an
|
||||||
|
answer that retrieval by keyword cannot.
|
||||||
|
|
||||||
## What is still wrong
|
## What is still wrong
|
||||||
|
|
||||||
Two of the eight are still not answered, and both are the same shape. The
|
Two of the eight are still not answered, and both are the same shape. The
|
||||||
question names no article. "почему небо голубое" is answered by Rayleigh
|
question names no article and no redirect covers it. "почему небо голубое" is
|
||||||
scattering and "столица Франции" by the lead of Франция. Neither title is in
|
answered by Rayleigh scattering, and nothing in the question says so. Keyword
|
||||||
the question. Keyword retrieval cannot bridge that and neither can a
|
retrieval cannot bridge that and neither can a threshold. The candidates are a
|
||||||
threshold. The candidates are a semantic index over titles, or asking the
|
semantic index over titles, or asking the resident model for the article title
|
||||||
resident model for the article title rather than for keywords.
|
rather than for keywords.
|
||||||
|
|
||||||
`Response.Empty()` is still the whole gate. A wrong article that the search
|
`Response.Empty()` is still the whole gate. A wrong article that the search
|
||||||
does return is still spoken. What this change buys is that the article is
|
does return is still spoken. What this change buys is that the article is
|
||||||
|
|||||||
@@ -39,9 +39,13 @@ func TestLiveTopicBeatsTheSentence(t *testing.T) {
|
|||||||
before := firstTitle(ctx, c, q, book)
|
before := firstTitle(ctx, c, q, book)
|
||||||
topic := Topic(q)
|
topic := Topic(q)
|
||||||
after := ""
|
after := ""
|
||||||
if page, err := c.Article(ctx, TitlePath(book, topic), 400); err == nil && page.Text != "" {
|
for _, cand := range TitleCandidates(topic) {
|
||||||
after = page.Title + " (by title)"
|
if page, err := c.Article(ctx, TitlePath(book, cand), 400); err == nil && page.Text != "" {
|
||||||
} else {
|
after = page.Title + " (by title)"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if after == "" {
|
||||||
after = firstTitle(ctx, c, topic, book)
|
after = firstTitle(ctx, c, topic, book)
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
|
|||||||
@@ -61,6 +61,26 @@ func Topic(utterance string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TitleCandidates is the topic as it might be titled, best first.
|
||||||
|
//
|
||||||
|
// A ZIM title is capitalized and the utterance is not: measured on 2026-08-09,
|
||||||
|
// `/A/фотосинтез` is a 404 and `/A/Фотосинтез` is a 200. The spoken form is
|
||||||
|
// tried first anyway, because a title that begins lowercase on purpose
|
||||||
|
// ("iPhone") would not survive capitalizing it. Both are one request each
|
||||||
|
// against a server on the same box, and a miss is a 404 rather than a wrong
|
||||||
|
// article.
|
||||||
|
func TitleCandidates(topic string) []string {
|
||||||
|
if topic == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r := []rune(topic)
|
||||||
|
up := unicode.ToUpper(r[0])
|
||||||
|
if up == r[0] {
|
||||||
|
return []string{topic}
|
||||||
|
}
|
||||||
|
return []string{topic, string(up) + string(r[1:])}
|
||||||
|
}
|
||||||
|
|
||||||
func isCopula(w string) bool {
|
func isCopula(w string) bool {
|
||||||
switch w {
|
switch w {
|
||||||
case "такое", "такой", "такая", "такие", "is", "are", "was", "were":
|
case "такое", "такой", "такая", "такие", "is", "are", "was", "were":
|
||||||
|
|||||||
@@ -49,3 +49,19 @@ func TestTitlePathEscapesAndUnderscores(t *testing.T) {
|
|||||||
t.Errorf("TitlePath = %q, want %q", got, want)
|
t.Errorf("TitlePath = %q, want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A ZIM title carries a leading capital and the utterance does not. The spoken
|
||||||
|
// form is still tried first, so a title that begins lowercase on purpose keeps
|
||||||
|
// its chance.
|
||||||
|
func TestTitleCandidatesTryTheSpokenFormFirst(t *testing.T) {
|
||||||
|
got := TitleCandidates("фотосинтез")
|
||||||
|
if len(got) != 2 || got[0] != "фотосинтез" || got[1] != "Фотосинтез" {
|
||||||
|
t.Errorf("TitleCandidates = %q", got)
|
||||||
|
}
|
||||||
|
if got := TitleCandidates("TCP"); len(got) != 1 || got[0] != "TCP" {
|
||||||
|
t.Errorf("an already-capital topic was tried twice: %q", got)
|
||||||
|
}
|
||||||
|
if got := TitleCandidates(""); got != nil {
|
||||||
|
t.Errorf("TitleCandidates(\"\") = %q, want nil", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Raw transcript, two weeks of usage
|
||||||
|
|
||||||
|
YOU: что такое TCP?
|
||||||
|
MAVEN: есть такое: Tcp/Ip — Википедия — TCP позволяет регулировать нагрузку на сеть, а также уменьшать время ожидания данных при передаче на большие расстояния. Более того, TCP гарантирует, что полученные ...
|
||||||
|
[2.1s src=search t=666]
|
||||||
|
|
||||||
|
YOU: кто такой Линус Торвальдс?
|
||||||
|
MAVEN: вот что я нашла: Торвальдс, Линус — Википедия — Ли́нус Бенедикт То́рвальдс (встречается написание Ту́рвальдс, швед. Linus Benedict Torvalds МФА: [ˈliːn.ɵs ˈtuːr.valds] о файле; род. 28 декабря 1969, Хельсинки) — финско-американский программист, создатель ядра Linux.
|
||||||
|
[0.8s src=search t=667]
|
||||||
|
|
||||||
|
YOU: что такое фотосинтез
|
||||||
|
MAVEN: нашла вот это: Фотосинтез — Фотосинтез
|
||||||
|
Схематическое изображение процесса фотосинтеза, происходящего в растениях
|
||||||
|
Упрощённое уравнение фотосинтеза, происходящего в растениях
|
||||||
|
Упрощённая схема фотосинтеза
|
||||||
|
Фотоси́нтез (от др.-греч. φῶς — « свет » и σύνθεσις — «соединённый», «складной», «связывание», « синтез ») — сл…
|
||||||
|
[0.6s src=kiwix t=668]
|
||||||
|
|
||||||
|
YOU: кто написал Войну и мир
|
||||||
|
MAVEN: вот что я нашла: Война и мир — Война и мир
|
||||||
|
Война и мир
|
||||||
|
Война и миръ
|
||||||
|
Обложка издания 1873 года
|
||||||
|
Жанр
|
||||||
|
роман-эпопея
|
||||||
|
Автор
|
||||||
|
Лев Николаевич Толстой
|
||||||
|
Язык оригинала
|
||||||
|
русский, фрагменты на французском и немецком языках
|
||||||
|
Дата написания
|
||||||
|
1863—1869, 1873
|
||||||
|
Дата первой публикации
|
||||||
|
1865—1869
|
||||||
|
Предыдущее
|
||||||
|
Казаки
|
||||||
|
Следующее
|
||||||
|
Анна Каренина
|
||||||
|
Тек…
|
||||||
|
[12.4s src=kiwix t=669]
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{"reply": "есть такое: Tcp/Ip — Википедия — TCP позволяет регулировать нагрузку на сеть, а также уменьшать время ожидания данных при передаче на большие расстояния. Более того, TCP гарантирует, что полученные ...", "source": "search", "trace": "666", "secs": 2.1235475060530007, "day": 0, "n": 1, "utterance": "что такое TCP?"}
|
||||||
|
{"reply": "вот что я нашла: Торвальдс, Линус — Википедия — Ли́нус Бенедикт То́рвальдс (встречается написание Ту́рвальдс, швед. Linus Benedict Torvalds МФА: [ˈliːn.ɵs ˈtuːr.valds] о файле; род. 28 декабря 1969, Хельсинки) — финско-американский программист, создатель ядра Linux.", "source": "search", "trace": "667", "secs": 0.7689587379572913, "day": 0, "n": 2, "utterance": "кто такой Линус Торвальдс?"}
|
||||||
|
{"reply": "нашла вот это: Фотосинтез — Фотосинтез\nСхематическое изображение процесса фотосинтеза, происходящего в растениях\nУпрощённое уравнение фотосинтеза, происходящего в растениях\nУпрощённая схема фотосинтеза\nФотоси́нтез (от др.-греч. φῶς — « свет » и σύνθεσις — «соединённый», «складной», «связывание», « синтез ») — сл…", "source": "kiwix", "trace": "668", "secs": 0.575586927938275, "day": 0, "n": 3, "utterance": "что такое фотосинтез"}
|
||||||
|
{"reply": "вот что я нашла: Война и мир — Война и мир\nВойна и мир\nВойна и миръ\nОбложка издания 1873 года\nЖанр\nроман-эпопея\nАвтор\nЛев Николаевич Толстой\nЯзык оригинала\nрусский, фрагменты на французском и немецком языках\nДата написания\n1863—1869, 1873\nДата первой публикации\n1865—1869\nПредыдущее\nКазаки\nСледующее\nАнна Каренина\nТек…", "source": "kiwix", "trace": "669", "secs": 12.44599153404124, "day": 0, "n": 4, "utterance": "кто написал Войну и мир"}
|
||||||
Reference in New Issue
Block a user