package squad import ( "fmt" "os" "path/filepath" "strings" "time" ) // WorkspacePrepareRequest 工作区准备请求 type WorkspacePrepareRequest struct { WorkDir string SessionID FlexSessionID ProjectID int64 TaskKey string StageTitle string ProjectName string CustomerName string DeviceType string IntendedUse string TargetMarket string Backend string TemplateContent string Materials []MaterialItem KnowledgeHits []KnowledgeHit PriorDocs []PriorDocItem UserContext string ProjectContext string AgentContext string MemoryBankFiles map[string]string // DialoguePackage is optional layered project dialogue (digest/turns/facts). DialoguePackage *DialoguePackage CustomAgents []AgentSpec WorkflowSteps []StepSpec InitialFields map[string]string Metadata map[string]string } // DialoguePackage is the squad-facing dialogue injection payload. type DialoguePackage struct { DigestMD string TurnsMD string FactsJSON []byte ConfirmationsMD string TurnCount int ExcludedPrivate int ExcludedNoAccess int ExcludedNoise int } // Workspace 表示已初始化的工作区句柄 type Workspace struct { WorkDir string Meta WorkspaceMetadata Squad SquadConfig Workflow WorkflowPlan } // WorkspaceEngine 工作区生命周期与沙箱构建引擎 type WorkspaceEngine struct { assets AssetProvider } func NewWorkspaceEngine(assets AssetProvider) *WorkspaceEngine { return &WorkspaceEngine{assets: assets} } // PrepareWorkspace 初始化自包含工作区目录 func (e *WorkspaceEngine) PrepareWorkspace(req WorkspacePrepareRequest) (*Workspace, error) { workDir := req.WorkDir if workDir == "" { ts := time.Now().UnixNano() workDir = filepath.Join("data", "dev", "workspaces", "bizdev", fmt.Sprintf("task_%s_%d", req.TaskKey, ts)) } if err := os.MkdirAll(workDir, 0o755); err != nil { return nil, fmt.Errorf("create workspace root dir: %w", err) } now := time.Now() wsID := fmt.Sprintf("ws_%s_%d", req.TaskKey, now.UnixNano()) if req.Metadata != nil && req.Metadata["id"] != "" { wsID = req.Metadata["id"] } // 1. 初始化 workspace.json meta := WorkspaceMetadata{ ID: wsID, SessionID: req.SessionID, ProjectID: req.ProjectID, TaskKey: req.TaskKey, StageTitle: req.StageTitle, ProjectName: req.ProjectName, CustomerName: req.CustomerName, DeviceType: req.DeviceType, IntendedUse: req.IntendedUse, TargetMarket: req.TargetMarket, Status: "created", Backend: req.Backend, WorkDir: workDir, CreatedAt: now, UpdatedAt: now, Metadata: req.Metadata, } if err := writeJSON(filepath.Join(workDir, "workspace.json"), meta); err != nil { return nil, fmt.Errorf("write workspace.json: %w", err) } // 2. 初始化自适应小队与工作流规划 var squadCfg SquadConfig var wf WorkflowPlan tmplContent := req.TemplateContent if len(req.WorkflowSteps) > 0 { agents := req.CustomAgents if len(agents) == 0 { agents = DefaultSquadAgents(req.TaskKey) } squadCfg = SquadConfig{ SquadName: fmt.Sprintf("%s_squad", req.TaskKey), Version: "1.0.0", Agents: agents, } steps := req.WorkflowSteps for idx := range steps { if steps[idx].Status == "" { steps[idx].Status = "pending" } } wf = WorkflowPlan{ WorkflowID: fmt.Sprintf("wf_%s_%d", req.TaskKey, now.UnixNano()), TaskKey: req.TaskKey, Title: req.StageTitle, Status: "pending", CurrentStepIndex: 0, Steps: steps, } } else { planner := NewAdaptivePlanner(nil, e.assets) pRes, pErr := planner.Plan(PlannerRequest{ TaskKey: req.TaskKey, Title: req.StageTitle, ProjectName: req.ProjectName, CustomerName: req.CustomerName, DeviceType: req.DeviceType, IntendedUse: req.IntendedUse, TargetMarket: req.TargetMarket, Materials: req.Materials, KnowledgeHits: req.KnowledgeHits, Fields: req.InitialFields, CustomSteps: req.WorkflowSteps, }) if pErr == nil && pRes != nil { squadCfg = pRes.Squad wf = pRes.Workflow if strings.TrimSpace(tmplContent) == "" { tmplContent = pRes.TemplateContent } } else { agents := req.CustomAgents if len(agents) == 0 { agents = DefaultSquadAgents(req.TaskKey) } squadCfg = SquadConfig{ SquadName: fmt.Sprintf("%s_squad", req.TaskKey), Version: "1.0.0", Agents: agents, } steps := req.WorkflowSteps if len(steps) == 0 { steps = DefaultWorkflowSteps(req.TaskKey) } wf = WorkflowPlan{ WorkflowID: fmt.Sprintf("wf_%s_%d", req.TaskKey, now.UnixNano()), Status: "pending", CurrentStepIndex: 0, Steps: steps, } } } if err := writeJSON(filepath.Join(workDir, "squad.json"), squadCfg); err != nil { return nil, fmt.Errorf("write squad.json: %w", err) } if err := writeJSON(filepath.Join(workDir, "workflow.json"), wf); err != nil { return nil, fmt.Errorf("write workflow.json: %w", err) } // 4. 写入自描述模板 template.md if strings.TrimSpace(tmplContent) == "" && e.assets != nil { var err error tmplContent, err = e.assets.DocTemplate(req.TaskKey) if err != nil { tmplContent = "" } } if strings.TrimSpace(tmplContent) == "" { tmplContent = fmt.Sprintf("# %s\n\n## 1. 结论\n\n待确认\n\n## 2. 正文\n\n待确认\n", req.StageTitle) } vars := map[string]string{ "project_name": req.ProjectName, "product_name": req.ProjectName, "customer_name": req.CustomerName, "device_type": req.DeviceType, "intended_use": req.IntendedUse, "target_market": req.TargetMarket, "generated_at": now.Format("2006-01-02 15:04:05"), "snapshot_id": wsID, } for k, v := range req.InitialFields { vars[k] = v } var renderedTmpl string if e.assets != nil { renderedTmpl = e.assets.RenderDoc(tmplContent, vars) } else { renderedTmpl = tmplContent } if err := os.WriteFile(filepath.Join(workDir, "template.md"), []byte(renderedTmpl), 0o644); err != nil { return nil, fmt.Errorf("write template.md: %w", err) } // 5. 初始化 blackboard.json bb := BlackboardState{ Facts: make([]FactItem, 0), Gaps: make([]GapItem, 0), Implications: make([]ImplicationItem, 0), Assumptions: make([]AssumptionItem, 0), Fields: make(map[string]string), UpdatedAt: now, } if req.CustomerName != "" { bb.Fields["customer_name"] = req.CustomerName bb.Facts = append(bb.Facts, FactItem{ ID: "fact_01", Claim: "客户名称为 " + req.CustomerName, Source: "用户输入", Verified: true, }) } if req.ProjectName != "" { bb.Fields["project_name"] = req.ProjectName } if req.DeviceType != "" { bb.Fields["device_type"] = req.DeviceType } if req.IntendedUse != "" { bb.Fields["intended_use"] = req.IntendedUse } if req.TargetMarket != "" { bb.Fields["target_market"] = req.TargetMarket } for k, v := range req.InitialFields { bb.Fields[k] = v } if err := writeJSON(filepath.Join(workDir, "blackboard.json"), bb); err != nil { return nil, fmt.Errorf("write blackboard.json: %w", err) } // 6. 构造自描述上下文体系 context/ if err := e.buildContextSystem(workDir, req); err != nil { return nil, fmt.Errorf("build context system: %w", err) } // 7. 创建子工作区 tasks/ 并注入 TASK.md 与 AGENTS.md if err := e.buildSubWorkspaces(workDir, wf, squadCfg, renderedTmpl); err != nil { return nil, fmt.Errorf("build sub workspaces: %w", err) } // 8. 创建 artifacts/ 与基础日志 _ = os.MkdirAll(filepath.Join(workDir, "artifacts"), 0o755) logEntry := fmt.Sprintf("[%s] 工作区创建完成: ID=%s, Task=%s\n", now.Format(time.RFC3339), wsID, req.TaskKey) _ = os.WriteFile(filepath.Join(workDir, "execution.log"), []byte(logEntry), 0o644) return &Workspace{ WorkDir: workDir, Meta: meta, Squad: squadCfg, Workflow: wf, }, nil }