From d342728b46352e2178d23fc73380e2c2c9adc128 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Tue, 25 Aug 2026 18:12:13 +0300 Subject: [PATCH] Improve nearest flight research answers --- adapters/research/search_connector.go | 3 +++ adapters/research/search_connector_test.go | 6 ++++++ cmd/core-runtime/main.go | 12 ++++++++++++ cmd/core-runtime/main_test.go | 12 ++++++++++++ 4 files changed, 33 insertions(+) diff --git a/adapters/research/search_connector.go b/adapters/research/search_connector.go index d27a73f..811b524 100644 --- a/adapters/research/search_connector.go +++ b/adapters/research/search_connector.go @@ -211,6 +211,9 @@ func enrichedSearchQuery(query string) string { if (strings.Contains(lower, "цен") || strings.Contains(lower, "стоим")) && !strings.Contains(lower, "куп") { return trimmed + " цена купить сегодня" } + if (strings.Contains(lower, "рейс") || strings.Contains(lower, "самолет") || strings.Contains(lower, "самолёт") || strings.Contains(lower, "вылет")) && strings.Contains(lower, "ближ") { + return trimmed + " расписание вылеты время" + } return trimmed } diff --git a/adapters/research/search_connector_test.go b/adapters/research/search_connector_test.go index 3011b89..0f176b1 100644 --- a/adapters/research/search_connector_test.go +++ b/adapters/research/search_connector_test.go @@ -55,3 +55,9 @@ func TestEnrichedSearchQueryAddsPriceSignalsOnlyToPriceRequests(t *testing.T) { t.Fatalf("non-price query = %q", got) } } + +func TestEnrichedSearchQueryAddsFlightSignalsToNearestFlightRequests(t *testing.T) { + if got := enrichedSearchQuery("ближайший рейс Москва Казань сегодня 25.08.2026"); got != "ближайший рейс Москва Казань сегодня 25.08.2026 расписание вылеты время" { + t.Fatalf("flight query = %q", got) + } +} diff --git a/cmd/core-runtime/main.go b/cmd/core-runtime/main.go index c72df82..f83f552 100644 --- a/cmd/core-runtime/main.go +++ b/cmd/core-runtime/main.go @@ -1722,6 +1722,9 @@ func renderResearchReply(query string, result epistemics.ResearchResult, researc if len(lines) == 0 { return "Проверяемых данных пока не найдено. Могу повторить поиск или уточнить, что именно проверить.", false } + if nearestFlightQuery(query) && !containsFlightTimeEvidence(strings.Join(lines, " ")) { + return "Нашёл расписание по маршруту, но точное время ближайшего вылета в доступных фрагментах не указано. Откройте источник и выберите ближайший рейс:\n\n" + strings.Join(lines, "\n\n"), true + } reply := "Нашёл проверяемые данные:\n\n" + strings.Join(lines, "\n\n") lowerQuery := strings.ToLower(query) if (strings.Contains(lowerQuery, "цен") || strings.Contains(lowerQuery, "стоим")) && !containsPriceEvidence(reply) { @@ -1730,6 +1733,15 @@ func renderResearchReply(query string, result epistemics.ResearchResult, researc return reply, true } +func nearestFlightQuery(query string) bool { + lower := strings.ToLower(query) + return strings.Contains(lower, "ближ") && (strings.Contains(lower, "рейс") || strings.Contains(lower, "самолет") || strings.Contains(lower, "самолёт") || strings.Contains(lower, "вылет")) +} + +func containsFlightTimeEvidence(value string) bool { + return regexp.MustCompile(`(?m)\b(?:[01]?\d|2[0-3]):[0-5]\d\b`).MatchString(value) +} + func humanResearchExcerpt(query, raw string) string { text := strings.Join(strings.Fields(strings.TrimSpace(raw)), " ") if text == "" { diff --git a/cmd/core-runtime/main_test.go b/cmd/core-runtime/main_test.go index c329620..a9ac525 100644 --- a/cmd/core-runtime/main_test.go +++ b/cmd/core-runtime/main_test.go @@ -22,6 +22,18 @@ func TestHumanResearchExcerptRemovesSearchResultHeading(t *testing.T) { } } +func TestNearestFlightEvidenceDoesNotClaimMissingDepartureTime(t *testing.T) { + if !nearestFlightQuery("ближайший рейс Москва Казань сегодня") { + t.Fatal("nearest flight query was not recognized") + } + if containsFlightTimeEvidence("Казань составляет 1 ч 25 мин. График вылетов доступен онлайн") { + t.Fatal("duration was mistaken for a departure time") + } + if !containsFlightTimeEvidence("Ближайший вылет в 18:40") { + t.Fatal("departure time was not recognized") + } +} + func TestBearerTokenRequiresBearerScheme(t *testing.T) { request := httptest.NewRequest("GET", "/", nil) request.Header.Set("Authorization", "Basic value")