U4 第一步:Squad 依赖注入解耦(Options + 后端工厂),移除 config.Config 依赖 #4

Merged
weichen merged 2 commits from feature/squad-options-injection-2026-09-29 into dev 2026-09-29 05:19:27 +00:00
2 changed files with 50 additions and 13 deletions
Showing only changes of commit 72c0996b74 - Show all commits
+9 -1
View File
@@ -10,6 +10,7 @@ import (
"strings" "strings"
"time" "time"
"chat-agent-system/internal/agent"
"chat-agent-system/internal/app/uow" "chat-agent-system/internal/app/uow"
"chat-agent-system/internal/authz" "chat-agent-system/internal/authz"
"chat-agent-system/internal/config" "chat-agent-system/internal/config"
@@ -192,7 +193,14 @@ func NewService(cfg *config.Config) (*Service, error) {
svc := &Service{ svc := &Service{
cfg: cfg, cfg: cfg,
kb: kb.NewClient(cfg), kb: kb.NewClient(cfg),
squad: squad.NewService(cfg), squad: squad.NewService(squad.Options{
BackendName: cfg.AgentBackend,
BackendTimeout: cfg.AgentTimeout,
DataDir: cfg.DataDir,
NewBackend: func(name string) agent.Backend {
return agent.NewBackend(name, cfg)
},
}),
ctxMgr: ctxMgr, ctxMgr: ctxMgr,
cas: casStore, cas: casStore,
materialIndex: matIndex, materialIndex: matIndex,
+41 -12
View File
@@ -10,7 +10,6 @@ import (
"time" "time"
"chat-agent-system/internal/agent" "chat-agent-system/internal/agent"
"chat-agent-system/internal/config"
"chat-agent-system/internal/quote" "chat-agent-system/internal/quote"
"chat-agent-system/internal/stream" "chat-agent-system/internal/stream"
"chat-agent-system/internal/workflowdef" "chat-agent-system/internal/workflowdef"
@@ -109,18 +108,42 @@ type SquadService interface {
ResumeOrModifyWorkspace(ctx context.Context, req ModifyWorkflowRequest) (*SquadRunResult, error) ResumeOrModifyWorkspace(ctx context.Context, req ModifyWorkflowRequest) (*SquadRunResult, error)
} }
// Options 是 Squad 依赖注入的唯一配置面;不反向依赖主仓 config.Config,
// 为独立模块做准备(见多仓方案 KTD1)。
type Options struct {
// BackendName 为执行后端名(空 = 由调用方默认)。
BackendName string
// BackendTimeout 为单次执行超时;零值由调用方默认。
BackendTimeout time.Duration
// DataDir 为工作区根(任务目录挂在其下)。
DataDir string
// NewBackend 由调用方注入的后端工厂(Core 装配:凭证、环境、并发上限
// 留在产品侧)。nil 时返回错误,不静默降级 Stub。
NewBackend func(name string) agent.Backend
}
func (o Options) withDefaults() Options {
if o.BackendName == "" {
o.BackendName = "pi-agent"
}
if o.BackendTimeout <= 0 {
o.BackendTimeout = 10 * time.Minute
}
return o
}
// Service SquadService 实现 // Service SquadService 实现
type Service struct { type Service struct {
cfg *config.Config opts Options
engine *WorkspaceEngine engine *WorkspaceEngine
driver *WorkflowDriver driver *WorkflowDriver
gate *QualityGate gate *QualityGate
} }
func NewService(cfg *config.Config) *Service { func NewService(opts Options) *Service {
engine := NewWorkspaceEngine() engine := NewWorkspaceEngine()
return &Service{ return &Service{
cfg: cfg, opts: opts.withDefaults(),
engine: engine, engine: engine,
driver: NewWorkflowDriver(engine), driver: NewWorkflowDriver(engine),
gate: NewQualityGate(), gate: NewQualityGate(),
@@ -207,12 +230,12 @@ func (s *Service) ExecuteTask(ctx context.Context, req SquadRunRequest) (*SquadR
workDir := req.WorkDir workDir := req.WorkDir
if workDir == "" { if workDir == "" {
ts := time.Now().UnixNano() ts := time.Now().UnixNano()
workDir = filepath.Join(s.cfg.DataDir, "workspaces", "bizdev", fmt.Sprintf("%s_%d", req.TaskKey, ts)) workDir = filepath.Join(s.opts.DataDir, "workspaces", "bizdev", fmt.Sprintf("%s_%d", req.TaskKey, ts))
} }
backendName := req.Backend backendName := req.Backend
if backendName == "" { if backendName == "" {
backendName = s.cfg.AgentBackend backendName = s.opts.BackendName
} }
// 1. 初始化工作区目录体系 // 1. 初始化工作区目录体系
@@ -258,10 +281,13 @@ func (s *Service) ExecuteTask(ctx context.Context, req SquadRunRequest) (*SquadR
streamer.EmitPhase("squad_assembly", fmt.Sprintf("正在组建并启动【%s】智能体小队(后端: %s)...", TaskArtifactTitle(req.TaskKey), backendName), "leader", 0, 0, 5) streamer.EmitPhase("squad_assembly", fmt.Sprintf("正在组建并启动【%s】智能体小队(后端: %s)...", TaskArtifactTitle(req.TaskKey), backendName), "leader", 0, 0, 5)
} }
backend := agent.NewBackend(backendName, s.cfg) if s.opts.NewBackend == nil {
return nil, fmt.Errorf("squad: NewBackend factory not injected")
}
backend := s.opts.NewBackend(backendName)
timeout := req.Timeout timeout := req.Timeout
if timeout <= 0 { if timeout <= 0 {
timeout = s.cfg.AgentTimeout timeout = s.opts.BackendTimeout
} }
execOpts := ExecuteOptions{ execOpts := ExecuteOptions{
Timeout: timeout, Timeout: timeout,
@@ -397,9 +423,12 @@ func repairBudgetForSquadTask(req SquadRunRequest) int {
func (s *Service) ExecuteWorkflow(ctx context.Context, wsDir string, backendName string, opts ExecuteOptions) (*SquadRunResult, error) { func (s *Service) ExecuteWorkflow(ctx context.Context, wsDir string, backendName string, opts ExecuteOptions) (*SquadRunResult, error) {
start := time.Now() start := time.Now()
if backendName == "" { if backendName == "" {
backendName = s.cfg.AgentBackend backendName = s.opts.BackendName
} }
backend := agent.NewBackend(backendName, s.cfg) if s.opts.NewBackend == nil {
return nil, fmt.Errorf("squad: NewBackend factory not injected")
}
backend := s.opts.NewBackend(backendName)
execRes, err := s.driver.Execute(ctx, wsDir, backend, opts) execRes, err := s.driver.Execute(ctx, wsDir, backend, opts)
if err != nil { if err != nil {
@@ -491,12 +520,12 @@ func (s *Service) ResumeOrModifyWorkspace(ctx context.Context, req ModifyWorkflo
backendName := req.Backend backendName := req.Backend
if backendName == "" { if backendName == "" {
backendName = s.cfg.AgentBackend backendName = s.opts.BackendName
} }
timeout := req.Timeout timeout := req.Timeout
if timeout <= 0 { if timeout <= 0 {
timeout = s.cfg.AgentTimeout timeout = s.opts.BackendTimeout
} }
opts := ExecuteOptions{ opts := ExecuteOptions{