Route questions to query, not fact
The router prompt tested "reports current state -> fact" before "wants information -> query", so a question naming a fact key was written as a fact. Query now comes first, plus an explicit question test. Reviewers: the prompt block in llmrouter.go, and the note about the training-side copy of the prompt that needs the same edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
This commit is contained in:
@@ -34,6 +34,14 @@ string ::= "\"" ([^"\\] | "\\" .)* "\""
|
||||
ws ::= [ \t\n]*
|
||||
`
|
||||
|
||||
// routeSystem — the router prompt. Changed 31-07-2026: the query test now sits
|
||||
// above the fact test and there is an explicit question test. Before that, a
|
||||
// question naming a fact key ("сколько воды я выпил с утра") matched the fact
|
||||
// rule first and was stored as an assertion — 15 of 76 fixture cases.
|
||||
//
|
||||
// The training workspace keeps its own copy of this prompt for relabelling, and
|
||||
// `llm/check_prompt_parity.py` there compares the two. That copy is in another
|
||||
// repo and was not touched, so parity will fail until it gets the same edit.
|
||||
const routeSystem = `Классифицируй ровно одно сообщение пользователя. Верни ОДИН JSON-массив действий.
|
||||
|
||||
Ровно одно намерение: fact, reminder, note, query, act, chat, system.
|
||||
@@ -41,22 +49,26 @@ const routeSystem = `Классифицируй ровно одно сообще
|
||||
Классифицируй по цели пользователя. Порядок решения:
|
||||
1. Хочет напоминание в будущем → reminder
|
||||
2. Явно просит сохранить информацию → note
|
||||
3. Сообщает или обновляет текущее состояние/событие → fact
|
||||
4. Хочет получить информацию → query
|
||||
5. Просит выполнить работу → act
|
||||
6. Про ассистента, настройки или память → system
|
||||
7. Иначе → chat
|
||||
3. Задаёт вопрос: есть вопросительное слово (сколько, что, какой, когда, где, кто, почему, как) или знак «?» → query
|
||||
4. Хочет получить информацию, в том числе о своих же данных → query
|
||||
5. Утверждает: сообщает или обновляет текущее состояние/событие → fact
|
||||
6. Просит выполнить работу → act
|
||||
7. Про ассистента, настройки или память → system
|
||||
8. Иначе → chat
|
||||
|
||||
Различия:
|
||||
- note — сохранить информацию, без напоминания. text = суть.
|
||||
- reminder — уведомить позже. text = что напомнить.
|
||||
- fact — неявное обновление: пользователь сообщает, что что-то в мире изменилось (текущее/изменённое состояние, случившееся событие). key/value.
|
||||
- query против fact — решает форма реплики, а не тема. Вопрос о состоянии — это query, даже если названо то же самое, что бывает в fact. Только утверждение — это fact.
|
||||
|
||||
Примеры:
|
||||
"запиши пароль" → {"intent":"note","text":"пароль"}
|
||||
"напомни купить молоко" → {"intent":"reminder","text":"купить молоко"}
|
||||
"запиши купить молоко" → {"intent":"note","text":"купить молоко"}
|
||||
"я выпил воду" → {"intent":"fact","key":"water","value":"выпил"}
|
||||
"сколько воды я выпил с утра" → {"intent":"query","text":"сколько воды я выпил с утра"}
|
||||
"сколько раз я ел вчера?" → {"intent":"query","text":"сколько раз я ел вчера"}
|
||||
"мой любимый фильм — Интерстеллар" → {"intent":"note","text":"любимый фильм — Интерстеллар"}
|
||||
"что такое docker?" → {"intent":"query","text":"что такое docker"}
|
||||
"напиши письмо" → {"intent":"act","verb":"написать письмо"}
|
||||
|
||||
@@ -3,16 +3,36 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kami/maven/internal/llm"
|
||||
)
|
||||
|
||||
type mockLLM struct{ out string; err error }
|
||||
type mockLLM struct {
|
||||
out string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m mockLLM) Complete(_ context.Context, _ llm.Req) (string, error) { return m.out, m.err }
|
||||
|
||||
// A question naming a fact key used to be stored as a fact because the fact rule
|
||||
// was tested first. Keep the query rule above it.
|
||||
func TestRoutePromptTestsQueryBeforeFact(t *testing.T) {
|
||||
query := strings.Index(routeSystem, "→ query")
|
||||
fact := strings.Index(routeSystem, "состояние/событие → fact")
|
||||
if query < 0 || fact < 0 {
|
||||
t.Fatalf("prompt lost a rule: query=%d fact=%d", query, fact)
|
||||
}
|
||||
if query > fact {
|
||||
t.Fatal("query rule must come before the fact rule")
|
||||
}
|
||||
if !strings.Contains(routeSystem, "Задаёт вопрос") {
|
||||
t.Fatal("prompt lost the explicit question test")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLLMRouterFactMapping(t *testing.T) {
|
||||
lr := NewLLMRouter(mockLLM{out: `{"intent":"fact","key":"water","value":"выпил"}`})
|
||||
d, ok, err := lr.Route(context.Background(), "я выпил воду", time.Now())
|
||||
|
||||
Reference in New Issue
Block a user