Apply learned source aliases to research #378
@@ -792,7 +792,9 @@ func main() {
|
||||
replies = append(replies, "По запросу «"+part+"»: "+clarification)
|
||||
continue
|
||||
}
|
||||
searchQuery := interactiondomain.TravelResearchQuery(part, conversationHistory, now)
|
||||
learnedLexicon := interactiondomain.TaskLexiconFromHistory(conversationHistory)
|
||||
searchQuery := interactiondomain.SearchQueryWithLexicon(part, learnedLexicon)
|
||||
searchQuery = interactiondomain.TravelResearchQuery(searchQuery, conversationHistory, now)
|
||||
researchCtx, cancelResearch := context.WithTimeout(r.Context(), 8*time.Second)
|
||||
result, researchErr := researchClient.SearchWeb(researchCtx, epistemics.WebSearchRequest{
|
||||
Plan: epistemics.ResearchPlan{ID: epistemics.ResearchPlanID(fmt.Sprintf("plan:%s:%d", planID, index)), Question: searchQuery, SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"},
|
||||
|
||||
@@ -204,6 +204,14 @@ func TestDecomposeUsesRememberedAliasesAcrossWordings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskLexiconFromHistoryFeedsSearchQueryWithoutCoreAlias(t *testing.T) {
|
||||
history := []interaction.HumanUtterance{{Content: "запомни: WB — это Wildberries, источник для поиска"}}
|
||||
lexicon := TaskLexiconFromHistory(history)
|
||||
if got := SearchQueryWithLexicon("найди на WB бренд marka-wear", lexicon); got != "найди на WB бренд marka-wear wildberries" {
|
||||
t.Fatalf("learned source was not applied to search: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecomposeCreativeRequestDoesNotInventResearch(t *testing.T) {
|
||||
lexicon := TaskLexicon{ObjectAliases: []TaskLexiconEntry{{Alias: "медвед", Value: "IMAGE"}}}
|
||||
got := DecomposeTaskWithLexicon("нарисуй бурого медведя", lexicon)
|
||||
|
||||
@@ -2,6 +2,8 @@ package domain
|
||||
|
||||
import "strings"
|
||||
|
||||
import "git.cin.su/m/Assistant/contracts/interaction"
|
||||
|
||||
// TaskDecomposition is the small, typed intermediate representation used
|
||||
// before a capability is called. It separates the user's goal from the
|
||||
// object, source and target instead of treating the whole sentence as a
|
||||
@@ -38,6 +40,51 @@ func DecomposeTask(message string) TaskDecomposition {
|
||||
return DecomposeTaskWithLexicon(message, TaskLexicon{})
|
||||
}
|
||||
|
||||
// TaskLexiconFromHistory rebuilds learned search-source aliases from ordinary
|
||||
// human utterances. The fact remains user-owned conversational memory; core
|
||||
// code never embeds mutable marketplace or provider names.
|
||||
//
|
||||
// A source fact has the deliberately explicit form:
|
||||
// "запомни: <alias> — это <source>, источник для поиска".
|
||||
func TaskLexiconFromHistory(history []interaction.HumanUtterance) TaskLexicon {
|
||||
lexicon := TaskLexicon{}
|
||||
for _, utterance := range history {
|
||||
message := strings.TrimSpace(utterance.Content)
|
||||
lower := strings.ToLower(message)
|
||||
if !strings.Contains(lower, "источник") || !strings.Contains(lower, "поиск") || !strings.Contains(lower, "это") {
|
||||
continue
|
||||
}
|
||||
before, after, found := strings.Cut(lower, "это")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
alias := strings.TrimSpace(strings.TrimPrefix(before, "запомни:"))
|
||||
alias = strings.Trim(alias, " \t—-:;,")
|
||||
value := strings.TrimSpace(after)
|
||||
if index := strings.IndexAny(value, ",;."); index >= 0 {
|
||||
value = strings.TrimSpace(value[:index])
|
||||
}
|
||||
if len([]rune(alias)) < 2 || len([]rune(value)) < 2 {
|
||||
continue
|
||||
}
|
||||
lexicon.SourceAliases = append(lexicon.SourceAliases, TaskLexiconEntry{Alias: alias, Value: value})
|
||||
}
|
||||
return lexicon
|
||||
}
|
||||
|
||||
// SearchQueryWithLexicon keeps a learned source in the external query without
|
||||
// turning its alias into core vocabulary.
|
||||
func SearchQueryWithLexicon(message string, lexicon TaskLexicon) string {
|
||||
decomposition := DecomposeTaskWithLexicon(message, lexicon)
|
||||
if decomposition.Intent == TaskIntentSearch && decomposition.Source != "" {
|
||||
if decomposition.Target != "" {
|
||||
return decomposition.Target + " " + decomposition.Source
|
||||
}
|
||||
return strings.TrimSpace(message) + " " + decomposition.Source
|
||||
}
|
||||
return strings.TrimSpace(message)
|
||||
}
|
||||
|
||||
// DecomposeTaskWithLexicon performs a cheap semantic pass. The lexicon is
|
||||
// intentionally an input: a remembered alias may be revalidated or replaced
|
||||
// without changing the parser or shipping a new hard-coded branch.
|
||||
|
||||
Reference in New Issue
Block a user