You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
// planner.go 是纯函数(无 I/O)的阶段规划逻辑:阶段→agent-kind 解析、阶段→prompt
|
||||
// 组装、网关结果→下一阶段。可独立单测(模仿 acp.ResolveBranch 的取舍)。
|
||||
package orchestrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/project"
|
||||
)
|
||||
|
||||
// phaseOrder 是 6 阶段的线性推进顺序,对应 project.AllPhases。
|
||||
var phaseOrder = project.AllPhases
|
||||
|
||||
// NextPhase 根据当前阶段与网关结果决定下一阶段。
|
||||
//
|
||||
// - 网关未通过(!g.Pass)→ 回环本阶段(next==current,terminal=false),让 step
|
||||
// 在同阶段重试。
|
||||
// - 网关通过且 current==done → 终态(terminal=true),run 成功。
|
||||
// - 网关通过且 current!=done → 线性推进到下一阶段。
|
||||
//
|
||||
// current 非法(不在枚举内)时视为终态,避免无限循环。
|
||||
func NextPhase(current project.Phase, g GateResult) (next project.Phase, terminal bool) {
|
||||
if !g.Pass {
|
||||
return current, false
|
||||
}
|
||||
if current == project.PhaseDone {
|
||||
return project.PhaseDone, true
|
||||
}
|
||||
for i, p := range phaseOrder {
|
||||
if p == current {
|
||||
if i+1 >= len(phaseOrder) {
|
||||
// current 已是最后一个阶段(done)——由上面的分支处理;兜底终止。
|
||||
return project.PhaseDone, true
|
||||
}
|
||||
// 推进到下一阶段。即便下一阶段是 done,也非终态:done 仍要跑一个 step,
|
||||
// 只有 done 阶段网关通过后(current==done 分支)run 才成功。
|
||||
return phaseOrder[i+1], false
|
||||
}
|
||||
}
|
||||
// 未知阶段:保守终止。
|
||||
return current, true
|
||||
}
|
||||
|
||||
// PhaseAgentKind 解析某阶段应使用的 agent-kind:优先 run.config 覆盖,否则退回
|
||||
// defaults(app 层从配置注入的"阶段→默认 agent-kind"映射)。两者皆缺则报错。
|
||||
func PhaseAgentKind(cfg RunConfig, phase project.Phase, defaults map[project.Phase]uuid.UUID) (uuid.UUID, error) {
|
||||
if cfg.PhaseAgentKinds != nil {
|
||||
if id, ok := cfg.PhaseAgentKinds[phase]; ok && id != uuid.Nil {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
if defaults != nil {
|
||||
if id, ok := defaults[phase]; ok && id != uuid.Nil {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
return uuid.Nil, errs.New(errs.CodeInvalidInput,
|
||||
fmt.Sprintf("no agent kind configured for phase %q", phase))
|
||||
}
|
||||
|
||||
// BuildPrompt 组装某阶段发给 agent 的 prompt。config 提供覆盖模板时优先用覆盖
|
||||
// (仍前置需求/产物上下文);否则用内置的 per-phase 默认指令。纯字符串拼接,无 I/O。
|
||||
func BuildPrompt(phase project.Phase, req *project.Requirement, artifacts []*project.Artifact, override string) string {
|
||||
var b strings.Builder
|
||||
|
||||
if req != nil {
|
||||
fmt.Fprintf(&b, "Requirement #%d: %s\n", req.Number, req.Title)
|
||||
if strings.TrimSpace(req.Description) != "" {
|
||||
fmt.Fprintf(&b, "\nDescription:\n%s\n", strings.TrimSpace(req.Description))
|
||||
}
|
||||
}
|
||||
|
||||
if len(artifacts) > 0 {
|
||||
b.WriteString("\nPrior artifacts:\n")
|
||||
for _, a := range artifacts {
|
||||
if a == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, "- [%s v%d] %s\n", a.Phase, a.Version, summarizeArtifact(a))
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString("\nCurrent phase: ")
|
||||
b.WriteString(string(phase))
|
||||
b.WriteString("\n\n")
|
||||
|
||||
if strings.TrimSpace(override) != "" {
|
||||
b.WriteString(strings.TrimSpace(override))
|
||||
b.WriteString("\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
b.WriteString(defaultPhaseInstruction(phase))
|
||||
b.WriteString("\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// summarizeArtifact 取产物内容首行(截断)作为简介,避免 prompt 过长。
|
||||
func summarizeArtifact(a *project.Artifact) string {
|
||||
content := strings.TrimSpace(a.Content)
|
||||
if content == "" {
|
||||
return "(empty)"
|
||||
}
|
||||
if idx := strings.IndexByte(content, '\n'); idx >= 0 {
|
||||
content = content[:idx]
|
||||
}
|
||||
if len(content) > 120 {
|
||||
content = content[:120] + "..."
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// defaultPhaseInstruction 是每个阶段的内置默认指令。
|
||||
func defaultPhaseInstruction(phase project.Phase) string {
|
||||
switch phase {
|
||||
case project.PhasePlanning:
|
||||
return "Produce a clear plan for this requirement and record it as a planning artifact."
|
||||
case project.PhasePrototyping:
|
||||
return "Build a prototype validating the approach and record a prototyping artifact."
|
||||
case project.PhaseAuditing:
|
||||
return "Audit the plan/prototype for risks and gaps; record an auditing artifact."
|
||||
case project.PhaseImplementing:
|
||||
return "Implement the requirement on this worktree; commit your changes."
|
||||
case project.PhaseReviewing:
|
||||
return "Review the implementation, fix issues, and record a reviewing artifact."
|
||||
case project.PhaseDone:
|
||||
return "Summarize the completed work; no further action is required."
|
||||
default:
|
||||
return "Proceed with the current phase."
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user