Compare commits

...
Author SHA1 Message Date
m ccf790d130 DEV-000066 natural response target resolution
CI / test (push) Successful in 17s
CI / test (pull_request) Successful in 16s
2026-08-17 17:11:10 +03:00
m 54c2cc27a3 Promote DEV-000066 human response continuation semantics
CI / test (push) Successful in 15s
DEV-000066 Human Response + Continuation Semantics V1
2026-08-17 14:04:11 +00:00
2 changed files with 37 additions and 0 deletions
@@ -50,6 +50,12 @@ func InterpretHumanResponse(request contract.HumanResponseRequest, owner identit
response.Clarification = "Я не вижу активного ожидания, к которому это относится."
return contract.HumanResponseResult{Response: response, Handled: true, Reply: response.Clarification}, nil
}
if len(candidates) > 1 {
matched := matchingCandidates(message, candidates)
if len(matched) == 1 {
candidates = matched
}
}
if len(candidates) > 1 {
response.ResolutionState = contract.ResponseAmbiguous
response.Outcome = contract.ResponseOutcomeClarificationNeeded
@@ -153,6 +159,30 @@ func candidateSummary(candidates []ResponseCandidate) string {
return strings.Join(parts, "; ")
}
func matchingCandidates(message string, candidates []ResponseCandidate) []ResponseCandidate {
lower := strings.ToLower(message)
var matched []ResponseCandidate
for _, candidate := range candidates {
if summaryMatches(lower, candidate.Attention.Summary) {
matched = append(matched, candidate)
}
}
return matched
}
func summaryMatches(lowerMessage string, summary string) bool {
for _, token := range strings.Fields(strings.ToLower(summary)) {
token = strings.Trim(token, ".,!?:;()[]«»\"'")
if len([]rune(token)) < 4 {
continue
}
if strings.Contains(lowerMessage, token) {
return true
}
}
return false
}
func humanResponseTime(value time.Time) string {
if value.IsZero() {
return "позже"
@@ -41,6 +41,13 @@ func TestInterpretHumanResponseAmbiguousTargetAsksClarificationWithoutResolvedMu
}
}
func TestInterpretHumanResponseResolvesAmbiguousTargetByNaturalSummaryReference(t *testing.T) {
result := interpretResponseForTest(t, "Готово, проверить базу сделано", []ResponseCandidate{responseCandidate("a1", "p1", "позвонить врачу"), responseCandidate("a2", "p2", "проверить базу")})
if !result.Handled || result.Response.ResolutionState != continuity.ResponseResolved || result.Response.TargetAttentionID != "a2" {
t.Fatalf("expected natural summary target resolution, got %#v", result)
}
}
func TestInterpretHumanResponseNoTargetAsksClarification(t *testing.T) {
result := interpretResponseForTest(t, "Готово", nil)
if !result.Handled || result.Response.ResolutionState != continuity.ResponseNoTarget || result.Response.Outcome != continuity.ResponseOutcomeClarificationNeeded {