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
3 changed files with 51 additions and 13 deletions
@@ -26,6 +26,7 @@ imports:
authz:
- docduty
bizdev:
- agent
- authz
- app/uow
- config
+9 -1
View File
@@ -10,6 +10,7 @@ import (
"strings"
"time"
"chat-agent-system/internal/agent"
"chat-agent-system/internal/app/uow"
"chat-agent-system/internal/authz"
"chat-agent-system/internal/config"
@@ -192,7 +193,14 @@ func NewService(cfg *config.Config) (*Service, error) {
svc := &Service{
cfg: 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,
cas: casStore,
materialIndex: matIndex,
+41 -12
View File
@@ -10,7 +10,6 @@ import (
"time"
"chat-agent-system/internal/agent"
"chat-agent-system/internal/config"
"chat-agent-system/internal/quote"
"chat-agent-system/internal/stream"
"chat-agent-system/internal/workflowdef"
@@ -109,18 +108,42 @@ type SquadService interface {
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 实现
type Service struct {
cfg *config.Config
opts Options
engine *WorkspaceEngine
driver *WorkflowDriver
gate *QualityGate
}
func NewService(cfg *config.Config) *Service {
func NewService(opts Options) *Service {
engine := NewWorkspaceEngine()
return &Service{
cfg: cfg,
opts: opts.withDefaults(),
engine: engine,
driver: NewWorkflowDriver(engine),
gate: NewQualityGate(),
@@ -207,12 +230,12 @@ func (s *Service) ExecuteTask(ctx context.Context, req SquadRunRequest) (*SquadR
workDir := req.WorkDir
if workDir == "" {
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
if backendName == "" {
backendName = s.cfg.AgentBackend
backendName = s.opts.BackendName
}
// 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)
}
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
if timeout <= 0 {
timeout = s.cfg.AgentTimeout
timeout = s.opts.BackendTimeout
}
execOpts := ExecuteOptions{
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) {
start := time.Now()
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)
if err != nil {
@@ -491,12 +520,12 @@ func (s *Service) ResumeOrModifyWorkspace(ctx context.Context, req ModifyWorkflo
backendName := req.Backend
if backendName == "" {
backendName = s.cfg.AgentBackend
backendName = s.opts.BackendName
}
timeout := req.Timeout
if timeout <= 0 {
timeout = s.cfg.AgentTimeout
timeout = s.opts.BackendTimeout
}
opts := ExecuteOptions{