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>
103 lines
2.6 KiB
Go
103 lines
2.6 KiB
Go
package intent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var planConnectors = []string{",然后", ",然后", ",并", ",并", ",接着", ",接着", ";再", ";再"}
|
|
|
|
// splitOnPlanConnectors 按有限连接词拆分多意图输入
|
|
func splitOnPlanConnectors(input string) []string {
|
|
for _, sep := range planConnectors {
|
|
if idx := strings.Index(input, sep); idx > 0 {
|
|
left := strings.TrimSpace(input[:idx])
|
|
right := strings.TrimSpace(input[idx+len(sep):])
|
|
if left != "" && right != "" {
|
|
return []string{left, right}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// buildLimitedIntentPlan 构建有限多意图计划(仅 query+query / query+generate 等安全组合)
|
|
func buildLimitedIntentPlan(ctx context.Context, r *RuleBasedRecognizer, input string, snap *ContextSnapshot, primary *IntentRecognitionResult) *IntentPlan {
|
|
if !CurrentFeatureFlags().MultiIntentEnabled {
|
|
return nil
|
|
}
|
|
parts := splitOnPlanConnectors(input)
|
|
if len(parts) < 2 {
|
|
return nil
|
|
}
|
|
|
|
var steps []IntentPlanStep
|
|
for i, part := range parts {
|
|
subRes, err := r.Recognize(ctx, part, snap)
|
|
if err != nil || subRes == nil || subRes.TopIntent == nil {
|
|
return nil
|
|
}
|
|
frame := BuildIntentFrameV2(subRes, DefaultRegistry())
|
|
if frame == nil {
|
|
return nil
|
|
}
|
|
// 高风险写动作组合初期不支持
|
|
if isWriteIntent(subRes.TopIntent.Intent) {
|
|
return nil
|
|
}
|
|
stepID := fmt.Sprintf("%d", i+1)
|
|
var depends []string
|
|
if i > 0 {
|
|
depends = []string{fmt.Sprintf("%d", i)}
|
|
}
|
|
steps = append(steps, IntentPlanStep{
|
|
StepID: stepID,
|
|
Frame: *frame,
|
|
DependsOn: depends,
|
|
OnFailure: "abort",
|
|
})
|
|
}
|
|
|
|
if len(steps) < 2 {
|
|
return nil
|
|
}
|
|
// 至少一个查询步骤
|
|
hasQuery := false
|
|
for _, s := range steps {
|
|
if s.Frame.Goal == GoalQuery || isQueryIntent(s.Frame.LegacyIntent) {
|
|
hasQuery = true
|
|
break
|
|
}
|
|
}
|
|
if !hasQuery {
|
|
return nil
|
|
}
|
|
|
|
if primary != nil && primary.IntentFrameV2 != nil {
|
|
primary.IntentFrameV2.Relation = RelationExpectationPlusNew
|
|
}
|
|
return &IntentPlan{
|
|
Steps: steps,
|
|
Atomicity: "sequential",
|
|
NeedsConfirm: false,
|
|
FailureMode: "stop_on_first_failure",
|
|
}
|
|
}
|
|
|
|
func inferSupplementSubtype(input string, slots map[string]string) string {
|
|
if strings.Contains(input, "附件") || strings.Contains(input, "材料") || strings.Contains(input, "上传") {
|
|
return "attach"
|
|
}
|
|
if strings.Contains(input, "纠正") || strings.Contains(input, "更正") ||
|
|
(strings.Contains(input, "改为") && strings.Contains(input, "原来")) {
|
|
return "correct"
|
|
}
|
|
if strings.Contains(input, "修改为") || strings.Contains(input, "改为") || strings.Contains(input, "改成") {
|
|
if slots[SlotFactKey] != "" {
|
|
return "correct"
|
|
}
|
|
}
|
|
return "add"
|
|
}
|