Add env-based feature flags (intent_v2_shadow/decision, multi_intent, workflow_intents), V1/V2 shadow diff reporting, expectation_plus_new plan building, ranked-frame L2 conflict gate, and coordinator sequential query plan execution with confirm-step deferral. Co-authored-by: Cursor <cursoragent@cursor.com>
112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
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
|
|
}
|