package intent import ( "context" "fmt" "strings" ) // buildExpectationPlusNewPlan 确认挂起预期 + 独立新请求(如「好,请先查法规」) func buildExpectationPlusNewPlan( _ context.Context, _ *RuleBasedRecognizer, input string, _ *ContextSnapshot, pending *PendingExpectation, primary *IntentRecognitionResult, ) *IntentPlan { if pending == nil || primary == nil || primary.TopIntent == nil { return nil } if !CurrentFeatureFlags().MultiIntentEnabled { return nil } if !hasIndependentNewPredicate(input) { return nil } // 新请求必须是独立抢占意图,不能仍是挂起预期本身 if primary.Branch == BranchExpectationHit { return nil } confirmFrame := buildExpectationConfirmFrame(pending) if confirmFrame == nil { return nil } newFrame := BuildIntentFrameV2(primary, DefaultRegistry()) if newFrame == nil { return nil } if isWriteIntent(primary.TopIntent.Intent) { return nil } steps := []IntentPlanStep{ { StepID: "1", Frame: *confirmFrame, OnFailure: "abort", }, { StepID: "2", Frame: *newFrame, DependsOn: []string{"1"}, OnFailure: "abort", }, } if primary.IntentFrameV2 != nil { primary.IntentFrameV2.Relation = RelationExpectationPlusNew } return &IntentPlan{ Steps: steps, Atomicity: "sequential", NeedsConfirm: confirmFrame.Goal == GoalControl || confirmFrame.Goal == GoalMutate, FailureMode: "stop_on_first_failure", } } func buildExpectationConfirmFrame(pending *PendingExpectation) *IntentFrameV2 { var opt *ExpectationOption if pending.DefaultOption != nil { opt = pending.DefaultOption } else if len(pending.Options) > 0 { opt = &pending.Options[0] } else if pending.ExpectedIntent != "" { opt = &ExpectationOption{ OptionID: pending.TargetAction, Title: pending.Prompt, Intent: pending.ExpectedIntent, Slots: pending.Payload, } } if opt == nil { return nil } spec, ok := DefaultRegistry().GetByLegacyIntent(opt.Intent) frame := &IntentFrameV2{ Version: IntentFrameV2Version, LegacyIntent: opt.Intent, Relation: RelationExpectationHit, Score: 1.0, DialogueAct: DialogueActConfirm, } if ok { frame.ActionKey = spec.Key frame.Domain = spec.Domain frame.Object = spec.Object frame.Goal = spec.Goal } else { frame.ActionKey = fmt.Sprintf("pending.%s", strings.ToLower(string(opt.Intent))) frame.Domain = "conversation" frame.Object = "system_ui" frame.Goal = GoalDecide } if len(opt.Slots) > 0 { frame.Slots = make(map[string]SlotValue, len(opt.Slots)) for k, v := range opt.Slots { frame.Slots[k] = SlotValue{Value: v, Source: "expectation"} } } return frame }