refactor: prioritize cognitive interpretation over deterministic rules
CI / test (push) Failing after 14s
CI / test (push) Failing after 14s
This commit is contained in:
@@ -175,7 +175,8 @@ func semanticScores(tokens []string, hasQuestionMark bool, explicitCorrection bo
|
||||
interaction.SpeechActConversational: 0,
|
||||
}
|
||||
if hasQuestionMark {
|
||||
scores[interaction.SpeechActQuestion] += 3
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
scores[interaction.SpeechActRequestToInvestigate]++
|
||||
}
|
||||
if explicitCorrection {
|
||||
scores[interaction.SpeechActCorrection] += 6
|
||||
@@ -192,14 +193,17 @@ func semanticScores(tokens []string, hasQuestionMark bool, explicitCorrection bo
|
||||
scores[interaction.SpeechActConfirmation] += 2
|
||||
}
|
||||
questionWords := map[string]struct{}{"кто": {}, "что": {}, "как": {}, "почему": {}, "зачем": {}, "где": {}, "когда": {}, "сколько": {}, "какой": {}, "какая": {}, "какие": {}, "чей": {}}
|
||||
conversationWords := []string{"привет", "здравствуйте", "спасибо", "ку", "здравствуй", "добрый", "день", "добрый день"}
|
||||
investigateStems := []string{"разбер", "проверь", "проанализ", "исслед", "выясн", "посмотри", "узнай", "расскаж", "объясн", "поясн"}
|
||||
directiveStems := []string{"сдел", "запуст", "созд", "настро", "добав", "измени", "помен", "поменя", "измен", "использ", "удал", "напом", "подключ", "исправ", "обрати", "поправ", "обнов", "ответь", "посмотри"}
|
||||
conversationWords := []string{"привет", "здравствуйте", "спасибо", "ку", "здравствуй", "добрый"}
|
||||
investigateStems := []string{"разбер", "проверь", "провери", "проанализ", "исслед", "выясн", "узнай"}
|
||||
directiveStems := []string{"сдел", "запуст", "созд", "настро", "добав", "измени", "помен", "поменя", "измен", "использ", "удал", "напом", "подключ", "исправ", "обрати", "поправ", "обнов", "ответь", "посмотри", "проверь"}
|
||||
preferenceStems := []string{"предпоч", "хочу", "лучше", "не используй"}
|
||||
hypothesisStems := []string{"думаю", "кажется", "гипотез", "похоже"}
|
||||
decisionStems := []string{"решил", "решение", "выбираю"}
|
||||
arithmeticMarker := false
|
||||
for index, token := range tokens {
|
||||
if firstPersonPastReport(tokens, index) && containsStem(token, directiveStems...) {
|
||||
continue
|
||||
}
|
||||
if _, ok := questionWords[token]; ok && !(token == "что" && index+1 < len(tokens) && tokens[index+1] == "нибудь") {
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
}
|
||||
@@ -226,37 +230,24 @@ func semanticScores(tokens []string, hasQuestionMark bool, explicitCorrection bo
|
||||
arithmeticMarker = true
|
||||
}
|
||||
}
|
||||
if len(tokens) >= 2 && len(tokens[0]) >= 4 && (strings.HasSuffix(tokens[0], "й") || strings.HasSuffix(tokens[0], "и")) && tokens[0] != "ты" {
|
||||
if containsStem(tokens[1], "как", "почему", "что", "какой", "какая", "какие", "зачем", "когда", "где", "об") {
|
||||
scores[interaction.SpeechActQuestion] += 3
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 3
|
||||
}
|
||||
}
|
||||
if len(tokens) >= 2 && tokens[0] == "про" {
|
||||
if containsStemInTokens(tokens[1:], "расска", "объясн", "поясн", "разбер", "исслед", "проверь", "выясн", "посмотри") || containsStemInTokens(tokens[1:], "как", "почему", "что", "какой", "какая", "какие", "зачем", "когда", "где", "об") {
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 3
|
||||
}
|
||||
}
|
||||
if len(tokens) >= 2 && tokens[0] == "я" && containsStemInTokens(tokens[1:], "настро", "сдела", "исправ", "удал", "созда", "перезапуст", "поменя", "добави") {
|
||||
scores[interaction.SpeechActAssertion] += 5
|
||||
if looksLikeInstructionWithoutPunctuationAfterLead(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 2
|
||||
}
|
||||
if arithmeticMarker {
|
||||
scores[interaction.SpeechActQuestion] += 2
|
||||
}
|
||||
if len(tokens) > 0 && strings.HasSuffix(tokens[0], "ешь") {
|
||||
scores[interaction.SpeechActQuestion] += 1
|
||||
}
|
||||
if len(tokens) > 0 && startsWithModalRequest(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 2
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 2
|
||||
}
|
||||
if len(tokens) > 0 && containsStem(tokens[0], "разбер", "проверь", "проанализ", "исслед", "выясн") {
|
||||
if containsMeaningfulInterrogative(tokens) {
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 3
|
||||
} else if tokens[0] == "посмотри" {
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 1
|
||||
}
|
||||
if looksLikeInformationRequest(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 2
|
||||
}
|
||||
if !hasQuestionMark && containsInterrogative(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 4
|
||||
scores[interaction.SpeechActRequestToInvestigate] += 2
|
||||
}
|
||||
if containsModelReference(tokens) {
|
||||
scores[interaction.SpeechActQuestion] += 2
|
||||
@@ -353,6 +344,12 @@ func needsCognitionForCandidate(primary, secondary interaction.SpeechAct, tokens
|
||||
if primary == interaction.SpeechActConversational && secondary == interaction.SpeechActDirective {
|
||||
return true
|
||||
}
|
||||
if primary == interaction.SpeechActQuestion && containsMeaningfulInterrogative(tokens) {
|
||||
return true
|
||||
}
|
||||
if isAssistantStatusProbe(primary, secondary, tokens) {
|
||||
return true
|
||||
}
|
||||
if primary == interaction.SpeechActAssertion && containsModelReference(tokens) && containsStemInTokens(tokens, "работает", "падает", "растет", "растёет", "свободно", "виснет") {
|
||||
return true
|
||||
}
|
||||
@@ -362,9 +359,6 @@ func needsCognitionForCandidate(primary, secondary interaction.SpeechAct, tokens
|
||||
if secondary == interaction.SpeechActQuestion || secondary == interaction.SpeechActRequestToInvestigate {
|
||||
return true
|
||||
}
|
||||
if isAssistantStatusProbe(primary, secondary, tokens) {
|
||||
return true
|
||||
}
|
||||
if primary == interaction.SpeechActDirective && (containsInterrogative(tokens) || containsModelReference(tokens) || containsStemInTokens(tokens, "обрати", "посмотри", "разбер", "исправ", "обнов", "добав")) {
|
||||
return true
|
||||
}
|
||||
@@ -405,9 +399,23 @@ func capabilityHintsFor(primary, secondary interaction.SpeechAct, tokens []strin
|
||||
add("SEND_EMAIL")
|
||||
add("READ_EMAIL")
|
||||
}
|
||||
if primary == interaction.SpeechActDirective && containsStemInTokens(tokens, "добав", "реализ", "поддержк") {
|
||||
add("MODIFY_CODE")
|
||||
}
|
||||
if containsToken(tokens, "напомни") || containsToken(tokens, "напомин") {
|
||||
add("SCHEDULE_REMINDER")
|
||||
}
|
||||
if primary == interaction.SpeechActRequestToInvestigate || (primary == interaction.SpeechActQuestion && containsInterrogative(tokens) && containsStemInTokens(tokens, "почему", "как", "что")) {
|
||||
add("COGNITIVE_REASONING")
|
||||
}
|
||||
if containsInterrogative(tokens) && containsArithmetic(tokens) {
|
||||
add("COGNITIVE_REASONING")
|
||||
}
|
||||
if containsStemInTokens(tokens, "тормоз", "медлен", "почему", "что", "сервис", "база", "падает") {
|
||||
add("INSPECT_PROJECT")
|
||||
add("READ_DATABASE")
|
||||
add("QUERY_LOGS")
|
||||
}
|
||||
if containsToken(tokens, "свободно") || containsToken(tokens, "гб") || containsToken(tokens, "модель") || containsToken(tokens, "postgres") || containsToken(tokens, "база") {
|
||||
add("VERIFY_FACT")
|
||||
}
|
||||
@@ -531,6 +539,66 @@ func containsArithmetic(tokens []string) bool {
|
||||
return digits >= 2
|
||||
}
|
||||
|
||||
func looksLikeInstructionWithoutPunctuation(first string) bool {
|
||||
if first == "" {
|
||||
return false
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(first, "й"), strings.HasSuffix(first, "и"), strings.HasSuffix(first, "ите"):
|
||||
return utf8.RuneCountInString(first) > 3
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func looksLikeInstructionWithoutPunctuationAfterLead(tokens []string) bool {
|
||||
if len(tokens) < 2 {
|
||||
return false
|
||||
}
|
||||
if utf8.RuneCountInString(tokens[0]) > 3 {
|
||||
return false
|
||||
}
|
||||
switch tokens[0] {
|
||||
case "про", "по", "на", "в", "за":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
for _, token := range tokens[1:] {
|
||||
if utf8.RuneCountInString(token) <= 3 {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(token, "ешь"), strings.HasSuffix(token, "и"), strings.HasSuffix(token, "й"), strings.HasSuffix(token, "те"), strings.HasSuffix(token, "йте"):
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func looksLikeInformationRequest(tokens []string) bool {
|
||||
if len(tokens) < 2 {
|
||||
return false
|
||||
}
|
||||
if !containsSuffix(tokens[0], "й", "и", "те", "йте") {
|
||||
return false
|
||||
}
|
||||
for _, token := range tokens[1:] {
|
||||
if containsStem(token, "что", "как", "почему", "зачем", "когда", "кто", "какой", "какая", "какие", "об", "про", "подроб") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func containsSuffix(value string, suffixes ...string) bool {
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(value, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func likelyConsequential(tokens []string) bool {
|
||||
for _, token := range tokens {
|
||||
if containsStem(token, "удал", "снес", "замен", "миграц", "парол", "опубли", "перезапуст", "смени", "купи", "установ", "внедр") {
|
||||
@@ -550,6 +618,24 @@ func followsNibud(tokens []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func firstPersonPastReport(tokens []string, index int) bool {
|
||||
if len(tokens) == 0 || index == 0 {
|
||||
return false
|
||||
}
|
||||
if tokens[0] != "я" && tokens[0] != "мы" {
|
||||
return false
|
||||
}
|
||||
if index < 2 {
|
||||
return false
|
||||
}
|
||||
for _, token := range tokens[:index] {
|
||||
if token == "всё" || token == "все" || token == "сейчас" || token == "уже" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func containsModelReference(tokens []string) bool {
|
||||
for _, token := range tokens {
|
||||
if token == "модель" || token == "модели" || token == "postgres" || token == "postgresql" || strings.Contains(token, "gemma") || strings.Contains(token, "гемма") || strings.Contains(token, "qwen") {
|
||||
|
||||
@@ -129,6 +129,8 @@ func TestInterpretUtteranceNaturalRussianHelpflowUsesContextAwareCognitionInput(
|
||||
{message: "Расскажи как это работает", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "можешь объяснить почему база растёт", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "ПОЧЕМУ postgres использует так много памяти", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "ПОЧЕМУ postgres использует столько памяти", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "разберись почему сервис упал", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "разберись почему сервис упал", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "посмотри что произошло", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
{message: "исправь ошибку если найдёшь", wantSpeech: "", wantCognition: true, allowNoMatch: true},
|
||||
|
||||
@@ -82,11 +82,16 @@ func DetermineHelpPath(input work.PlanningInput) work.HelpPath {
|
||||
hasQueryLogsHint := hasPlanningHint(input, capability.CapabilityKindQueryLogs)
|
||||
hasVerifyHint := hasPlanningHint(input, capability.CapabilityKindVerifyFact)
|
||||
rawTextFallbackAllowed := len(input.CapabilityHints) == 0 && !input.RequiresClarification && !input.NeedsCognition
|
||||
if input.SpeechAct != work.SpeechActRef("") && input.SpeechAct != work.SpeechActRef("UNKNOWN") {
|
||||
rawTextFallbackAllowed = false
|
||||
}
|
||||
topSpeech := input.SpeechAct
|
||||
|
||||
switch {
|
||||
case input.RequiresClarification:
|
||||
return work.HelpPath{Kind: work.HelpPathAnswer, Reason: "clarification is required before capability selection or durable work", Reply: "Нужно короткое уточнение, прежде чем выбирать capability или строить более тяжёлый маршрут."}
|
||||
case topSpeech == work.SpeechActRef("REQUEST_TO_INVESTIGATE"):
|
||||
return work.HelpPath{Kind: work.HelpPathDurableWork, NeedsDurableGoal: true, NeedsDurableWork: true, NeedsDurablePlan: true, Reason: "explicit request-to-investigate should materialize durable work"}
|
||||
case hasReminderHint || (rawTextFallbackAllowed && isReminderRequest(lower)):
|
||||
return work.HelpPath{Kind: work.HelpPathDirectCapability, SuggestedCapabilityKinds: []capability.CapabilityKind{capability.CapabilityKindScheduleReminder}, Reason: "reminder is a direct scheduling-capability path, not a coding/project workflow", Reply: "Понял: нужен reminder/scheduler capability. В DEV-000063 я фиксирую это как безопасный путь помощи; само напоминание ещё не зарегистрировано, потому что scheduling adapter не подключён."}
|
||||
case isEmailReviewRequest(lower) && hasReadEmailHint:
|
||||
@@ -95,10 +100,10 @@ func DetermineHelpPath(input work.PlanningInput) work.HelpPath {
|
||||
return work.HelpPath{Kind: work.HelpPathCapabilityGap, SuggestedCapabilityKinds: []capability.CapabilityKind{capability.CapabilityKindSendEmail}, CapabilityGap: true, Reason: "email sending needs a communication capability implementation and explicit authority", Reply: "Сейчас у меня ещё не подключена возможность отправлять почту. Я зафиксирую этот capability gap, чтобы позже к нему можно было вернуться."}
|
||||
case hasModifyHint || hasInspectHint || hasQueryLogsHint || hasReadEmailHint || hasRecallHint || hasVerifyHint || needsDurableWorkFromHints(input):
|
||||
return work.HelpPath{Kind: work.HelpPathDurableWork, NeedsDurableGoal: true, NeedsDurableWork: true, NeedsDurablePlan: true, Reason: "semantic hints indicate project/code work even if cognition may still be useful upstream"}
|
||||
case isUnsupportedCapabilityRequest(lower):
|
||||
return work.HelpPath{Kind: work.HelpPathCapabilityGap, SuggestedCapabilityKinds: []capability.CapabilityKind{}, CapabilityGap: true, Reason: "requested help needs a currently missing capability and may later trigger acquisition/self-extension", Reply: "Похоже, для этой просьбы нужна capability, которой сейчас нет. Это не задача конкретного executor автоматически: в будущем Домовой сможет найти, подключить или создать подходящую capability и вернуться к исходной потребности."}
|
||||
case rawTextFallbackAllowed && requiresDurableWork(lower):
|
||||
return work.HelpPath{Kind: work.HelpPathDurableWork, NeedsDurableGoal: true, NeedsDurableWork: true, NeedsDurablePlan: true, Reason: "request needs durable coordination, planning, investigation, or outcome tracking"}
|
||||
case rawTextFallbackAllowed && isUnsupportedCapabilityRequest(lower):
|
||||
return work.HelpPath{Kind: work.HelpPathCapabilityGap, SuggestedCapabilityKinds: []capability.CapabilityKind{}, CapabilityGap: true, Reason: "requested help needs a currently missing capability and may later trigger acquisition/self-extension", Reply: "Похоже, для этой просьбы нужна capability, которой сейчас нет. Это не задача конкретного executor автоматически: в будущем Домовой сможет найти, подключить или создать подходящую capability и вернуться к исходной потребности."}
|
||||
case rawTextFallbackAllowed && containsAny(lower, "объясни", "расскажи", "что делает"):
|
||||
return work.HelpPath{Kind: work.HelpPathCognition, Reason: "simple help can be answered without durable Goal/Work/Plan"}
|
||||
case input.NeedsCognition && (topSpeech == work.SpeechActRef("QUESTION") || topSpeech == work.SpeechActRef("REQUEST_TO_INVESTIGATE") || hasRecallHint):
|
||||
@@ -173,14 +178,32 @@ func ValidatePlanCandidate(candidate work.PlanCandidate) error {
|
||||
|
||||
func addScenarioPlan(input work.PlanningInput, millis int64, candidate *work.PlanCandidate) {
|
||||
lower := strings.ToLower(input.Message)
|
||||
hasReminderHint := hasPlanningHint(input, capability.CapabilityKindScheduleReminder)
|
||||
hasSendEmailHint := hasPlanningHint(input, capability.CapabilityKindSendEmail)
|
||||
hasReadEmailHint := hasPlanningHint(input, capability.CapabilityKindReadEmail)
|
||||
hasModifyHint := hasPlanningHint(input, capability.CapabilityKindModifyCode)
|
||||
hasInspectHint := hasPlanningHint(input, capability.CapabilityKindInspectProject)
|
||||
hasQueryLogsHint := hasPlanningHint(input, capability.CapabilityKindQueryLogs)
|
||||
hasVerifyHint := hasPlanningHint(input, capability.CapabilityKindVerifyFact)
|
||||
requiresInvestigation := input.SpeechAct == work.SpeechActRef("REQUEST_TO_INVESTIGATE")
|
||||
if input.SecondarySpeechAct == work.SpeechActRef("CORRECTION") {
|
||||
addStep(candidate, work.PlanStepClarify, "Preserve the correction as part of the same human intent history before execution-oriented work.", len(candidate.Steps)+1, millis)
|
||||
}
|
||||
switch {
|
||||
case isReminderRequest(lower):
|
||||
addRequirement(candidate, capability.CapabilityKindScheduleReminder, "schedule a future reminder after temporal interpretation", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepSchedule, "Interpret the requested reminder time and reminder subject.", 1, millis)
|
||||
case hasReminderHint:
|
||||
addRequirement(candidate, capability.CapabilityKindScheduleReminder, "respecting a structured reminder intent", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepSchedule, "Interpret requested reminder horizon and context.", 1, millis)
|
||||
addStep(candidate, work.PlanStepNoExecution, "Do not register a reminder until scheduling capability/authority is available.", 2, millis)
|
||||
case hasReadEmailHint:
|
||||
addRequirement(candidate, capability.CapabilityKindReadEmail, "read incoming communications", epistemics.VerificationUnknown, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindCognitiveReasoning, "classify messages needing response", epistemics.VerificationUnknown, millis)
|
||||
addStep(candidate, work.PlanStepObserve, "Read relevant email messages through a mail capability.", 1, millis)
|
||||
addStep(candidate, work.PlanStepReason, "Classify which messages need a response and why.", 2, millis)
|
||||
addStep(candidate, work.PlanStepReport, "Report recommended replies without claiming email was sent.", 3, millis)
|
||||
case hasSendEmailHint:
|
||||
addRequirement(candidate, capability.CapabilityKindSendEmail, "send email only after authority", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepPrepare, "Resolve recipient and appropriate communication channel.", 1, millis)
|
||||
addStep(candidate, work.PlanStepCommunicate, "Prepare the message for a communication adapter; do not send without authority.", 2, millis)
|
||||
case isEmailReviewRequest(lower):
|
||||
addRequirement(candidate, capability.CapabilityKindReadEmail, "read incoming communications", epistemics.VerificationUnknown, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindCognitiveReasoning, "classify messages needing response", epistemics.VerificationUnknown, millis)
|
||||
@@ -191,25 +214,29 @@ func addScenarioPlan(input work.PlanningInput, millis int64, candidate *work.Pla
|
||||
addRequirement(candidate, capability.CapabilityKindSendEmail, "send email only after authority", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepPrepare, "Resolve recipient and appropriate communication channel.", 1, millis)
|
||||
addStep(candidate, work.PlanStepCommunicate, "Prepare the message for a communication adapter; do not send without authority.", 2, millis)
|
||||
case containsAny(lower, "тормозит", "медлен", "производительн", "разберись", "почему"):
|
||||
case isReminderRequest(lower):
|
||||
addRequirement(candidate, capability.CapabilityKindScheduleReminder, "schedule a future reminder after temporal interpretation", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepSchedule, "Interpret the requested reminder time and reminder subject.", 1, millis)
|
||||
addStep(candidate, work.PlanStepNoExecution, "Do not register a reminder until scheduling capability/authority is available.", 2, millis)
|
||||
case requiresInvestigation || hasInspectHint || hasQueryLogsHint || hasVerifyHint:
|
||||
addRequirement(candidate, capability.CapabilityKindInspectProject, "inspect project context", epistemics.VerificationNotApplicable, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindReadDatabase, "collect database performance observations", epistemics.VerificationUnknown, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindQueryLogs, "collect runtime/log observations", epistemics.VerificationUnknown, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindCognitiveReasoning, "compare evidence and identify likely causes", epistemics.VerificationUnknown, millis)
|
||||
if containsAny(lower, "патч", "исправ") {
|
||||
if containsAny(lower, "патч", "исправ") || containsAny(lower, "обнов", "конфиг") {
|
||||
addRequirement(candidate, capability.CapabilityKindModifyCode, "modify software after investigation confirms the change", epistemics.VerificationUnknown, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindRunTests, "validate the patch after code modification", epistemics.VerificationNotApplicable, millis)
|
||||
}
|
||||
addStep(candidate, work.PlanStepInvestigate, "Identify the relevant project/service scope and current symptoms.", 1, millis)
|
||||
addStep(candidate, work.PlanStepObserve, "Collect database, runtime, log and configuration observations.", 2, millis)
|
||||
addStep(candidate, work.PlanStepReason, "Compare evidence and determine likely performance causes.", 3, millis)
|
||||
if containsAny(lower, "патч", "исправ") {
|
||||
if containsAny(lower, "патч", "исправ") || containsAny(lower, "обнов", "конфиг") {
|
||||
addStep(candidate, work.PlanStepModify, "Prepare a validated patch only after the investigation narrows the likely cause.", 4, millis)
|
||||
addStep(candidate, work.PlanStepRunChecks, "Run relevant checks before claiming the patch is safe.", 5, millis)
|
||||
} else {
|
||||
addStep(candidate, work.PlanStepReport, "Report findings and what remains unverified.", 4, millis)
|
||||
}
|
||||
case containsAny(lower, "добавь", "поддержк", "возможность", "реализ"):
|
||||
case hasModifyHint:
|
||||
addRequirement(candidate, capability.CapabilityKindModifyCode, "modify software project", epistemics.VerificationNotApplicable, millis)
|
||||
addRequirement(candidate, capability.CapabilityKindRunTests, "validate software change", epistemics.VerificationNotApplicable, millis)
|
||||
addStep(candidate, work.PlanStepInvestigate, "Understand desired capability and affected project scope.", 1, millis)
|
||||
@@ -227,7 +254,6 @@ func addScenarioPlan(input work.PlanningInput, millis int64, candidate *work.Pla
|
||||
addDelegation(candidate, requirement, millis)
|
||||
}
|
||||
}
|
||||
|
||||
func requiresDurableWork(lower string) bool {
|
||||
return containsAny(lower, "тормозит", "медлен", "производительн", "разберись", "почему", "добавь", "поддержк", "возможность", "реализ", "патч", "исправь", "установи", "большую модель")
|
||||
}
|
||||
@@ -335,4 +361,4 @@ func isExplicitEmailGapRequest(lower string) bool {
|
||||
|
||||
func isUnsupportedCapabilityRequest(lower string) bool {
|
||||
return containsAny(lower, "не умеешь", "подключи интеграцию", "подключи сервис", "интеграц", "неизвестн", "нет capability", "создай adapter", "создай адаптер", "сервис, которого сейчас нет")
|
||||
}
|
||||
}\r\n
|
||||
@@ -25,7 +25,9 @@ func TestPlanningInvestigationIsNotCodexPrompt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPlanningCodeChangeRequiresModifyCodeWithoutExecutor(t *testing.T) {
|
||||
result := planForTest(t, "В Assistant добавь поддержку X.", "DIRECTIVE", epistemics.VerificationNotApplicable)
|
||||
input := planningInputForTest("В Assistant добавь поддержку X.", "DIRECTIVE", epistemics.VerificationNotApplicable)
|
||||
input.CapabilityHints = []capability.CapabilityKind{capability.CapabilityKindModifyCode}
|
||||
result := planForTestWithInput(t, input)
|
||||
requireKinds(t, result, capability.CapabilityKindModifyCode, capability.CapabilityKindRunTests)
|
||||
for _, delegation := range result.Candidate.Delegations {
|
||||
if delegation.ExecutorHint != "" {
|
||||
@@ -41,7 +43,9 @@ func TestPlanningEmailReviewIsNotCodexSpecific(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPlanningReminderUsesSchedulingRequirement(t *testing.T) {
|
||||
result := planForTest(t, "Напомни завтра позвонить врачу.", "DIRECTIVE", epistemics.VerificationNotApplicable)
|
||||
input := planningInputForTest("Напомни завтра позвонить врачу.", "DIRECTIVE", epistemics.VerificationNotApplicable)
|
||||
input.CapabilityHints = []capability.CapabilityKind{capability.CapabilityKindScheduleReminder}
|
||||
result := planForTestWithInput(t, input)
|
||||
requireKinds(t, result, capability.CapabilityKindScheduleReminder)
|
||||
requireNoKind(t, result, capability.CapabilityKindModifyCode)
|
||||
}
|
||||
@@ -79,14 +83,18 @@ func TestPlanningFallbackPersistsWithoutCognition(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHelpPathSimpleAnswerDoesNotRequireDurableGoalWorkPlan(t *testing.T) {
|
||||
path := DetermineHelpPath(planningInputForTest("Объясни, что делает этот код.", "QUESTION", epistemics.VerificationNotApplicable))
|
||||
input := planningInputForTest("Объясни, что делает этот код.", "QUESTION", epistemics.VerificationNotApplicable)
|
||||
input.NeedsCognition = true
|
||||
path := DetermineHelpPath(input)
|
||||
if path.Kind != work.HelpPathCognition || path.NeedsDurableGoal || path.NeedsDurableWork || path.NeedsDurablePlan {
|
||||
t.Fatalf("simple help must not require durable planning: %#v", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpPathReminderCanUseDirectCapabilityWithoutWorkflow(t *testing.T) {
|
||||
path := DetermineHelpPath(planningInputForTest("Напомни завтра позвонить.", "DIRECTIVE", epistemics.VerificationNotApplicable))
|
||||
input := planningInputForTest("Напомни завтра позвонить.", "DIRECTIVE", epistemics.VerificationNotApplicable)
|
||||
input.CapabilityHints = []capability.CapabilityKind{capability.CapabilityKindScheduleReminder}
|
||||
path := DetermineHelpPath(input)
|
||||
if path.Kind != work.HelpPathDirectCapability || path.NeedsDurableWork || len(path.SuggestedCapabilityKinds) != 1 || path.SuggestedCapabilityKinds[0] != capability.CapabilityKindScheduleReminder {
|
||||
t.Fatalf("reminder should be direct scheduling capability path: %#v", path)
|
||||
}
|
||||
@@ -99,6 +107,31 @@ func TestHelpPathComplexWorkMaterializesDurablePlanning(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpPathStructuredIntentNotOverriddenByRawRussianText(t *testing.T) {
|
||||
path := DetermineHelpPath(work.PlanningInput{
|
||||
ConversationID: "conversation-1",
|
||||
UtteranceID: "utterance-1",
|
||||
IntentID: "intent-1",
|
||||
ActorID: identity.ActorID("actor-1"),
|
||||
PrincipalID: identity.PrincipalID("principal-1"),
|
||||
ProjectID: work.ProjectRef("upo"),
|
||||
ProjectDisplayName: "УПО",
|
||||
ProjectProvenance: work.ProjectProvenanceRef("EXPLICIT"),
|
||||
Message: "Объясни, как добавить поддержку отправки писем в этом проекте.",
|
||||
SpeechAct: work.SpeechActRef("DIRECTIVE"),
|
||||
NeedsCognition: false,
|
||||
CapabilityHints: []capability.CapabilityKind{capability.CapabilityKindModifyCode},
|
||||
AssertionVerification: epistemics.VerificationNotApplicable,
|
||||
ReceivedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
|
||||
})
|
||||
if path.Kind != work.HelpPathDurableWork || !path.NeedsDurableWork || !path.NeedsDurablePlan {
|
||||
t.Fatalf("structured durable intent should be preserved over raw phrase heuristics: %#v", path)
|
||||
}
|
||||
if path.Kind == work.HelpPathCognition {
|
||||
t.Fatalf("raw Russian phrase should not force cognition when selected interpretation is already directive: %#v", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpPathCapabilityGapDoesNotBecomeCodexTask(t *testing.T) {
|
||||
path := DetermineHelpPath(planningInputForTest("Подключи интеграцию с неизвестным сервисом, которого сейчас нет.", "DIRECTIVE", epistemics.VerificationNotApplicable))
|
||||
if path.Kind != work.HelpPathCapabilityGap || !path.CapabilityGap {
|
||||
@@ -121,7 +154,12 @@ func TestHelpPathStructuredHintsOverrideRawCognitionPhrase(t *testing.T) {
|
||||
|
||||
func planForTest(t *testing.T, message string, act string, verification epistemics.VerificationState) work.PlanningResult {
|
||||
t.Helper()
|
||||
result, err := BuildPlanningResult(planningInputForTest(message, act, verification), time.Unix(100, 0))
|
||||
return planForTestWithInput(t, planningInputForTest(message, act, verification))
|
||||
}
|
||||
|
||||
func planForTestWithInput(t *testing.T, input work.PlanningInput) work.PlanningResult {
|
||||
t.Helper()
|
||||
result, err := BuildPlanningResult(input, time.Unix(100, 0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user