fix: route model inventory by semantic intent
This commit is contained in:
@@ -394,7 +394,7 @@ func main() {
|
||||
}
|
||||
needsCognition = false
|
||||
}
|
||||
if isModelInventoryQuestion(utterance.Content) {
|
||||
if isModelInventoryQuestion(semantic) {
|
||||
candidates, inventoryErr := capabilityRepository.LoadCognitiveCandidates(r.Context(), capability.CapabilityID("creator-advisory"), session.PrincipalID)
|
||||
planningReply = humanCognitiveModelInventory(candidates, inventoryErr)
|
||||
needsCognition = false
|
||||
@@ -1158,14 +1158,8 @@ func isCapabilityQuestion(message string) bool {
|
||||
return strings.Contains(lower, "что ты умеешь") || strings.Contains(lower, "что умеешь") || strings.Contains(lower, "какие возможности")
|
||||
}
|
||||
|
||||
func isModelInventoryQuestion(message string) bool {
|
||||
lower := strings.ToLower(strings.TrimSpace(message))
|
||||
if lower == "" {
|
||||
return false
|
||||
}
|
||||
asksAboutModels := strings.Contains(lower, "модел") || strings.Contains(lower, "ии") && (strings.Contains(lower, "внешн") || strings.Contains(lower, "настро") || strings.Contains(lower, "подключ"))
|
||||
asksForInventory := strings.Contains(lower, "какие") || strings.Contains(lower, "какая") || strings.Contains(lower, "список") || strings.Contains(lower, "доступ") || strings.Contains(lower, "настро")
|
||||
return asksAboutModels && asksForInventory
|
||||
func isModelInventoryQuestion(result interactiondomain.SemanticResult) bool {
|
||||
return result.InformationRequestKind == interactiondomain.InformationRequestCognitiveModelInventory
|
||||
}
|
||||
|
||||
func humanCognitiveModelInventory(candidates []capability.ResolvedCognitiveBinding, inventoryErr error) string {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain"
|
||||
)
|
||||
|
||||
func TestBearerTokenRequiresBearerScheme(t *testing.T) {
|
||||
@@ -51,9 +53,20 @@ func TestCognitiveDegradedReplyKeepsDomovoyAlive(t *testing.T) {
|
||||
|
||||
func TestModelInventoryQuestionUsesReadOnlyFastPath(t *testing.T) {
|
||||
for _, message := range []string{"какие модели доступны?", "напиши список настроек ИИ", "какие есть подключения ИИ"} {
|
||||
if !isModelInventoryQuestion(message) {
|
||||
t.Fatalf("message %q was not recognized as model inventory question", message)
|
||||
result, err := interactiondomain.InterpretUtterance(interaction.HumanUtterance{Content: message}, nil, nil, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("interpretation failed for %q: %v", message, err)
|
||||
}
|
||||
if !isModelInventoryQuestion(result) {
|
||||
t.Fatalf("semantic inventory request %q was not recognized: kind=%q act=%q hints=%v", message, result.InformationRequestKind, result.Interpretation.SpeechAct, result.CapabilityHints)
|
||||
}
|
||||
}
|
||||
ordinary, err := interactiondomain.InterpretUtterance(interaction.HumanUtterance{Content: "настрой локальную модель"}, nil, nil, time.Now())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if isModelInventoryQuestion(ordinary) {
|
||||
t.Fatal("configuration mutation must not use read-only model inventory path")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
"architecture_revision": "DEV-000073-baseline",
|
||||
"base_repo_revision": "2306c8d6b31fbf5ac0933ecc72b4f76bec7218f2",
|
||||
"risk_tier": "E",
|
||||
"intent": "Answer model and external-AI settings questions from the capability registry through a deterministic read-only chat fast path instead of treating them as unavailable cognition.",
|
||||
"intent": "Answer semantically identified model and external-AI inventory requests from the capability registry through a deterministic read-only chat fast path instead of treating them as unavailable cognition.",
|
||||
"requirement_ids": ["Model inventory is readable from chat", "No secret material is shown", "Inventory does not require model reasoning"],
|
||||
"allowed_change_scope": {
|
||||
"modules": ["core-runtime", "development-governance"],
|
||||
"paths": ["cmd/core-runtime/main.go", "cmd/core-runtime/main_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000092-cognitive-model-inventory-fast-path.json"]
|
||||
"modules": ["core-runtime", "interaction", "development-governance"],
|
||||
"paths": ["cmd/core-runtime/main.go", "cmd/core-runtime/main_test.go", "modules/interaction/domain/chat.go", "modules/interaction/domain/chat_test.go", "modules/interaction/domain/semantics.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000092-cognitive-model-inventory-fast-path.json"]
|
||||
},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**"]},
|
||||
"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},
|
||||
|
||||
@@ -194,6 +194,9 @@ func semanticScores(tokens []string, hasQuestionMark bool, explicitCorrection bo
|
||||
if hasQuestionMark {
|
||||
scores[interaction.SpeechActQuestion]++
|
||||
}
|
||||
if containsMeaningfulInterrogative(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 2
|
||||
}
|
||||
if explicitCorrection {
|
||||
scores[interaction.SpeechActCorrection] += 6
|
||||
}
|
||||
@@ -432,6 +435,9 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
if containsToken(tokens, "свободно") || containsToken(tokens, "гб") || containsToken(tokens, "модель") {
|
||||
add("VERIFY_FACT")
|
||||
}
|
||||
if containsStemInTokens(tokens, "модел", "ии") && containsStemInTokens(tokens, "настро", "список", "доступ", "подключ") {
|
||||
add("VERIFY_FACT")
|
||||
}
|
||||
values := make([]string, 0, len(seen))
|
||||
for kind := range seen {
|
||||
values = append(values, kind)
|
||||
@@ -439,6 +445,18 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
return values
|
||||
}
|
||||
|
||||
func informationRequestKind(message string) InformationRequestKind {
|
||||
tokens := semanticTokens(message)
|
||||
if len(tokens) == 0 || containsStem(tokens[0], "настро") || !containsStemInTokens(tokens, "модел", "ии") {
|
||||
return ""
|
||||
}
|
||||
inventoryCue := containsStemInTokens(tokens, "список", "доступ", "подключ") || (containsMeaningfulInterrogative(tokens) && containsStemInTokens(tokens, "настро"))
|
||||
if !inventoryCue {
|
||||
return ""
|
||||
}
|
||||
return InformationRequestCognitiveModelInventory
|
||||
}
|
||||
|
||||
func boundedConfidence(primaryScore, secondaryScore int, scores map[interaction.SpeechAct]int) int64 {
|
||||
total := 0
|
||||
for _, score := range scores {
|
||||
|
||||
@@ -11,16 +11,21 @@ import (
|
||||
)
|
||||
|
||||
type SemanticResult struct {
|
||||
Interpretation interaction.UtteranceInterpretation
|
||||
Candidates []interaction.SemanticCandidate
|
||||
Projects []interaction.ProjectReference
|
||||
Relations []interaction.InteractionRelation
|
||||
ActiveFocus *situation.ActiveProjectFocus
|
||||
Reply string
|
||||
NeedsCognition bool
|
||||
CapabilityHints []string
|
||||
Interpretation interaction.UtteranceInterpretation
|
||||
Candidates []interaction.SemanticCandidate
|
||||
Projects []interaction.ProjectReference
|
||||
Relations []interaction.InteractionRelation
|
||||
ActiveFocus *situation.ActiveProjectFocus
|
||||
Reply string
|
||||
NeedsCognition bool
|
||||
CapabilityHints []string
|
||||
InformationRequestKind InformationRequestKind
|
||||
}
|
||||
|
||||
type InformationRequestKind string
|
||||
|
||||
const InformationRequestCognitiveModelInventory InformationRequestKind = "COGNITIVE_MODEL_INVENTORY"
|
||||
|
||||
func InterpretUtterance(utterance interaction.HumanUtterance, projects []situation.ProjectDescriptor, previousFocus *situation.ActiveProjectFocus, now time.Time) (SemanticResult, error) {
|
||||
interpretationID, err := ids.New()
|
||||
if err != nil {
|
||||
@@ -46,9 +51,10 @@ func InterpretUtterance(utterance interaction.HumanUtterance, projects []situati
|
||||
SelectionReason: selectedCandidate.SelectionReason,
|
||||
RecordedAtUnixMillis: millis,
|
||||
},
|
||||
Candidates: []interaction.SemanticCandidate{deterministicCandidate},
|
||||
NeedsCognition: selectedCandidate.NeedsCognition,
|
||||
CapabilityHints: append([]string(nil), selectedCandidate.CapabilityHints...),
|
||||
Candidates: []interaction.SemanticCandidate{deterministicCandidate},
|
||||
NeedsCognition: selectedCandidate.NeedsCognition,
|
||||
CapabilityHints: append([]string(nil), selectedCandidate.CapabilityHints...),
|
||||
InformationRequestKind: informationRequestKind(utterance.Content),
|
||||
}
|
||||
explicitFocus := explicitProjectFocus(utterance.Content, projects)
|
||||
projectRefs, err := projectReferences(utterance, projects, explicitFocus, previousFocus, millis)
|
||||
|
||||
Reference in New Issue
Block a user