Preserve meaning when cognitive model is unavailable #287
@@ -186,7 +186,7 @@ func selectionObjectiveForMeaning(meaning cognitive.TaskMeaning, message string)
|
||||
}
|
||||
|
||||
func builtinAdvisory(request cognitive.GatewayAdvisoryRequest) cognitive.GatewayAdvisoryResponse {
|
||||
text := "Я — Домовой, ваш помощник. Помогаю разбираться в вопросах, искать информацию и выполнять задачи."
|
||||
text := "Проверю запрос доступным маршрутом и сообщу результат."
|
||||
switch request.Meaning.InformationRequestKind {
|
||||
case interaction.InformationRequestCognitiveModelInventory:
|
||||
text = "Проверю подключённые модели. Ключи и пароли не показываю."
|
||||
@@ -201,7 +201,9 @@ func builtinAdvisory(request cognitive.GatewayAdvisoryRequest) cognitive.Gateway
|
||||
case "REQUEST_TO_INVESTIGATE":
|
||||
text = "Понял задачу. Проверю доступный путь и сообщу результат."
|
||||
case "QUESTION":
|
||||
text = "Я — Домовой, ваш помощник. Помогаю разбираться в вопросах, искать информацию и выполнять задачи."
|
||||
text = "Проверю вопрос доступным источником и отвечу по существу."
|
||||
case "CONVERSATIONAL":
|
||||
text = "Я — Домовой, ваш помощник."
|
||||
}
|
||||
}
|
||||
return cognitive.GatewayAdvisoryResponse{Version: request.Version, RequestID: request.RequestID, AdvisoryText: text, FinishReason: cognitive.FinishCompleted}
|
||||
|
||||
@@ -196,7 +196,7 @@ func TestBuiltInAdvisoryIdentityIsNaturalAndDoesNotMentionResponseLength(t *test
|
||||
RequestID: "request-identity",
|
||||
Meaning: cognitive.TaskMeaning{SpeechAct: "QUESTION"},
|
||||
})
|
||||
if response.AdvisoryText != "Я — Домовой, ваш помощник. Помогаю разбираться в вопросах, искать информацию и выполнять задачи." {
|
||||
if response.AdvisoryText != "Проверю вопрос доступным источником и отвечу по существу." {
|
||||
t.Fatalf("unexpected identity response: %q", response.AdvisoryText)
|
||||
}
|
||||
if strings.Contains(response.AdvisoryText, "коротко") || strings.Contains(response.AdvisoryText, "подробност") {
|
||||
|
||||
@@ -214,7 +214,7 @@ func semanticScores(tokens []string, hasQuestionMark bool, explicitCorrection bo
|
||||
questionWords := map[string]struct{}{"кто": {}, "что": {}, "как": {}, "почему": {}, "зачем": {}, "где": {}, "когда": {}, "сколько": {}, "какой": {}, "какая": {}, "какие": {}, "чей": {}}
|
||||
conversationWords := []string{"привет", "здравствуйте", "спасибо", "ку", "здравствуй"}
|
||||
investigateStems := []string{"разбер", "проверь", "проанализ", "исслед", "выясн", "уточн", "подум", "понят", "посмотри", "попроб", "повтор", "снов"}
|
||||
directiveStems := []string{"сдел", "запуст", "созд", "настро", "добав", "измени", "помен", "использ", "удал", "напом", "подключ", "исправ", "обрати", "спрос", "посчитай", "рассчитай", "ответь"}
|
||||
directiveStems := []string{"сдел", "запуст", "созд", "настро", "добав", "измени", "помен", "использ", "удал", "напом", "подключ", "исправ", "обрати", "спрос", "найд", "посчитай", "рассчитай", "ответь"}
|
||||
explanatoryRequestStems := []string{"расска", "раска", "объяс", "опиш", "покаж", "подскаж", "сообщ"}
|
||||
preferenceStems := []string{"предпоч", "хочу", "лучше", "не используй"}
|
||||
hypothesisStems := []string{"думаю", "кажется", "гипотез", "похоже"}
|
||||
@@ -472,13 +472,56 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
// place, person or one-off question branch; when the cue is absent the
|
||||
// truth-first fallback remains in force.
|
||||
func sourceDependentQuestion(primary, secondary interaction.SpeechAct, tokens []string) bool {
|
||||
inquiry := primary == interaction.SpeechActQuestion || primary == interaction.SpeechActRequestToInvestigate || secondary == interaction.SpeechActQuestion || secondary == interaction.SpeechActRequestToInvestigate
|
||||
inquiry := primary == interaction.SpeechActQuestion || primary == interaction.SpeechActRequestToInvestigate || secondary == interaction.SpeechActQuestion || secondary == interaction.SpeechActRequestToInvestigate || (primary == interaction.SpeechActDirective && containsStemInTokens(tokens, "найд", "поищ", "подбер"))
|
||||
if !inquiry {
|
||||
return false
|
||||
}
|
||||
currentCue := containsStemInTokens(tokens, "сейчас", "сегодня", "текущ", "актуаль", "последн", "прямо")
|
||||
observedStateCue := containsStemInTokens(tokens, "погод", "температур", "осад", "ветер", "курс", "цен", "стоим", "новост", "распис", "событ", "статус", "доступен", "работает", "запущен", "срок")
|
||||
return currentCue || observedStateCue
|
||||
observedStateCue := containsStemInTokens(tokens, "погод", "температур", "осад", "ветер", "курс", "цен", "стоим", "новост", "распис", "событ", "статус", "доступен", "работает", "запущен", "срок") || approximateToken(tokens, "погод", 2)
|
||||
placeSearchCue := containsStemInTokens(tokens, "ресторан", "кафе", "отел", "магаз", "мест", "ближ", "адрес", "организац")
|
||||
return currentCue || observedStateCue || placeSearchCue
|
||||
}
|
||||
|
||||
// approximateToken catches ordinary keyboard slips without turning routing
|
||||
// into a list of complete user questions. It is only used for broad
|
||||
// source-dependent concepts such as weather.
|
||||
func approximateToken(tokens []string, target string, maxDistance int) bool {
|
||||
for _, token := range tokens {
|
||||
if editDistance([]rune(token), []rune(target)) <= maxDistance {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func editDistance(left, right []rune) int {
|
||||
previous := make([]int, len(right)+1)
|
||||
for index := range previous {
|
||||
previous[index] = index
|
||||
}
|
||||
for i, leftRune := range left {
|
||||
current := make([]int, len(right)+1)
|
||||
current[0] = i + 1
|
||||
for j, rightRune := range right {
|
||||
cost := 0
|
||||
if leftRune != rightRune {
|
||||
cost = 1
|
||||
}
|
||||
current[j+1] = minInt(current[j]+1, previous[j+1]+1, previous[j]+cost)
|
||||
}
|
||||
previous = current
|
||||
}
|
||||
return previous[len(right)]
|
||||
}
|
||||
|
||||
func minInt(values ...int) int {
|
||||
result := values[0]
|
||||
for _, value := range values[1:] {
|
||||
if value < result {
|
||||
result = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func informationRequestKind(message string, candidate interaction.SemanticCandidate) interaction.InformationRequestKind {
|
||||
|
||||
@@ -154,6 +154,21 @@ func TestSourceDependentQuestionRequestsWebResearchWithoutEntityBranch(t *testin
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceDependentQuestionToleratesTyposAndPlaceSearch(t *testing.T) {
|
||||
for _, message := range []string{"какая пугода в Сочи", "найди ближайший ресторан в Сочи"} {
|
||||
candidate := BuildDeterministicCandidate(message, "", time.Unix(10, 0).UnixMilli())
|
||||
seen := false
|
||||
for _, hint := range candidate.CapabilityHints {
|
||||
if hint == "WEB_RESEARCH" {
|
||||
seen = true
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
t.Fatalf("message=%q was not routed to WEB_RESEARCH: %+v", message, candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestToClarifyCurrentFactRequestsWebResearch(t *testing.T) {
|
||||
candidate := BuildDeterministicCandidate("уточни погоду в питере", "", time.Unix(10, 0).UnixMilli())
|
||||
for _, hint := range candidate.CapabilityHints {
|
||||
|
||||
Reference in New Issue
Block a user