DEV-000285 Route source-dependent questions to research #263
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"task_file": "dev/tasks/DEV-000284-live-generic-synthesis-evidence.json",
|
||||
"task_id": "DEV-000284",
|
||||
"task_file": "dev/tasks/DEV-000285-semantic-research-routing-fix.json",
|
||||
"task_id": "DEV-000285",
|
||||
"task_revision": 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000285",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "5c98bc3",
|
||||
"risk_tier": "E",
|
||||
"intent": "Route semantically source-dependent current-fact questions to the existing provider-neutral WEB_RESEARCH capability instead of returning an unverified fallback.",
|
||||
"requirement_ids": ["truth-first source routing", "no entity-specific keyword branches", "local deterministic fallback remains honest"],
|
||||
"allowed_change_scope": {"modules": ["interaction"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000285-semantic-research-routing-fix.json", "modules/interaction/domain/chat.go", "modules/interaction/domain/chat_test.go"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "cmd/**", "deploy/**", "LLM-local/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["interaction semantic candidate contract", "capability WEB_RESEARCH hint"], "may_change": []},
|
||||
"semantic_owner": "interaction",
|
||||
"canonical_state": ["no new canonical state; only semantic interpretation hints change"],
|
||||
"state_transitions": ["source-dependent question -> WEB_RESEARCH hint -> existing research runtime"],
|
||||
"side_effects_and_privileges": ["existing read-only research connector may be selected by downstream runtime"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["if research is unavailable, existing truthful unverified response remains"],
|
||||
"assumptions": ["lexical cues represent semantic classes such as currentness and observed external state, not named entities"],
|
||||
"acceptance_scenarios": ["weather-like current fact routes to WEB_RESEARCH without city-specific code", "ordinary model mention does not route to research", "non-research factual question remains honest when no source-dependent cue exists"],
|
||||
"required_tests": ["go test ./modules/interaction/domain", "go vet ./modules/interaction/domain", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production health smoke"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_MISSING_CAPABILITY"]
|
||||
}
|
||||
@@ -405,6 +405,9 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
if primary == interaction.SpeechActQuestion || primary == interaction.SpeechActRequestToInvestigate || secondary == interaction.SpeechActQuestion || secondary == interaction.SpeechActRequestToInvestigate {
|
||||
add("COGNITIVE_REASONING")
|
||||
}
|
||||
if sourceDependentQuestion(primary, secondary, tokens) {
|
||||
add("WEB_RESEARCH")
|
||||
}
|
||||
if containsToken(tokens, "напомни") || containsToken(tokens, "напомин") {
|
||||
add("SCHEDULE_REMINDER")
|
||||
}
|
||||
@@ -446,6 +449,20 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
return values
|
||||
}
|
||||
|
||||
// sourceDependentQuestion identifies semantic classes whose answer depends on
|
||||
// a current or externally observed source. It intentionally uses no named
|
||||
// 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
|
||||
if !inquiry {
|
||||
return false
|
||||
}
|
||||
currentCue := containsStemInTokens(tokens, "сейчас", "сегодня", "текущ", "актуаль", "последн", "прямо")
|
||||
observedStateCue := containsStemInTokens(tokens, "погод", "температур", "осад", "ветер", "курс", "цен", "стоим", "новост", "распис", "событ", "статус", "доступен", "работает", "запущен", "срок")
|
||||
return currentCue || observedStateCue
|
||||
}
|
||||
|
||||
func informationRequestKind(message string, candidate interaction.SemanticCandidate) interaction.InformationRequestKind {
|
||||
tokens := semanticTokens(message)
|
||||
if len(tokens) == 0 {
|
||||
|
||||
@@ -133,6 +133,25 @@ func TestInformationRequestsAreClassifiedOnceByInteraction(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceDependentQuestionRequestsWebResearchWithoutEntityBranch(t *testing.T) {
|
||||
candidate := BuildDeterministicCandidate("какая погода в Киеве?", "", time.Unix(10, 0).UnixMilli())
|
||||
seen := false
|
||||
for _, hint := range candidate.CapabilityHints {
|
||||
if hint == "WEB_RESEARCH" {
|
||||
seen = true
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
t.Fatalf("source-dependent question was not routed to WEB_RESEARCH: %+v", candidate)
|
||||
}
|
||||
ordinary := BuildDeterministicCandidate("что такое гравитация?", "", time.Unix(10, 0).UnixMilli())
|
||||
for _, hint := range ordinary.CapabilityHints {
|
||||
if hint == "WEB_RESEARCH" {
|
||||
t.Fatalf("ordinary conceptual question was incorrectly routed to WEB_RESEARCH: %+v", ordinary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeterministicCandidateKeepsFirstPersonCompletedWorkAsAssertion(t *testing.T) {
|
||||
candidate := BuildDeterministicCandidate("Я всё уже настроил.", "", time.Unix(10, 0).UnixMilli())
|
||||
if candidate.PrimarySpeechAct != interaction.SpeechActAssertion {
|
||||
|
||||
Reference in New Issue
Block a user