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>
144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package coordinator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"chat-agent-system/internal/intent"
|
|
)
|
|
|
|
func candidateFromFrame(frame intent.IntentFrameV2) *intent.CandidateIntent {
|
|
slots := make(map[string]string, len(frame.Slots))
|
|
for k, v := range frame.Slots {
|
|
slots[k] = v.Value
|
|
}
|
|
return &intent.CandidateIntent{
|
|
Intent: frame.LegacyIntent,
|
|
Confidence: frame.Score,
|
|
FilledSlots: slots,
|
|
Reasoning: fmt.Sprintf("IntentPlan step action=%s", frame.ActionKey),
|
|
}
|
|
}
|
|
|
|
// executeIntentPlan 顺序执行有限多意图计划(Phase 5:仅安全只读组合自动执行)
|
|
func (e *CoordinatorEngine) executeIntentPlan(
|
|
ctx context.Context,
|
|
recRes *intent.IntentRecognitionResult,
|
|
userCtx *UserProjectContext,
|
|
) (*CoordinationResult, error) {
|
|
plan := recRes.IntentPlan
|
|
if plan == nil || len(plan.Steps) == 0 || !intent.CurrentFeatureFlags().MultiIntentEnabled {
|
|
return nil, nil
|
|
}
|
|
|
|
var parts []string
|
|
var last *CoordinationResult
|
|
var pendingConfirm *intent.IntentPlanStep
|
|
|
|
for _, step := range plan.Steps {
|
|
cand := candidateFromFrame(step.Frame)
|
|
|
|
if step.Frame.Relation == intent.RelationExpectationHit ||
|
|
step.Frame.Goal == intent.GoalDecide ||
|
|
step.Frame.Goal == intent.GoalControl && isConfirmRequiredStep(&step) {
|
|
pendingConfirm = &step
|
|
continue
|
|
}
|
|
|
|
if !isAutoExecutableQueryStep(&step) {
|
|
return nil, nil
|
|
}
|
|
|
|
stepRes, err := e.dispatchIntentHandler(ctx, cand, userCtx, recRes.RawInput)
|
|
if err != nil {
|
|
if plan.FailureMode == "stop_on_first_failure" {
|
|
return nil, err
|
|
}
|
|
continue
|
|
}
|
|
if stepRes == nil {
|
|
continue
|
|
}
|
|
last = stepRes
|
|
if stepRes.NaturalReply != "" {
|
|
parts = append(parts, fmt.Sprintf("**步骤 %s** %s", step.StepID, stepRes.NaturalReply))
|
|
}
|
|
}
|
|
|
|
if len(parts) == 0 && pendingConfirm == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
reply := strings.Join(parts, "\n\n")
|
|
if pendingConfirm != nil {
|
|
confirmNote := fmt.Sprintf(
|
|
"另:您同时提到了「%s」。该操作需单独确认后再执行,请回复「确认」或选择对应选项。",
|
|
pendingConfirm.Frame.ActionKey,
|
|
)
|
|
if reply == "" {
|
|
reply = confirmNote
|
|
} else {
|
|
reply += "\n\n" + confirmNote
|
|
}
|
|
}
|
|
|
|
if last == nil {
|
|
last = &CoordinationResult{UpdatedContext: userCtx}
|
|
}
|
|
last.NaturalReply = reply
|
|
last.Intent = recRes.TopIntent.Intent
|
|
last.Branch = recRes.Branch
|
|
last.UpdatedContext = userCtx
|
|
if pendingConfirm != nil && userCtx.PendingExpectation != nil {
|
|
last.NextExpectation = userCtx.PendingExpectation
|
|
}
|
|
return last, nil
|
|
}
|
|
|
|
func isAutoExecutableQueryStep(step *intent.IntentPlanStep) bool {
|
|
if step == nil {
|
|
return false
|
|
}
|
|
return step.Frame.Goal == intent.GoalQuery || intent.IsQueryLegacyIntent(step.Frame.LegacyIntent)
|
|
}
|
|
|
|
func isConfirmRequiredStep(step *intent.IntentPlanStep) bool {
|
|
if step == nil {
|
|
return false
|
|
}
|
|
spec, ok := intent.DefaultRegistry().Get(step.Frame.ActionKey)
|
|
return ok && spec.RequiresConfirm
|
|
}
|
|
|
|
func (e *CoordinatorEngine) dispatchIntentHandler(
|
|
ctx context.Context,
|
|
cand *intent.CandidateIntent,
|
|
userCtx *UserProjectContext,
|
|
rawInput string,
|
|
) (*CoordinationResult, error) {
|
|
if cand == nil {
|
|
return nil, nil
|
|
}
|
|
switch cand.Intent {
|
|
case intent.IntentQueryProgress:
|
|
return e.handleQueryProgress(ctx, cand, userCtx, rawInput)
|
|
case intent.IntentQueryRegulations:
|
|
return e.handleQueryRegulations(ctx, cand, userCtx)
|
|
case intent.IntentQuerySimilarCases:
|
|
return e.handleQuerySimilarCases(ctx, cand, userCtx)
|
|
case intent.IntentQueryUserRights:
|
|
return e.handleQueryUserRights(ctx, cand, userCtx)
|
|
case intent.IntentQueryWorkflowStatus:
|
|
return e.handleQueryWorkflowStatus(ctx, cand, userCtx)
|
|
case intent.IntentQueryDocStatus:
|
|
return e.handleQueryDocStatus(ctx, cand, userCtx)
|
|
case intent.IntentRunStatusQuery:
|
|
return e.handleRunStatusQuery(ctx, cand, userCtx)
|
|
case intent.IntentHelpCapabilities:
|
|
return e.handleHelpCapabilities(ctx, cand, userCtx)
|
|
default:
|
|
return nil, nil
|
|
}
|
|
}
|