Route creative chat by meaning #339

Merged
m merged 1 commits from codex/creative-chat-routing into main 2026-08-25 08:32:57 +00:00
5 changed files with 138 additions and 3 deletions
+20 -1
View File
@@ -572,6 +572,20 @@ func main() {
semantic.NeedsCognition = false
semantic.Interpretation.NeedsCognition = false
}
creativeRequest := interactiondomain.IsCreativeRequest(effectiveTaskMessage)
creativeReply := interactiondomain.CreativeDeterministicReply(effectiveTaskMessage)
if creativeRequest {
semantic.CapabilityHints = interactiondomain.RemoveCapabilityHint(semantic.CapabilityHints, string(capability.CapabilityKindWebResearch))
if creativeReply != "" {
semantic.Reply = creativeReply
semantic.NeedsCognition = false
semantic.Interpretation.NeedsCognition = false
} else {
semantic.NeedsCognition = true
semantic.Interpretation.NeedsCognition = true
semantic.CapabilityHints = append(semantic.CapabilityHints, string(capability.CapabilityKindCognitiveReasoning))
}
}
locationReply := interactiondomain.LocationContextReply(utterance.Content)
if locationReply != "" {
semantic.Reply = locationReply
@@ -868,7 +882,7 @@ func main() {
ConfidenceMillis: semantic.Interpretation.ConfidenceMillis,
})
cancelReview()
stableSemanticReply := identityReply != "" || semantic.InformationRequestKind != "" || locationReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection
stableSemanticReply := identityReply != "" || creativeReply != "" || semantic.InformationRequestKind != "" || locationReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection
if reviewErr != nil {
log.Printf("cognitive response review unavailable: %v", reviewErr)
} else if !stableSemanticReply && (!researchBacked || reviewPreservesResearchEvidence(reviewedReply, planningReply)) {
@@ -890,6 +904,11 @@ func main() {
needsCognition = false
planningReply = identityReply
}
if creativeReply != "" {
semantic.Reply = creativeReply
needsCognition = false
planningReply = creativeReply
}
if strings.TrimSpace(planningReply) != "" {
semantic.Reply = planningReply
}
+2 -2
View File
@@ -1,5 +1,5 @@
{
"task_file": "dev/tasks/DEV-000293-site-browser-capability.json",
"task_id": "DEV-000293",
"task_file": "dev/tasks/DEV-000294-creative-chat-routing.json",
"task_id": "DEV-000294",
"task_revision": 1
}
@@ -0,0 +1,42 @@
{
"task_id": "DEV-000294",
"task_revision": 1,
"architecture_revision": "ADR-0019",
"base_repo_revision": "origin/main",
"risk_tier": "E",
"intent": "Route creative requests by meaning: render simple geometric sketches deterministically and send rich creative requests to cognition without web research or truth-evidence fallback.",
"requirement_ids": ["creative conversation", "no irrelevant web research", "short deterministic drawing"],
"allowed_change_scope": {
"modules": ["interaction", "core-runtime"],
"paths": [
"cmd/core-runtime/**",
"modules/interaction/domain/**",
"dev/ACTIVE_TASK.json",
"dev/tasks/DEV-000294-creative-chat-routing.json"
]
},
"forbidden_change_scope": {
"paths": ["architecture/**", "security/**", "database/**", "deploy/docker-compose.prod.yml", "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": ["existing interaction semantic candidate", "existing cognitive gateway", "existing web research route"],
"may_change": ["creative capability hints and core route selection"]
},
"semantic_owner": "interaction owns meaning classification; core-runtime owns route orchestration",
"canonical_state": ["existing utterance and candidate history only"],
"state_transitions": ["creative request -> deterministic sketch or cognitive conversation route"],
"side_effects_and_privileges": ["no new privilege or external mutation"],
"persistence_and_migrations": ["no migration"],
"failure_retry_restart": ["creative cognition failure reports unavailable cognition; never falls back to web research"],
"assumptions": ["simple circle rendering is a text-safe projection; richer images remain a separate capability"],
"acceptance_scenarios": ["нарисуй круг returns a circle and no source links", "расскажи сказку о колобке uses cognition and no web route", "ordinary weather and price requests keep web research", "cognitive review cannot replace deterministic circle"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
+35
View File
@@ -495,6 +495,9 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
if sourceDependentQuestion(primary, secondary, tokens) {
add("WEB_RESEARCH")
}
if isCreativeRequestTokens(tokens) {
add("COGNITIVE_REASONING")
}
if containsToken(tokens, "напомни") || containsToken(tokens, "напомин") {
add("SCHEDULE_REMINDER")
}
@@ -536,6 +539,14 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
return values
}
func isCreativeRequestTokens(tokens []string) bool {
if containsStemInTokens(tokens, "нарис", "рису", "изобраз") {
return true
}
return containsStemInTokens(tokens, "расска", "раска", "сочин", "придум") &&
containsStemInTokens(tokens, "сказк", "истор", "стих", "шутк", "песн", "басн")
}
// 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
@@ -639,6 +650,24 @@ func LocationContextReply(message string) string {
return ""
}
// IsCreativeRequest recognizes generation requests rather than retrieval of
// externally verifiable facts. The cognitive route remains responsible for
// the requested style and content.
func IsCreativeRequest(message string) bool {
tokens := semanticTokens(message)
return isCreativeRequestTokens(tokens)
}
// CreativeDeterministicReply covers simple geometric sketches that do not
// need a model or external source. Rich creative content remains cognitive.
func CreativeDeterministicReply(message string) string {
tokens := semanticTokens(message)
if containsStemInTokens(tokens, "нарис", "рису", "изобраз") && containsStemInTokens(tokens, "круг", "окруж") {
return "◯"
}
return ""
}
func boundedConfidence(primaryScore, secondaryScore int, scores map[interaction.SpeechAct]int) int64 {
total := 0
for _, score := range scores {
@@ -818,3 +847,9 @@ func removeCapabilityHint(values []string, denied string) []string {
}
return result
}
// RemoveCapabilityHint lets orchestration boundaries suppress a route that
// contradicts an already understood semantic class.
func RemoveCapabilityHint(values []string, denied string) []string {
return removeCapabilityHint(values, denied)
}
+39
View File
@@ -215,6 +215,45 @@ func TestMemoryInventoryRequestIsSemantic(t *testing.T) {
}
}
func TestCreativeRequestsDoNotRouteToWebResearch(t *testing.T) {
for _, message := range []string{"нарисуй круг", "расскажи сказку о колобке"} {
candidate := BuildDeterministicCandidate(message, "", time.Now().UnixMilli())
t.Logf("%q candidate=%+v", message, candidate)
for _, hint := range candidate.CapabilityHints {
if hint == "WEB_RESEARCH" {
t.Fatalf("creative request %q unexpectedly selected web research: %+v", message, candidate)
}
}
}
}
func TestInterpretCreativeRequestDoesNotRequireExternalEvidence(t *testing.T) {
utterance := interaction.HumanUtterance{Content: "нарисуй круг"}
result, err := InterpretUtterance(utterance, nil, nil, time.Now())
if err != nil {
t.Fatal(err)
}
t.Logf("semantic result=%+v", result)
if result.Interpretation.SpeechAct == interaction.SpeechActQuestion || containsString(result.CapabilityHints, "WEB_RESEARCH") {
t.Fatalf("creative request was routed as external research: %+v", result)
}
if got := CreativeDeterministicReply("нарисуй круг"); got != "◯" {
t.Fatalf("circle reply = %q", got)
}
if !IsCreativeRequest("расскажи сказку о колобке") {
t.Fatal("story request was not recognized as creative")
}
}
func containsString(values []string, expected string) bool {
for _, value := range values {
if value == expected {
return true
}
}
return false
}
func TestLocationContextReply(t *testing.T) {
if got := LocationContextReply("я сейчас в Москве"); got != "Запомнил: сейчас вы в Москве." {
t.Fatalf("unexpected location reply %q", got)