feat(squad): define workspace directory metadata models and engine

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WeiCHEN
2026-09-13 02:50:18 +00:00
committed by weichen
co-authored by Cursor
parent 29e00db0d9
commit 6bbbb16a1c
2 changed files with 901 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
package squad
import "time"
// WorkspaceMetadata 记录工作区全局元数据
type WorkspaceMetadata struct {
ID string `json:"id"`
SessionID int64 `json:"session_id"`
ProjectID int64 `json:"project_id"`
TaskKey string `json:"task_key"`
StageTitle string `json:"stage_title"`
ProjectName string `json:"project_name"`
CustomerName string `json:"customer_name"`
DeviceType string `json:"device_type,omitempty"`
IntendedUse string `json:"intended_use,omitempty"`
TargetMarket string `json:"target_market,omitempty"`
Status string `json:"status"` // created, running, completed, failed
Backend string `json:"backend"`
WorkDir string `json:"work_dir"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// AgentSpec 声明单个专员角色在小队中的设定
type AgentSpec struct {
Role string `json:"role"`
Name string `json:"name"`
Model string `json:"model"`
Tools []string `json:"tools"`
BashPermission string `json:"bash_permission"` // allow, deny
Responsibilities []string `json:"responsibilities"`
ForbiddenActions []string `json:"forbidden_actions"`
InstructionsFile string `json:"instructions_file,omitempty"`
}
// SquadConfig 激活的智能体小队编制
type SquadConfig struct {
SquadName string `json:"squad_name"`
Version string `json:"version"`
Agents []AgentSpec `json:"agents"`
}
// StepSpec 单个任务步骤规格定义
type StepSpec struct {
ID string `json:"id"`
Role string `json:"role"`
Title string `json:"title"`
SubWorkspace string `json:"sub_workspace"` // 相对子路径,如 tasks/01_organize
Mode string `json:"mode"` // execute, plan, review_plan, replan
Parallel bool `json:"parallel"`
Needs []string `json:"needs,omitempty"`
Accept []string `json:"accept,omitempty"`
Status string `json:"status"` // pending, running, completed, failed, skipped
DurationMs int64 `json:"duration_ms,omitempty"`
Error string `json:"error,omitempty"`
}
// WorkflowPlan 动态任务编排清单
type WorkflowPlan struct {
WorkflowID string `json:"workflow_id"`
Status string `json:"status"` // pending, in_progress, completed, failed
CurrentStepIndex int `json:"current_step_index"`
Steps []StepSpec `json:"steps"`
}
// ContextItem 上下文项目元数据
type ContextItem struct {
File string `json:"file"` // 相对路径,如 context/user.md
Description string `json:"description"` // 用途说明
AuthorityLevel string `json:"authority_level"` // authoritative, primary_evidence, reference_standard, supplementary, inferred
Guidance string `json:"guidance"` // 使用建议与优先级说明
}
// ContextIndex 上下文索引表
type ContextIndex struct {
Items []ContextItem `json:"context_items"`
}
// MaterialItem 输入材料
type MaterialItem struct {
Filename string `json:"filename"`
Content []byte `json:"content"`
}
// KnowledgeHit 法规知识库检索命中条目
type KnowledgeHit struct {
DocCode string `json:"doc_code"`
Category string `json:"category,omitempty"` // standards, guidance, laws, catalog
Title string `json:"title"`
Content string `json:"content"`
Source string `json:"source"`
}
// PriorDocItem 前序阶段归档文档
type PriorDocItem struct {
DocKey string `json:"doc_key"`
Title string `json:"title"`
Filename string `json:"filename"`
Content string `json:"content"`
Stage string `json:"stage"`
}
// FactItem 黑板事实项
type FactItem struct {
ID string `json:"id"`
Claim string `json:"claim"`
Source string `json:"source"`
Verified bool `json:"verified"`
}
// GapItem 黑板缺口项
type GapItem struct {
ID string `json:"id"`
Item string `json:"item"`
Owner string `json:"owner"` // user, agent
Blocking bool `json:"blocking"` // 是否阻塞后续推进
Resolved bool `json:"resolved"`
}
// ImplicationItem 黑板含义项
type ImplicationItem struct {
ID string `json:"id"`
Topic string `json:"topic"`
Statement string `json:"statement"`
}
// BlackboardState 结构化共享黑板
type BlackboardState struct {
Facts []FactItem `json:"facts"`
Gaps []GapItem `json:"gaps"`
Implications []ImplicationItem `json:"implications"`
Fields map[string]string `json:"fields"`
UpdatedAt time.Time `json:"updated_at"`
}
// QualityReport 自动化质量门禁报告
type QualityReport struct {
Passed bool `json:"passed"`
Score float64 `json:"score"`
TitleCheck bool `json:"title_check"`
MissingSections []string `json:"missing_sections,omitempty"`
ProhibitedPhrases []string `json:"prohibited_phrases,omitempty"`
QuoteMatches bool `json:"quote_matches"`
Violations []string `json:"violations,omitempty"`
CheckedAt time.Time `json:"checked_at"`
}
// Manifest 执行清单与防篡改审计
type Manifest struct {
ManifestVersion string `json:"manifest_version"`
WorkspaceID string `json:"workspace_id"`
DocKey string `json:"doc_key"`
GeneratedAt time.Time `json:"generated_at"`
Deliverable string `json:"deliverable"`
Files map[string]string `json:"files"` // relPath -> sha256
QualityReport QualityReport `json:"quality_report"`
DurationMs int64 `json:"duration_ms"`
}
+741
View File
@@ -0,0 +1,741 @@
package squad
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"chat-agent-system/internal/template"
)
// WorkspacePrepareRequest 工作区准备请求
type WorkspacePrepareRequest struct {
WorkDir string
SessionID int64
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
CustomAgents []AgentSpec
WorkflowSteps []StepSpec
InitialFields map[string]string
Metadata map[string]string
}
// Workspace 表示已初始化的工作区句柄
type Workspace struct {
WorkDir string
Meta WorkspaceMetadata
Squad SquadConfig
Workflow WorkflowPlan
}
// WorkspaceEngine 工作区生命周期与沙箱构建引擎
type WorkspaceEngine struct{}
func NewWorkspaceEngine() *WorkspaceEngine {
return &WorkspaceEngine{}
}
// 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. 初始化 squad.json
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,
}
if err := writeJSON(filepath.Join(workDir, "squad.json"), squadCfg); err != nil {
return nil, fmt.Errorf("write squad.json: %w", err)
}
// 3. 初始化 workflow.json
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, "workflow.json"), wf); err != nil {
return nil, fmt.Errorf("write workflow.json: %w", err)
}
// 4. 写入自描述模板 template.md
tmplContent := req.TemplateContent
if strings.TrimSpace(tmplContent) == "" {
var err error
tmplContent, err = template.GetDocTemplate(req.TaskKey)
if err != nil {
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
}
renderedTmpl := template.RenderDoc(tmplContent, vars)
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),
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/<sub_dir> 并注入 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
}
// buildContextSystem 构建具有权威度说明的 context/ 体系
func (e *WorkspaceEngine) buildContextSystem(workDir string, req WorkspacePrepareRequest) error {
ctxDir := filepath.Join(workDir, "context")
_ = os.MkdirAll(filepath.Join(ctxDir, "materials_text"), 0o755)
_ = os.MkdirAll(filepath.Join(ctxDir, "knowledge"), 0o755)
_ = os.MkdirAll(filepath.Join(ctxDir, "prior_docs"), 0o755)
_ = os.MkdirAll(filepath.Join(ctxDir, "memory"), 0o755)
var contextItems []ContextItem
// 1. user.md
userContent := req.UserContext
if strings.TrimSpace(userContent) == "" {
userContent = fmt.Sprintf("# 用户角色与偏好\n\n- 用户角色: 商务与技术负责人\n- 决策风格: 严谨可追溯,注重四段式论据\n- 授权边界: 预算与商业承诺必须经由用户拍板\n")
}
_ = os.WriteFile(filepath.Join(ctxDir, "user.md"), []byte(userContent), 0o644)
contextItems = append(contextItems, ContextItem{
File: "context/user.md",
Description: "操作用户的业务角色、决策授权边界与协同偏好",
AuthorityLevel: "authoritative",
Guidance: "必须尊重用户设定的目标市场与边界,用户未决事项一律列入待确认清单,严禁越权替用户拍板",
})
// 2. project.md
projContent := req.ProjectContext
if strings.TrimSpace(projContent) == "" {
projContent = fmt.Sprintf("# 项目全局基线\n\n- 项目名称: %s\n- 客户名称: %s\n- 产品类型: %s\n- 预期用途: %s\n- 目标市场: %s\n",
req.ProjectName, req.CustomerName, req.DeviceType, req.IntendedUse, req.TargetMarket)
}
_ = os.WriteFile(filepath.Join(ctxDir, "project.md"), []byte(projContent), 0o644)
contextItems = append(contextItems, ContextItem{
File: "context/project.md",
Description: "项目立项章程与全局事实基线",
AuthorityLevel: "authoritative",
Guidance: "本项目核心前提。所有后续分析论证不得推翻已锁定的项目名称与核心预期用途",
})
// 3. agent.md
agentContent := req.AgentContext
if strings.TrimSpace(agentContent) == "" {
agentContent = "# 智能体小队协同通用规范\n\n- 事实闭环: 严禁凭空捏造未提供之参数或法规\n- 引用规范: 引用必须标注出处〔材料:...〕或〔法规:...〕\n- 负向红线: 禁止敷衍写作、禁止'不做展开'、'从略'\n"
}
_ = os.WriteFile(filepath.Join(ctxDir, "agent.md"), []byte(agentContent), 0o644)
contextItems = append(contextItems, ContextItem{
File: "context/agent.md",
Description: "智能体通用协同纪律与引用要求",
AuthorityLevel: "authoritative",
Guidance: "所有小队角色必须严格执行的专业守则与写作禁令",
})
// 4. materials_text/
if len(req.Materials) > 0 {
var allBuilder strings.Builder
for _, m := range req.Materials {
name := filepath.Base(m.Filename)
if name == "" || name == "." {
name = "material.txt"
}
p := filepath.Join(ctxDir, "materials_text", name)
_ = os.WriteFile(p, m.Content, 0o644)
// Also write to root materials_text/ for backward compatibility
rootMatDir := filepath.Join(workDir, "materials_text")
_ = os.MkdirAll(rootMatDir, 0o755)
_ = os.WriteFile(filepath.Join(rootMatDir, name), m.Content, 0o644)
allBuilder.WriteString(fmt.Sprintf("\n\n=== 文件: %s ===\n\n", name))
allBuilder.Write(m.Content)
contextItems = append(contextItems, ContextItem{
File: filepath.ToSlash(filepath.Join("context", "materials_text", name)),
Description: fmt.Sprintf("用户上传的输入材料文本: %s", name),
AuthorityLevel: "primary_evidence",
Guidance: "核心原始证据。提取参数需标明出处;材料内部矛盾需在矛盾表中并列呈现",
})
}
_ = os.WriteFile(filepath.Join(ctxDir, "materials_text", "_all.txt"), []byte(allBuilder.String()), 0o644)
}
// 5. knowledge/
if len(req.KnowledgeHits) > 0 {
var kbBuilder strings.Builder
kbBuilder.WriteString("# 法规与标准检索条文摘录\n\n")
for _, hit := range req.KnowledgeHits {
kbBuilder.WriteString(fmt.Sprintf("### [%s] %s %s\n\n%s\n\n> 来源: %s\n\n---\n\n",
strings.ToUpper(hit.Category), hit.DocCode, hit.Title, hit.Content, hit.Source))
}
_ = os.WriteFile(filepath.Join(ctxDir, "knowledge", "hits.md"), []byte(kbBuilder.String()), 0o644)
contextItems = append(contextItems, ContextItem{
File: "context/knowledge/hits.md",
Description: "法规知识库中命中的医疗器械分类、国家标准与指导原则",
AuthorityLevel: "reference_standard",
Guidance: "合规与技术论据源。引用必须带标准号;涉及审批结论需标注'需向药监局审评中心确认'",
})
}
// 6. prior_docs/
if len(req.PriorDocs) > 0 {
var indexBuilder strings.Builder
indexBuilder.WriteString("# 前序阶段交付物索引\n\n")
for _, doc := range req.PriorDocs {
filename := doc.Filename
if filename == "" {
filename = doc.DocKey + ".md"
}
p := filepath.Join(ctxDir, "prior_docs", filename)
_ = os.WriteFile(p, []byte(doc.Content), 0o644)
indexBuilder.WriteString(fmt.Sprintf("- [%s] `%s`: %s (阶段: %s)\n", doc.DocKey, filename, doc.Title, doc.Stage))
contextItems = append(contextItems, ContextItem{
File: filepath.ToSlash(filepath.Join("context", "prior_docs", filename)),
Description: fmt.Sprintf("前序已归档文档: %s (%s)", doc.Title, doc.DocKey),
AuthorityLevel: "primary_evidence",
Guidance: "前序阶段已批准的结论基准。本阶段内容应与前序结论保持自洽",
})
}
_ = os.WriteFile(filepath.Join(ctxDir, "prior_docs", "index.md"), []byte(indexBuilder.String()), 0o644)
}
// 7. memory/
if len(req.MemoryBankFiles) > 0 {
for k, v := range req.MemoryBankFiles {
p := filepath.Join(ctxDir, "memory", k)
_ = os.WriteFile(p, []byte(v), 0o644)
}
} else {
// 默认四大核心资产骨架
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "projectbrief.md"), []byte(fmt.Sprintf("# 项目章程\n- 项目: %s\n- 客户: %s\n", req.ProjectName, req.CustomerName)), 0o644)
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "activeContext.md"), []byte(fmt.Sprintf("# 当前焦点\n- 阶段: %s\n- 时间: %s\n", req.TaskKey, time.Now().Format("2006-01-02 15:04"))), 0o644)
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "systemPatterns.md"), []byte("# 规范与模式\n- 遵循四段式结构与严格角色边界\n- 单一真实源原则\n"), 0o644)
_ = os.WriteFile(filepath.Join(ctxDir, "memory", "progress.md"), []byte(fmt.Sprintf("# 进度\n- 当前正在进行: %s\n", req.StageTitle)), 0o644)
}
contextItems = append(contextItems, ContextItem{
File: "context/memory/",
Description: "长期项目记忆银行 (ActiveContext, Progress, ProjectBrief, SystemPatterns)",
AuthorityLevel: "supplementary",
Guidance: "协同上下文背景参考,防止上下文遗忘",
})
// 8. 写入 context/index.json
ctxIndex := ContextIndex{Items: contextItems}
if err := writeJSON(filepath.Join(ctxDir, "index.json"), ctxIndex); err != nil {
return err
}
// 9. 写入 context/README.md 指引
readmeContent := `# 上下文使用指引 (Context Reading Guide)
本目录为当前智能体任务的自包含上下文沙箱。所有智能体专员在执行任务时,必须按照以下权威度等级(Authority Level)采纳信息:
| 权威度级别 | 适用文件类别 | 采信与处理规则 |
|---|---|---|
| **authoritative** | ` + "`user.md`" + `, ` + "`project.md`" + `, ` + "`agent.md`" + ` | **最高权威**。必须绝对遵循;严禁越权替用户拍板,未决项列入待确认清单 |
| **primary_evidence** | ` + "`materials_text/*`" + `, ` + "`prior_docs/*`" + ` | **原始事实证据**。引用必须标明出处条文;若材料间存在矛盾,并列呈现 |
| **reference_standard** | ` + "`knowledge/hits.md`" + ` | **标准与法规依据**。必须指明法规/标准代码;涉及监管裁定标注需窗口核实 |
| **supplementary** | ` + "`memory/*`" + ` | **补充记忆背景**。仅供背景了解,不作为唯一支撑证据 |
---
`
return os.WriteFile(filepath.Join(ctxDir, "README.md"), []byte(readmeContent), 0o644)
}
// buildSubWorkspaces 为 workflow 中的各个子任务构建独立沙箱
func (e *WorkspaceEngine) buildSubWorkspaces(workDir string, wf WorkflowPlan, squadCfg SquadConfig, renderedTmpl string) error {
agentMap := make(map[string]AgentSpec)
for _, a := range squadCfg.Agents {
agentMap[a.Role] = a
}
for _, step := range wf.Steps {
subDir := filepath.Join(workDir, filepath.FromSlash(step.SubWorkspace))
if err := os.MkdirAll(subDir, 0o755); err != nil {
return err
}
// 1. 生成 AGENTS.md
var agentsBuilder strings.Builder
inst, err := template.GetAgentInstruction(step.Role)
if err == nil {
agentsBuilder.WriteString(inst)
} else {
spec := agentMap[step.Role]
agentsBuilder.WriteString(fmt.Sprintf("# 角色: %s (%s)\n\n", spec.Name, spec.Role))
if len(spec.Responsibilities) > 0 {
agentsBuilder.WriteString("## 核心职责\n")
for _, r := range spec.Responsibilities {
agentsBuilder.WriteString(fmt.Sprintf("- %s\n", r))
}
agentsBuilder.WriteString("\n")
}
if len(spec.ForbiddenActions) > 0 {
agentsBuilder.WriteString("## 严禁行为(红线)\n")
for _, f := range spec.ForbiddenActions {
agentsBuilder.WriteString(fmt.Sprintf("- ❌ %s\n", f))
}
agentsBuilder.WriteString("\n")
}
}
_ = os.WriteFile(filepath.Join(subDir, "AGENTS.md"), []byte(agentsBuilder.String()), 0o644)
_ = os.WriteFile(filepath.Join(subDir, "INSTRUCTIONS.md"), []byte(agentsBuilder.String()), 0o644)
// 2. 生成 TASK.md
deliverableName := "output.md"
if step.Mode == "plan" || step.Mode == "replan" {
deliverableName = "output_plan.md"
} else if step.Mode == "review_plan" {
deliverableName = "review_notes.md"
}
var taskBuilder strings.Builder
taskBuilder.WriteString(fmt.Sprintf("# 任务执行指令: %s\n\n", step.Title))
taskBuilder.WriteString(fmt.Sprintf("- **步骤 ID**: `%s`\n", step.ID))
taskBuilder.WriteString(fmt.Sprintf("- **分配角色**: `%s`\n", step.Role))
taskBuilder.WriteString(fmt.Sprintf("- **执行模式**: `%s`\n", step.Mode))
taskBuilder.WriteString(fmt.Sprintf("- **交付产物**: `%s`\n\n", deliverableName))
taskBuilder.WriteString("## 环境与输入导航\n")
taskBuilder.WriteString("请仔细查阅上级目录资源:\n")
taskBuilder.WriteString("- `../../context/index.json` 及 `../../context/README.md`:阅读上下文指引\n")
taskBuilder.WriteString("- `../../template.md`:自描述交付模板(嵌入四段式与格式约束)\n")
taskBuilder.WriteString("- `../../blackboard.json`:前序专员抽取的结构化事实与缺口\n")
taskBuilder.WriteString("- `../../context/materials_text/`:原始材料全文\n")
taskBuilder.WriteString("- `../../context/knowledge/`:法规与标准命中快照\n\n")
if len(step.Needs) > 0 {
taskBuilder.WriteString("## 前序依赖产出\n")
for _, n := range step.Needs {
taskBuilder.WriteString(fmt.Sprintf("- `../../%s`\n", filepath.ToSlash(n)))
}
taskBuilder.WriteString("\n")
}
if len(step.Accept) > 0 {
taskBuilder.WriteString("## 验收标准(Checklist)\n")
for _, a := range step.Accept {
taskBuilder.WriteString(fmt.Sprintf("- [ ] %s\n", a))
}
taskBuilder.WriteString("\n")
}
taskBuilder.WriteString("## 交付契约与写作纪律\n")
taskBuilder.WriteString(fmt.Sprintf("请在当前目录下生成完整交付物 `%s`:\n", deliverableName))
taskBuilder.WriteString("1. 严格四段式展开(结论句 → 表格/列表 → 解读 → 支撑出处)。\n")
taskBuilder.WriteString("2. 禁止使用'不做展开'、'从略'、'篇幅有限'等省略敷衍套话。\n")
taskBuilder.WriteString("3. 关键事实必须标注出处,未知项列入待确认清单。\n")
if step.Role == "organizer" {
taskBuilder.WriteString("4. 从材料提取的客观字段,请在当前工作目录下输出 `fields.json`。\n")
}
_ = os.WriteFile(filepath.Join(subDir, "TASK.md"), []byte(taskBuilder.String()), 0o644)
// 3. 写入 input_refs.json
inputRefs := map[string]any{
"step_id": step.ID,
"role": step.Role,
"needs": step.Needs,
"target_file": deliverableName,
}
_ = writeJSON(filepath.Join(subDir, "input_refs.json"), inputRefs)
}
return nil
}
// DefaultSquadAgents 根据任务类型返回默认小队成员编制
func DefaultSquadAgents(taskKey string) []AgentSpec {
switch taskKey {
case "intake":
return []AgentSpec{
{
Role: "organizer",
Name: "材料整理专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"材料事实抽取", "缺口识别", "字段结构化回填"},
ForbiddenActions: []string{"严禁测算价格", "严禁出具法规终结裁定", "严禁撰写正文终稿"},
},
{
Role: "writer",
Name: "文档撰写专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"严格四段式成文", "依据材料出处引用", "模板各章节写满"},
ForbiddenActions: []string{"严禁写不做展开或从略", "严禁随意篡改前序事实"},
},
{
Role: "leader",
Name: "商务对接负责人",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"计划审核", "正文终审", "待确认清单汇总", "对外汇报"},
ForbiddenActions: []string{"严禁代用户做商业拍板", "严禁直接交付未审核初稿"},
},
}
case "research":
return []AgentSpec{
{
Role: "searcher",
Name: "资料检索专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"公开资料检索", "法规标准核对", "出处与日期标注"},
ForbiddenActions: []string{"严禁编造URL与标准号", "未检索到须标明尝试路径"},
},
{
Role: "market",
Name: "市场与竞品分析师",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"竞品同维度对比", "市场规模测算", "机会与风险分析"},
ForbiddenActions: []string{"严禁编造市场份额与价格", "必须采用统一对比维度"},
},
{
Role: "regulatory",
Name: "医疗器械法规专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"器械分类初筛", "注册路径比对", "关键标准提炼"},
ForbiddenActions: []string{"严禁代做行政终局裁定", "必须标注需向窗口确认"},
},
{
Role: "writer",
Name: "文档撰写专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"按计划合成调研报告", "全面四段式展开"},
ForbiddenActions: []string{"严禁写不做展开或从略"},
},
{
Role: "leader",
Name: "商务对接负责人",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"审核撰写计划", "终审报告", "质量把关"},
ForbiddenActions: []string{"严禁代用户做商业拍板"},
},
}
default:
return []AgentSpec{
{
Role: "organizer",
Name: "材料整理专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"事实整理与基线对齐"},
},
{
Role: "writer",
Name: "文档撰写专员",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"正文撰写"},
},
{
Role: "leader",
Name: "商务对接负责人",
Model: "claude-3-7-sonnet",
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"终审与把关"},
},
}
}
}
// DefaultWorkflowSteps 根据任务类型返回最佳实践编排步骤
func DefaultWorkflowSteps(taskKey string) []StepSpec {
switch taskKey {
case "intake":
return []StepSpec{
{
ID: "step_01_organize",
Role: "organizer",
Title: "抽取材料事实并回填字段",
SubWorkspace: "tasks/01_organize",
Mode: "execute",
Accept: []string{
"抽取材料中的客户身份与产品定义",
"回填 fields.json 结构化数据",
"向 blackboard 写入 facts 与 gaps",
},
},
{
ID: "step_02_draft",
Role: "writer",
Title: "撰写用户初始需求收集表初稿",
SubWorkspace: "tasks/02_draft",
Mode: "execute",
Needs: []string{"tasks/01_organize/output.md"},
Accept: []string{
"按 template.md 各章节严格四段式展开",
"正文翔实,禁止写'不做展开'",
"文末集中附录字段快照与待确认清单",
},
},
{
ID: "step_03_review",
Role: "leader",
Title: "审核并定稿交付",
SubWorkspace: "tasks/03_review",
Mode: "execute",
Needs: []string{"tasks/02_draft/output.md"},
Accept: []string{
"核验全文逻辑一致性与出处标注",
"汇编待确认清单并给出下一步建议",
"交付终稿 output.md",
},
},
}
case "research":
return []StepSpec{
{
ID: "step_01_search",
Role: "searcher",
Title: "公开资料与标准检索",
SubWorkspace: "tasks/01_search",
Mode: "execute",
Parallel: true,
Accept: []string{
"检索纪要附带明确出处或未检索到说明",
"对本项目含义逐条阐述",
},
},
{
ID: "step_02_market",
Role: "market",
Title: "市场格局与竞品同维度对比",
SubWorkspace: "tasks/02_market",
Mode: "execute",
Parallel: true,
Accept: []string{
"多维度竞品对比表",
"机会与风险对称分析",
},
},
{
ID: "step_03_regulatory",
Role: "regulatory",
Title: "医疗器械法规与指导原则初筛",
SubWorkspace: "tasks/03_regulatory",
Mode: "execute",
Parallel: true,
Accept: []string{
"分类依据条目与建议类别",
"明确标注需向审评窗口确认",
},
},
{
ID: "step_04_plan",
Role: "writer",
Title: "撰写调研报告段落计划",
SubWorkspace: "tasks/04_plan",
Mode: "plan",
Needs: []string{
"tasks/01_search/output.md",
"tasks/02_market/output.md",
"tasks/03_regulatory/output.md",
},
Accept: []string{"各章节论点与引用规划"},
},
{
ID: "step_05_review_plan",
Role: "leader",
Title: "审核调研报告撰写计划",
SubWorkspace: "tasks/05_review_plan",
Mode: "review_plan",
Needs: []string{"tasks/04_plan/output_plan.md"},
Accept: []string{"出具审核意见 review_notes.md"},
},
{
ID: "step_06_draft",
Role: "writer",
Title: "按计划合成调研报告正文",
SubWorkspace: "tasks/06_draft",
Mode: "execute",
Needs: []string{
"tasks/04_plan/output_plan.md",
"tasks/05_review_plan/review_notes.md",
},
Accept: []string{"四段式完整正文,无省略套话"},
},
{
ID: "step_07_review",
Role: "leader",
Title: "终审并定稿",
SubWorkspace: "tasks/07_review",
Mode: "execute",
Needs: []string{"tasks/06_draft/output.md"},
Accept: []string{"质量把关并通过交付"},
},
}
default:
return []StepSpec{
{
ID: "step_01_draft",
Role: "writer",
Title: "撰写初稿",
SubWorkspace: "tasks/01_draft",
Mode: "execute",
Accept: []string{"按模板四段式展开"},
},
{
ID: "step_02_review",
Role: "leader",
Title: "审核定稿",
SubWorkspace: "tasks/02_review",
Mode: "execute",
Needs: []string{"tasks/01_draft/output.md"},
Accept: []string{"质量门禁通过"},
},
}
}
}
func writeJSON(path string, val any) error {
b, err := json.MarshalIndent(val, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, b, 0o644)
}