Files
weichenandCursor 93ee98af0e feat(squad): apply rdev stage step overrides and aliases
Stage-specific squad steps now win over the kind default, including always-on regulatory and searcher roles. Each of the 60 tasks carries its writing hint and Chinese alias so lookup and the writer accept line use them.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-19 10:05:26 +00:00

383 lines
12 KiB
Go

package squad
import (
"fmt"
"os"
"strings"
)
const rdevDHFAccept = "模板写满;结论先行;追溯表必填;缺项标「待确认」;不编造数据/签字/附件"
func rdevModel() string {
if v := strings.TrimSpace(os.Getenv("SQUAD_MODEL_ID")); v != "" {
return v
}
if v := strings.TrimSpace(os.Getenv("MINIMAX_MODEL_ID")); v != "" {
return v
}
return "MiniMax-M3"
}
func rdevComplexityTier(fields map[string]string) string {
if fields == nil {
return ""
}
switch strings.ToLower(strings.TrimSpace(fields["complexity_tier"])) {
case "high", "medium", "low":
return strings.ToLower(strings.TrimSpace(fields["complexity_tier"]))
default:
return ""
}
}
func rdevSquadAgents(taskKey, tier string) []AgentSpec {
if ov, ok := rdevResolvedOverride(taskKey, tier); ok {
return rdevAgentsFromOverride(ov)
}
meta, _ := LookupRDevTask(taskKey)
agents := []AgentSpec{
{
Role: "organizer",
Name: "材料整理专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"材料事实抽取", "适用性与缺口识别", "字段回填"},
ForbiddenActions: []string{"严禁编造试验数据", "严禁伪造签字"},
},
}
switch meta.Kind {
case RDevKindReviewMinutes:
agents = append(agents,
AgentSpec{
Role: "writer",
Name: "文档撰写专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"按评审记录模板成文", "问题-决议-行动项闭环"},
},
rdevLeaderAgent(),
)
case RDevKindUploadIndex:
agents = append(agents,
AgentSpec{
Role: "engineer",
Name: "技术专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"附件索引完整性", "缺失影响分析"},
},
AgentSpec{
Role: "writer",
Name: "文档撰写专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"按附件清单模板成文", "标明待上传项"},
},
rdevLeaderAgent(),
)
default:
agents = append(agents,
AgentSpec{
Role: "engineer",
Name: "技术专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"技术内容与追溯", "设计控制论证"},
},
)
if RDevNeedsRegulatoryAt(taskKey, tier) {
agents = append(agents, AgentSpec{
Role: "regulatory",
Name: "法规专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"法规/标准适用性", "软件与风险管理合规要点"},
})
}
agents = append(agents,
AgentSpec{
Role: "writer",
Name: "文档撰写专员",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
Responsibilities: []string{"按 RD 模板成文", "结论先行与待确认清单"},
},
rdevLeaderAgent(),
)
}
return agents
}
func rdevLeaderAgent() AgentSpec {
return AgentSpec{
Role: "leader",
Name: "研发设计控制负责人",
Model: rdevModel(),
Tools: []string{"read", "write", "edit"},
BashPermission: "deny",
InstructionsFile: "configs/agents/rdev_leader.md",
Responsibilities: []string{"DHF 完整性终审", "不适用理由审核", "禁止编造检查"},
ForbiddenActions: []string{"严禁代签", "严禁伪造附件内容"},
}
}
func rdevWorkflowSteps(taskKey, tier string) []StepSpec {
meta, ok := LookupRDevTask(taskKey)
kind := RDevKindDocument
title := taskKey
if ok {
kind = meta.Kind
title = meta.Title
}
if ov, found := rdevResolvedOverride(taskKey, tier); found {
return rdevAttachStageHint(taskKey, rdevStepsFromOverride(ov))
}
switch kind {
case RDevKindReviewMinutes:
return rdevAttachStageHint(taskKey, []StepSpec{
{
ID: "step_01_organize", Role: "organizer", Title: "整理评审输入与议题",
SubWorkspace: "tasks/01_organize", Mode: "execute",
Accept: []string{"汇总上游批准文档要点", "列出待评审议题与缺口", rdevDHFAccept},
},
{
ID: "step_02_draft", Role: "writer", Title: "撰写评审记录",
SubWorkspace: "tasks/02_draft", Mode: "execute",
Needs: []string{"tasks/01_organize/output.md"},
Accept: []string{"按模板写满评审记录", "问题-决议-行动项可追溯", rdevDHFAccept},
},
{
ID: "step_03_review", Role: "leader", Title: "研发设计控制终审",
SubWorkspace: "tasks/03_review", Mode: "execute",
Needs: []string{"tasks/02_draft/output.md"},
Accept: []string{"完整性与粒度达标", "无编造签字/数据", rdevDHFAccept},
},
})
case RDevKindUploadIndex:
return rdevAttachStageHint(taskKey, []StepSpec{
{
ID: "step_01_organize", Role: "organizer", Title: "整理附件材料线索",
SubWorkspace: "tasks/01_organize", Mode: "execute",
Accept: []string{"列出已知附件与缺失项", rdevDHFAccept},
},
{
ID: "step_02_engineer", Role: "engineer", Title: "完善附件索引与影响分析",
SubWorkspace: "tasks/02_engineer", Mode: "execute",
Needs: []string{"tasks/01_organize/output.md"},
Accept: []string{"索引表完整", "缺失影响已说明", rdevDHFAccept},
},
{
ID: "step_03_draft", Role: "writer", Title: "撰写附件清单",
SubWorkspace: "tasks/03_draft", Mode: "execute",
Needs: []string{"tasks/02_engineer/output.md"},
Accept: []string{"按 upload_index 模板成文", "禁止伪造附件正文", rdevDHFAccept},
},
{
ID: "step_04_review", Role: "leader", Title: "研发设计控制终审",
SubWorkspace: "tasks/04_review", Mode: "execute",
Needs: []string{"tasks/03_draft/output.md"},
Accept: []string{"索引与待上传结论一致", rdevDHFAccept},
},
})
default:
return rdevAttachStageHint(taskKey, rdevDocumentSteps(taskKey, title, tier))
}
}
func rdevDocumentSteps(taskKey, title, tier string) []StepSpec {
steps := []StepSpec{
{
ID: "step_01_organize", Role: "organizer", Title: "抽取材料事实并回填",
SubWorkspace: "tasks/01_organize", Mode: "execute",
Accept: []string{"facts/gaps/implications 入黑板", "适用性字段回填", rdevDHFAccept},
},
{
ID: "step_02_engineer", Role: "engineer", Title: "技术内容与追溯编制",
SubWorkspace: "tasks/02_engineer", Mode: "execute",
Needs: []string{"tasks/01_organize/output.md"},
Accept: []string{"技术论证完整", "追溯表可填则填", rdevDHFAccept},
},
}
draftNeeds := "tasks/02_engineer/output.md"
if RDevNeedsRegulatoryAt(taskKey, tier) {
steps = append(steps, StepSpec{
ID: "step_03_regulatory", Role: "regulatory", Title: "法规与风险合规要点",
SubWorkspace: "tasks/03_regulatory", Mode: "execute",
Needs: []string{"tasks/02_engineer/output.md"},
Accept: []string{"法规适用性明确", "风险类引用可追溯", rdevDHFAccept},
})
draftNeeds = "tasks/03_regulatory/output.md"
steps = append(steps,
StepSpec{
ID: "step_04_draft", Role: "writer", Title: "撰写《" + title + "》",
SubWorkspace: "tasks/04_draft", Mode: "execute",
Needs: []string{draftNeeds},
Accept: []string{"严格按 rd_ 模板成文", "结论先行", rdevDHFAccept},
},
StepSpec{
ID: "step_05_review", Role: "leader", Title: "研发设计控制终审",
SubWorkspace: "tasks/05_review", Mode: "execute",
Needs: []string{"tasks/04_draft/output.md"},
Accept: []string{"DHF 粒度达标", "无编造", rdevDHFAccept},
},
)
return steps
}
steps = append(steps,
StepSpec{
ID: "step_03_draft", Role: "writer", Title: "撰写《" + title + "》",
SubWorkspace: "tasks/03_draft", Mode: "execute",
Needs: []string{draftNeeds},
Accept: []string{"严格按 rd_ 模板成文", "结论先行", rdevDHFAccept},
},
StepSpec{
ID: "step_04_review", Role: "leader", Title: "研发设计控制终审",
SubWorkspace: "tasks/04_review", Mode: "execute",
Needs: []string{"tasks/03_draft/output.md"},
Accept: []string{"DHF 粒度达标", "无编造", rdevDHFAccept},
},
)
return steps
}
func rdevIncludeConditionalRegulatory(taskKey, tier string) bool {
switch strings.ToLower(strings.TrimSpace(tier)) {
case "medium", "low":
return false
case "high":
return true
default:
return RDevNeedsRegulatory(taskKey)
}
}
func rdevResolvedOverride(taskKey, tier string) ([]rdevOverrideStep, bool) {
raw, ok := rdevStageOverrides[taskKey]
if !ok {
return nil, false
}
out := make([]rdevOverrideStep, 0, len(raw))
for _, step := range raw {
if step.WhenHigh && !rdevIncludeConditionalRegulatory(taskKey, tier) {
continue
}
out = append(out, step)
}
return out, true
}
func rdevAgentsFromOverride(steps []rdevOverrideStep) []AgentSpec {
seen := map[string]bool{}
var agents []AgentSpec
for _, step := range steps {
if seen[step.Role] {
continue
}
seen[step.Role] = true
agents = append(agents, rdevAgentForRole(step.Role))
}
return agents
}
func rdevAgentForRole(role string) AgentSpec {
switch role {
case "writer":
return AgentSpec{
Role: "writer", Name: "文档撰写专员", Model: rdevModel(),
Tools: []string{"read", "write", "edit"}, BashPermission: "deny",
Responsibilities: []string{"按 RD 模板成文", "结论先行与待确认清单"},
}
case "engineer":
return AgentSpec{
Role: "engineer", Name: "技术专员", Model: rdevModel(),
Tools: []string{"read", "write", "edit"}, BashPermission: "deny",
Responsibilities: []string{"技术内容与追溯", "设计控制论证"},
}
case "regulatory":
return AgentSpec{
Role: "regulatory", Name: "法规专员", Model: rdevModel(),
Tools: []string{"read", "write", "edit"}, BashPermission: "deny",
Responsibilities: []string{"法规/标准适用性", "软件与风险管理合规要点"},
}
case "searcher":
return AgentSpec{
Role: "searcher", Name: "资料检索专员", Model: rdevModel(),
Tools: []string{"read", "write", "edit"}, BashPermission: "deny",
Responsibilities: []string{"公开资料检索", "出处与日期标注"},
ForbiddenActions: []string{"严禁编造URL与标准号"},
}
case "leader":
return rdevLeaderAgent()
default:
return AgentSpec{
Role: "organizer", Name: "材料整理专员", Model: rdevModel(),
Tools: []string{"read", "write", "edit"}, BashPermission: "deny",
Responsibilities: []string{"材料事实抽取", "适用性与缺口识别", "字段回填"},
ForbiddenActions: []string{"严禁编造试验数据", "严禁伪造签字"},
}
}
}
func rdevStepsFromOverride(steps []rdevOverrideStep) []StepSpec {
out := make([]StepSpec, 0, len(steps))
var parallelOutputs []string
prev := ""
for i, step := range steps {
id := fmt.Sprintf("step_%02d_%s", i+1, step.Role)
sub := fmt.Sprintf("tasks/%02d_%s", i+1, step.Role)
spec := StepSpec{
ID: id, Role: step.Role, Title: step.Title,
SubWorkspace: sub, Mode: "execute", Parallel: step.Parallel,
Accept: []string{rdevDHFAccept},
}
if step.Delegate {
spec.Accept = append([]string{"可委托检索,未命中须写检索局限"}, spec.Accept...)
}
if i > 0 {
if step.Parallel {
if prev != "" {
spec.Needs = []string{prev}
}
} else if len(parallelOutputs) > 0 {
spec.Needs = append([]string(nil), parallelOutputs...)
parallelOutputs = nil
} else if prev != "" {
spec.Needs = []string{prev}
}
}
out = append(out, spec)
file := sub + "/output.md"
if step.Parallel {
parallelOutputs = append(parallelOutputs, file)
} else {
prev = file
parallelOutputs = nil
}
}
return out
}
func rdevAttachStageHint(taskKey string, steps []StepSpec) []StepSpec {
meta, ok := LookupRDevTask(taskKey)
if !ok {
return steps
}
for i := range steps {
if steps[i].Role == "writer" && meta.Hint != "" {
steps[i].Accept = append(steps[i].Accept, meta.Hint)
}
if steps[i].Role == "organizer" && meta.SkipHint != "" {
steps[i].Accept = append(steps[i].Accept, meta.SkipHint)
}
}
return steps
}