Files
Agent-System/internal/intent/plan_builder.go
T
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

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"
}