Files
WeiCHENandCursor 24478ec383 feat(intent): Phase 5 V2 shadow gray flags and IntentPlan execution
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>
2026-09-13 02:50:21 +00:00

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
}