package intent import ( "fmt" "sync" ) // RiskLevel 动作风险等级 type RiskLevel string const ( RiskLow RiskLevel = "low" RiskMedium RiskLevel = "medium" RiskHigh RiskLevel = "high" ) // ActionAlias 自然语言别名 type ActionAlias struct { Phrase string `json:"phrase"` Acts []DialogueAct `json:"acts,omitempty"` } // SlotSpec 槽位规格 type SlotSpec struct { Key string `json:"key"` Required bool `json:"required"` Label string `json:"label,omitempty"` } // ActionSpec 可识别、可授权、可执行的动作单一事实源 type ActionSpec struct { Key string `json:"key"` Domain string `json:"domain"` Object string `json:"object"` Goal GoalType `json:"goal"` Risk RiskLevel `json:"risk"` Aliases []ActionAlias `json:"aliases,omitempty"` DialogueActs []DialogueAct `json:"dialogue_acts,omitempty"` RequiredSlots []SlotSpec `json:"required_slots,omitempty"` OptionalSlots []SlotSpec `json:"optional_slots,omitempty"` CapabilityKey string `json:"capability_key,omitempty"` AllowedStates []string `json:"allowed_states,omitempty"` RequiresConfirm bool `json:"requires_confirm,omitempty"` IdempotencyMode string `json:"idempotency_mode,omitempty"` LegacyIntent IntentType `json:"legacy_intent"` } // ActionRegistry 动作注册表 type ActionRegistry struct { mu sync.RWMutex byKey map[string]*ActionSpec byLegacy map[IntentType]*ActionSpec } var defaultRegistry *ActionRegistry func init() { defaultRegistry = NewActionRegistry() registerBuiltinActions(defaultRegistry) } // DefaultRegistry 返回全局内置注册表 func DefaultRegistry() *ActionRegistry { return defaultRegistry } // NewActionRegistry 创建空注册表 func NewActionRegistry() *ActionRegistry { return &ActionRegistry{ byKey: make(map[string]*ActionSpec), byLegacy: make(map[IntentType]*ActionSpec), } } // Register 注册动作规格 func (r *ActionRegistry) Register(spec ActionSpec) error { r.mu.Lock() defer r.mu.Unlock() if spec.Key == "" { return fmt.Errorf("action spec key is required") } if spec.LegacyIntent == "" { return fmt.Errorf("action spec %s: legacy_intent is required", spec.Key) } if existing, ok := r.byKey[spec.Key]; ok && existing.LegacyIntent != spec.LegacyIntent { return fmt.Errorf("duplicate action key %s", spec.Key) } cp := spec r.byKey[spec.Key] = &cp r.byLegacy[spec.LegacyIntent] = &cp return nil } // Get 按 action_key 查找 func (r *ActionRegistry) Get(key string) (*ActionSpec, bool) { r.mu.RLock() defer r.mu.RUnlock() spec, ok := r.byKey[key] return spec, ok } // GetByLegacyIntent 按旧 IntentType 查找 func (r *ActionRegistry) GetByLegacyIntent(t IntentType) (*ActionSpec, bool) { r.mu.RLock() defer r.mu.RUnlock() spec, ok := r.byLegacy[t] return spec, ok } // All 返回全部已注册动作 func (r *ActionRegistry) All() []*ActionSpec { r.mu.RLock() defer r.mu.RUnlock() out := make([]*ActionSpec, 0, len(r.byKey)) for _, s := range r.byKey { out = append(out, s) } return out } // CapabilityKeys 派生能力键列表(供 AvailableActions 同源生成) func (r *ActionRegistry) CapabilityKeys() []string { r.mu.RLock() defer r.mu.RUnlock() seen := make(map[string]struct{}) var keys []string for _, s := range r.byKey { if s.CapabilityKey == "" { continue } if _, ok := seen[s.CapabilityKey]; ok { continue } seen[s.CapabilityKey] = struct{}{} keys = append(keys, s.CapabilityKey) } return keys } func registerBuiltinActions(r *ActionRegistry) { specs := []ActionSpec{ {Key: "project.advisory", Domain: "project", Object: "project", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryProgress, CapabilityKey: "QUERY_PROGRESS", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "project.fact.supplement", Domain: "project", Object: "project_fact", Goal: GoalMutate, Risk: RiskMedium, LegacyIntent: IntentSupplementInfo, CapabilityKey: "SUPPLEMENT_INFO", DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "project.stage.advance", Domain: "project", Object: "project", Goal: GoalControl, Risk: RiskHigh, LegacyIntent: IntentAdvanceStage, CapabilityKey: "ADVANCE_STAGE", RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand, DialogueActConfirm}}, {Key: "task.run", Domain: "project", Object: "task", Goal: GoalGenerate, Risk: RiskMedium, LegacyIntent: IntentTriggerTask, CapabilityKey: "TRIGGER_TASK", DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "task.run.adhoc", Domain: "project", Object: "task", Goal: GoalGenerate, Risk: RiskMedium, LegacyIntent: IntentTriggerAdhocTask, CapabilityKey: "TRIGGER_ADHOC_TASK"}, {Key: "conversation.chat", Domain: "conversation", Object: "system_ui", Goal: GoalConverse, Risk: RiskLow, LegacyIntent: IntentCasualChat}, {Key: "conversation.decline", Domain: "conversation", Object: "system_ui", Goal: GoalDecide, Risk: RiskLow, LegacyIntent: IntentDeclineExpectation, DialogueActs: []DialogueAct{DialogueActDecline}}, {Key: "document.claim", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskMedium, LegacyIntent: IntentDocGovClaim, CapabilityKey: "DOCGOV_CLAIM", DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "document.review.submit", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskMedium, LegacyIntent: IntentDocGovSubmitReview, CapabilityKey: "DOCGOV_SUBMIT_REVIEW", DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "document.review.approve", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskHigh, LegacyIntent: IntentDocGovReviewApprove, RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand, DialogueActConfirm}}, {Key: "document.review.reject", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskHigh, LegacyIntent: IntentDocGovReviewReject, RequiresConfirm: true, RequiredSlots: []SlotSpec{{Key: SlotRejectReason, Required: true, Label: "驳回理由"}}, DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "document.final.approve", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskHigh, LegacyIntent: IntentDocGovFinalApprove, RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand, DialogueActConfirm}}, {Key: "document.final.reject", Domain: "document_gov", Object: "controlled_document", Goal: GoalMutate, Risk: RiskHigh, LegacyIntent: IntentDocGovFinalReject, RequiresConfirm: true, RequiredSlots: []SlotSpec{{Key: SlotRejectReason, Required: true, Label: "驳回理由"}}, DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "document.proposal.decide", Domain: "document_gov", Object: "controlled_document", Goal: GoalDecide, Risk: RiskMedium, LegacyIntent: IntentDocGovProposalDecide, DialogueActs: []DialogueAct{DialogueActConfirm, DialogueActDecline}}, {Key: "regulation.query", Domain: "knowledge", Object: "regulation", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryRegulations, CapabilityKey: "QUERY_REGULATIONS", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "case.query", Domain: "knowledge", Object: "artifact", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQuerySimilarCases, CapabilityKey: "QUERY_SIMILAR_CASES", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "permission.query", Domain: "project", Object: "user_permission", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryUserRights, CapabilityKey: "QUERY_USER_RIGHTS", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "document.reject_reason.query", Domain: "document_gov", Object: "controlled_document", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryRejectReason, DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "card.confirm", Domain: "conversation", Object: "system_ui", Goal: GoalDecide, Risk: RiskHigh, LegacyIntent: IntentConfirmAction, RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActConfirm}}, {Key: "project.stage.execute", Domain: "project", Object: "project", Goal: GoalControl, Risk: RiskHigh, LegacyIntent: IntentExecuteStageAdvance, RequiresConfirm: true}, {Key: "security.forbidden", Domain: "security", Object: "system_configuration", Goal: GoalConverse, Risk: RiskHigh, LegacyIntent: IntentSecurityForbidden}, {Key: "workflow.switch", Domain: "project_workflow", Object: "workflow", Goal: GoalControl, Risk: RiskHigh, LegacyIntent: IntentSwitchWorkflow, CapabilityKey: "SWITCH_WORKFLOW", RequiresConfirm: true, RequiredSlots: []SlotSpec{{Key: SlotTargetWorkflow, Required: true, Label: "目标工作流"}}, DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "workflow.run", Domain: "project_workflow", Object: "workflow_run", Goal: GoalControl, Risk: RiskMedium, LegacyIntent: IntentTriggerWorkflow, CapabilityKey: "TRIGGER_WORKFLOW", RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "workflow.status.query", Domain: "project_workflow", Object: "workflow", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryWorkflowStatus, CapabilityKey: "QUERY_WORKFLOW", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "document.status.query", Domain: "document_gov", Object: "controlled_document", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentQueryDocStatus, CapabilityKey: "VIEW_DOC", DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "conversation.help", Domain: "conversation", Object: "system_ui", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentHelpCapabilities, DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "conversation.unsupported", Domain: "conversation", Object: "system_ui", Goal: GoalConverse, Risk: RiskLow, LegacyIntent: IntentUnsupportedAction}, {Key: "run.status.query", Domain: "project", Object: "workflow_run", Goal: GoalQuery, Risk: RiskLow, LegacyIntent: IntentRunStatusQuery, DialogueActs: []DialogueAct{DialogueActAsk}}, {Key: "run.resume", Domain: "project", Object: "workflow_run", Goal: GoalControl, Risk: RiskMedium, LegacyIntent: IntentRunResume, RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand}}, {Key: "run.restart", Domain: "project", Object: "workflow_run", Goal: GoalControl, Risk: RiskHigh, LegacyIntent: IntentRunRestart, RequiresConfirm: true, DialogueActs: []DialogueAct{DialogueActCommand}}, } for _, s := range specs { _ = r.Register(s) } }