You've already forked agentic-coding-workflow
160 lines
5.7 KiB
Go
160 lines
5.7 KiB
Go
// Package orchestrator 实现"编排器/规划器":一个 per-requirement 的 run 聚合根,
|
|
// 驱动 6 阶段状态机(planning → prototyping → auditing → implementing → reviewing
|
|
// → done)。每个 step 选取一个 agent-kind + prompt,以特权但真实的 owner 身份经
|
|
// acp.SessionService 创建一个 ACP session,发一条 prompt 并阻塞等待回合完成
|
|
// (session/prompt stopReason),评估阶段网关后推进或回环。run 状态落库到
|
|
// orchestrator_runs / orchestrator_steps,每个 step 由 jobs (orchestrator.step) 驱动,
|
|
// 因而能跨重启恢复并复用既有 backoff/dead-letter 机制。
|
|
//
|
|
// agent-to-agent handoff 发生在编排器层:特权身份创建下一个 session,不放松
|
|
// checkNotSystemToken。request_subtask MCP 工具让运行中的 agent 在 depth/fan-out
|
|
// 限制内入队一个子 step。崩溃的 session 在同一 worktree 上重试/恢复。
|
|
package orchestrator
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/yan1h/agent-coding-workflow/internal/project"
|
|
)
|
|
|
|
// RunStatus 是 orchestrator_runs.status 枚举。
|
|
type RunStatus string
|
|
|
|
const (
|
|
RunPending RunStatus = "pending"
|
|
RunRunning RunStatus = "running"
|
|
RunPaused RunStatus = "paused"
|
|
RunSucceeded RunStatus = "succeeded"
|
|
RunFailed RunStatus = "failed"
|
|
RunCanceled RunStatus = "canceled"
|
|
)
|
|
|
|
// IsTerminal 报告 run 是否已到终态(不再驱动)。
|
|
func (s RunStatus) IsTerminal() bool {
|
|
return s == RunSucceeded || s == RunFailed || s == RunCanceled
|
|
}
|
|
|
|
// IsActive 报告 run 是否处于可驱动状态(pending/running)。paused 不驱动但非终态。
|
|
func (s RunStatus) IsActive() bool { return s == RunPending || s == RunRunning }
|
|
|
|
// StepStatus 是 orchestrator_steps.status 枚举。
|
|
type StepStatus string
|
|
|
|
const (
|
|
StepPending StepStatus = "pending"
|
|
StepSpawning StepStatus = "spawning"
|
|
StepRunning StepStatus = "running"
|
|
StepAwaitingGate StepStatus = "awaiting_gate"
|
|
StepSucceeded StepStatus = "succeeded"
|
|
StepFailed StepStatus = "failed"
|
|
StepDead StepStatus = "dead"
|
|
)
|
|
|
|
// Run 是一次编排运行聚合根。
|
|
type Run struct {
|
|
ID uuid.UUID
|
|
ProjectID uuid.UUID
|
|
RequirementID uuid.UUID
|
|
WorkspaceID uuid.UUID
|
|
OwnerID uuid.UUID
|
|
Status RunStatus
|
|
CurrentPhase project.Phase
|
|
Config RunConfig
|
|
LastError *string
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
EndedAt *time.Time
|
|
}
|
|
|
|
// Step 是 run 内一次阶段尝试。parent_step_id 非 nil 表示由 request_subtask fan-out。
|
|
type Step struct {
|
|
ID uuid.UUID
|
|
RunID uuid.UUID
|
|
Phase project.Phase
|
|
Attempt int
|
|
ParentStepID *uuid.UUID
|
|
Depth int
|
|
Status StepStatus
|
|
AgentKindID uuid.UUID
|
|
ACPSessionID *uuid.UUID
|
|
Prompt string
|
|
StopReason *string
|
|
GateResult *GateResult
|
|
LastError *string
|
|
JobID *uuid.UUID
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
EndedAt *time.Time
|
|
}
|
|
|
|
// RunConfig 是 run 的 per-phase 配置(落 JSONB)。零值合法:PhaseToAgentKind 退回
|
|
// defaults,MaxAttemptsPerPhase/MaxDepth/FanOutLimit<=0 时由 service 用配置默认填充。
|
|
type RunConfig struct {
|
|
PhaseAgentKinds map[project.Phase]uuid.UUID `json:"phase_agent_kinds,omitempty"`
|
|
PromptOverrides map[project.Phase]string `json:"prompt_overrides,omitempty"`
|
|
MaxAttemptsPerPhase int `json:"max_attempts_per_phase,omitempty"`
|
|
MaxDepth int `json:"max_depth,omitempty"`
|
|
FanOutLimit int `json:"fan_out_limit,omitempty"`
|
|
}
|
|
|
|
// GateResult 是一次阶段网关评估结果。Pass=true 推进;Retry=true 且未通过 → 可重试
|
|
// (回环本阶段);Retry=false 且未通过 → 永久失败(dead-letter)。
|
|
type GateResult struct {
|
|
Pass bool `json:"pass"`
|
|
Retry bool `json:"retry"`
|
|
Reason string `json:"reason,omitempty"`
|
|
Detail map[string]any `json:"detail,omitempty"`
|
|
}
|
|
|
|
// PhaseGate 是可插拔的阶段网关接口。v1 实现见 gate.go。
|
|
type PhaseGate interface {
|
|
Evaluate(ctx context.Context, run *Run, step *Step, stopReason string) (GateResult, error)
|
|
}
|
|
|
|
// Caller 表示发起编排操作的用户上下文(与其他模块的 Caller 同语义)。
|
|
type Caller struct {
|
|
UserID uuid.UUID
|
|
IsAdmin bool
|
|
}
|
|
|
|
// StartRunInput 是启动一次编排运行的入参。
|
|
type StartRunInput struct {
|
|
ProjectSlug string
|
|
RequirementNumber int
|
|
StartPhase project.Phase
|
|
Config RunConfig
|
|
}
|
|
|
|
// SubtaskInput 是 request_subtask 的入参(由 MCP bridge 透传)。
|
|
type SubtaskInput struct {
|
|
Phase project.Phase
|
|
Prompt string
|
|
AgentKindID *uuid.UUID
|
|
}
|
|
|
|
// RunFilter 控制 ListRuns 的过滤维度。零值不过滤。
|
|
type RunFilter struct {
|
|
ProjectID *uuid.UUID
|
|
RequirementID *uuid.UUID
|
|
Status RunStatus
|
|
Limit int
|
|
}
|
|
|
|
// Service 暴露编排器聚合根的应用服务方法。
|
|
type Service interface {
|
|
StartRun(ctx context.Context, c Caller, in StartRunInput) (*Run, error)
|
|
GetRun(ctx context.Context, c Caller, id uuid.UUID) (*Run, error)
|
|
ListRuns(ctx context.Context, c Caller, f RunFilter) ([]*Run, error)
|
|
Pause(ctx context.Context, c Caller, id uuid.UUID) error
|
|
Resume(ctx context.Context, c Caller, id uuid.UUID) error
|
|
Cancel(ctx context.Context, c Caller, id uuid.UUID) error
|
|
// RequestSubtask 由 MCP bridge 调用:运行中的 agent 请求编排器为其当前 step 派生子 step。
|
|
RequestSubtask(ctx context.Context, parentACPSessionID uuid.UUID, in SubtaskInput) (*Step, error)
|
|
// EnqueueStepRedrive 由 acp supervisor onExit 注入的回调间接调用:把崩溃 session 的
|
|
// step 重新入队(带 backoff)。
|
|
EnqueueStepRedrive(ctx context.Context, stepID uuid.UUID, reason string)
|
|
}
|