From 399bdea220f91c684ddbd5359ddf30a6d8aaee97 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Tue, 25 Aug 2026 04:06:45 +0300 Subject: [PATCH] Keep conversational replies semantically grounded --- cmd/core-runtime/main.go | 7 ++++--- modules/interaction/domain/semantics.go | 13 +++++++++++++ modules/interaction/domain/semantics_test.go | 10 ++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cmd/core-runtime/main.go b/cmd/core-runtime/main.go index 0343a58..b363746 100644 --- a/cmd/core-runtime/main.go +++ b/cmd/core-runtime/main.go @@ -629,7 +629,7 @@ func main() { activeFocusName = previousFocus.ProjectDisplayName activeFocusProvenance = previousFocus.Provenance } - planningReply := identityReply + planningReply := semantic.Reply workPlanned := false needsCognition := semantic.NeedsCognition projectID := "" @@ -802,12 +802,13 @@ func main() { ConfidenceMillis: semantic.Interpretation.ConfidenceMillis, }) cancelReview() + stableSemanticReply := identityReply != "" || semantic.Interpretation.SpeechAct == interaction.SpeechActConversational || semantic.Interpretation.SpeechAct == interaction.SpeechActConfirmation || semantic.Interpretation.SpeechAct == interaction.SpeechActRejection if reviewErr != nil { log.Printf("cognitive response review unavailable: %v", reviewErr) - } else if identityReply == "" { + } else if !stableSemanticReply { planningReply = reviewedReply } else if reviewedReply != planningReply { - log.Printf("cognitive response review proposed identity rewrite; preserving semantic identity reply") + log.Printf("cognitive response review proposed stable semantic rewrite; preserving semantic reply") } } response := interaction.ChatMessageResponse{ diff --git a/modules/interaction/domain/semantics.go b/modules/interaction/domain/semantics.go index ef23fce..00ad1c6 100644 --- a/modules/interaction/domain/semantics.go +++ b/modules/interaction/domain/semantics.go @@ -329,6 +329,19 @@ func semanticReply(message string, result SemanticResult, previousFocus *situati return "Исправление записано отдельно, и я вижу здесь ещё новую рабочую просьбу; история сохранена без переписывания." } return "Исправление записано новым сообщением; прежняя история сохранена." + case interaction.SpeechActConversational: + lower := strings.ToLower(message) + if strings.Contains(lower, "спасибо") || strings.Contains(lower, "благодар") { + return "Пожалуйста." + } + if strings.Contains(lower, "привет") || strings.Contains(lower, "здравств") { + return "Здравствуйте." + } + return "Я рядом." + case interaction.SpeechActConfirmation: + return "Хорошо." + case interaction.SpeechActRejection: + return "Понял." case interaction.SpeechActQuestion: lower := strings.ToLower(message) if previousFocus != nil && (strings.Contains(lower, "сейчас") || strings.Contains(lower, "что мы") || strings.Contains(lower, "делаем")) { diff --git a/modules/interaction/domain/semantics_test.go b/modules/interaction/domain/semantics_test.go index 9956369..fad992b 100644 --- a/modules/interaction/domain/semantics_test.go +++ b/modules/interaction/domain/semantics_test.go @@ -43,6 +43,16 @@ func TestInterpretUtteranceNamingPreferenceIsAccepted(t *testing.T) { } } +func TestInterpretUtteranceThanksHasNaturalConversationalReply(t *testing.T) { + result := interpretForTest(t, "спасибо", nil, "") + if result.Interpretation.SpeechAct != interaction.SpeechActConversational { + t.Fatalf("expected conversational act, got %s", result.Interpretation.SpeechAct) + } + if result.Reply != "Пожалуйста." { + t.Fatalf("unexpected thanks reply: %q", result.Reply) + } +} + func TestInterpretUtteranceDirectiveRecordsOutcomeWithoutExecution(t *testing.T) { result := interpretForTest(t, "Поменяй базу на УПО1.", activeFocus("upo", "УПО"), "") if result.Interpretation.SpeechAct != interaction.SpeechActDirective { -- 2.54.0