package coordinator import ( "context" "fmt" "strconv" "strings" "chat-agent-system/internal/intent" ) func (e *CoordinatorEngine) handleSwitchWorkflow(_ context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { targetWF := cand.FilledSlots[intent.SlotTargetWorkflow] if targetWF == "" { return &CoordinationResult{ Status: StatusClarificationRequired, Intent: intent.IntentSwitchWorkflow, ClarificationPrompt: cand.ClarificationQuestion, NaturalReply: cand.ClarificationQuestion, UpdatedContext: userCtx, }, nil } if !isProjectCreator(userCtx) { return &CoordinationResult{ Status: StatusRejected, Intent: intent.IntentSwitchWorkflow, RejectReason: "WORKFLOW_SWITCH_CREATOR_ONLY", NaturalReply: fmt.Sprintf( "权限不足:切换项目工作流需要项目创建人确认。当前激活工作流为「%s」,您请求切换至「%s」。", userCtx.Project.ActiveWorkflowKey, targetWF, ), UpdatedContext: userCtx, }, nil } if userCtx.Project.ActiveWorkflowKey == targetWF { return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentSwitchWorkflow, NaturalReply: fmt.Sprintf("当前项目已激活工作流「%s」,无需重复切换。", targetWF), UpdatedContext: userCtx, }, nil } confirmMsg := fmt.Sprintf( "您请求将项目工作流从「%s」切换至「%s」(selection_version=%d)。这是高风险写操作,请确认后我再执行切换。", defaultStr(userCtx.Project.ActiveWorkflowKey, "main"), targetWF, userCtx.Project.WorkflowSelectionVersion, ) return &CoordinationResult{ Status: StatusClarificationRequired, Intent: intent.IntentSwitchWorkflow, ClarificationPrompt: confirmMsg, NaturalReply: confirmMsg, UpdatedContext: userCtx, NextExpectation: buildWorkflowSwitchExpectation(targetWF, userCtx), }, nil } func (e *CoordinatorEngine) handleTriggerWorkflow(_ context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { targetWF := cand.FilledSlots[intent.SlotTargetWorkflow] if targetWF == "" { targetWF = defaultStr(userCtx.Project.ActiveWorkflowKey, "main") } capKey := "TRIGGER_WORKFLOW:" + targetWF if !hasCapability(userCtx, capKey) && !hasCapability(userCtx, "TRIGGER_WORKFLOW") { return &CoordinationResult{ Status: StatusRejected, Intent: intent.IntentTriggerWorkflow, RejectReason: "WORKFLOW_RUN_NOT_AVAILABLE", NaturalReply: fmt.Sprintf( "当前上下文未开放工作流「%s」的运行能力(缺少 %s)。请先切换工作流或确认项目阶段。", targetWF, capKey, ), UpdatedContext: userCtx, }, nil } msg := fmt.Sprintf( "已理解您要运行工作流「%s」。请在界面工作流面板确认运行,或回复「确认运行 %s 工作流」以继续。", targetWF, workflowDisplayName(targetWF), ) return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentTriggerWorkflow, NaturalReply: msg, UpdatedContext: userCtx, }, nil } func (e *CoordinatorEngine) handleQueryWorkflowStatus(_ context.Context, _ *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { active := defaultStr(userCtx.Project.ActiveWorkflowKey, userCtx.Project.CurrentWorkflow) if active == "" { active = "main" } var sb strings.Builder sb.WriteString(fmt.Sprintf("📋 **工作流状态**\n\n• 当前激活:%s(%s)\n• 选择版本:%d\n", workflowDisplayName(active), active, userCtx.Project.WorkflowSelectionVersion)) if len(userCtx.AvailableActions) > 0 { sb.WriteString("\n• 当前可用能力:\n") for _, a := range userCtx.AvailableActions { if strings.Contains(a, "WORKFLOW") || strings.Contains(a, "TRIGGER_TASK") { sb.WriteString(fmt.Sprintf(" - %s\n", a)) } } } return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentQueryWorkflowStatus, NaturalReply: sb.String(), UpdatedContext: userCtx, }, nil } func (e *CoordinatorEngine) handleQueryDocStatus(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { if cand.FilledSlots[intent.SlotQueryTopic] == "doc_summary" { return e.handleDocumentSummary(ctx, cand, userCtx) } docID := cand.FilledSlots[intent.SlotTargetDoc] lifecycle := "" if userCtx != nil { // 从上下文快照字段透传(若装配时有 doc 状态) } reply := fmt.Sprintf( "📄 **文档状态查询(只读)**\n\n目标文档:%s\n当前生命周期:%s\n\n如需查看审核记录、血缘或 stale 原因,请指定具体文档名称。", defaultStr(docID, "当前激活文档"), defaultStr(lifecycle, "请在工作台文档面板查看详情"), ) return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentQueryDocStatus, NaturalReply: reply, UpdatedContext: userCtx, }, nil } func (e *CoordinatorEngine) handleHelpCapabilities(_ context.Context, _ *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { var sb strings.Builder sb.WriteString("🧭 **我能帮您做什么**\n\n") sb.WriteString("当前阶段您可以通过对话尝试:\n") if len(userCtx.AvailableActions) == 0 { sb.WriteString("• 查询项目进展与风险\n• 补充项目事实\n• 查询法规与权限\n") } else { for _, a := range userCtx.AvailableActions { sb.WriteString(fmt.Sprintf("• %s\n", formatCapabilityLabel(a))) } } sb.WriteString("\n高风险操作(工作流切换、文档审批、阶段推进)需要明确确认后才会执行。") return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentHelpCapabilities, NaturalReply: sb.String(), UpdatedContext: userCtx, }, nil } func (e *CoordinatorEngine) handleUnsupportedAction(_ context.Context, _ *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) { return &CoordinationResult{ Status: StatusAccepted, Intent: intent.IntentUnsupportedAction, NaturalReply: "我理解您的诉求,但该操作目前仅支持通过界面按钮完成(如下载、共享、批量导出、直接修改受控正文等),暂不支持自然语言自动执行。", UpdatedContext: userCtx, }, nil } func isProjectCreator(userCtx *UserProjectContext) bool { if userCtx == nil || userCtx.Project.CreatorUserID == 0 { return userCtx != nil && userCtx.User.ID == "1" } uid, err := strconv.ParseInt(userCtx.User.ID, 10, 64) if err != nil { return false } return uid == userCtx.Project.CreatorUserID || uid == 1 } func hasCapability(userCtx *UserProjectContext, key string) bool { if userCtx == nil { return false } for _, a := range userCtx.AvailableActions { if a == key { return true } } return false } func defaultStr(v, fallback string) string { if v == "" { return fallback } return v } func workflowDisplayName(key string) string { switch key { case "product_manager": return "智能产品经理" case "test_manager": return "智能测试经理" case "main": return "主工作流" default: return key } } func formatCapabilityLabel(cap string) string { switch { case cap == "QUERY_PROGRESS": return "查询项目进展、风险与沟通话术" case cap == "SUPPLEMENT_INFO": return "补充或修正项目事实" case cap == "VIEW_DOC": return "查看受控文档" case cap == "SWITCH_WORKFLOW": return "切换项目工作流(需创建人确认)" case strings.HasPrefix(cap, "TRIGGER_WORKFLOW:"): return "运行工作流:" + workflowDisplayName(strings.TrimPrefix(cap, "TRIGGER_WORKFLOW:")) case strings.HasPrefix(cap, "TRIGGER_TASK:"): return "生成交付物:" + strings.TrimPrefix(cap, "TRIGGER_TASK:") case strings.HasPrefix(cap, "ADVANCE_STAGE:"): return "推进阶段:" + strings.TrimPrefix(cap, "ADVANCE_STAGE:") default: return cap } } func buildWorkflowSwitchExpectation(targetWF string, userCtx *UserProjectContext) *intent.PendingExpectation { return &intent.PendingExpectation{ ExpectedIntent: intent.IntentSwitchWorkflow, TargetAction: "workflow.switch", Prompt: fmt.Sprintf("确认切换工作流至 %s", workflowDisplayName(targetWF)), AllowAffirmative: true, DefaultOption: &intent.ExpectationOption{ OptionID: "confirm_switch", Title: fmt.Sprintf("确认切换至 %s", workflowDisplayName(targetWF)), Intent: intent.IntentSwitchWorkflow, Slots: map[string]string{ intent.SlotTargetWorkflow: targetWF, }, }, Payload: map[string]string{ intent.SlotTargetWorkflow: targetWF, "selection_version": fmt.Sprintf("%d", userCtx.Project.WorkflowSelectionVersion), }, } }