refactor(engine): 移除从不执行的全局 planner/worker 空壳,就绪信号改用谓词

权威解析器(SetAuthoritativeAgentResolver)落地后,引擎每轮规划/worker 都走
per-task 的 agentsForTask 路由;applyLLM 经 buildPlannerWorker 构建、UseLLM 装入
的全局 planner/worker 从不被执行,仅充当 Ready() 的'已配置'探针。这层空壳是
功能容易被错误接线的温床(冷节点压缩此前就误接在它上面)。

改动:
- 删除 buildPlannerWorker、providerForAgent(仅前者调用)、Engine.UseLLM/snapshot/
  Mode、Engine.planner/worker/mu 字段。
- Ready() 改为读 SetReadiness 注入的谓词(基于 s.llmOn);snapshotFor 去掉全局
  兜底,权威解析器返回 nil 即表示该任务不可用(语义不变)。
- applyLLM 不再构建全局 pair;NewServer 注入就绪谓词。
- 相应更新 3 处依赖旧注入方式的测试(改用权威解析器注入)。

生产行为不变(全局 pair 本就从不执行);go build/vet 通过,server 测试失败集合
与基线一致(均为需 DB 的既有用例),无新增失败。
This commit is contained in:
Autumn-27 committed 2026-09-17 09:40:06 -04:00
1 parent cd5e779a24
commit 3b60191e86
5 files changed
+35 -94

No files matched your search

+16 -33
View File
@@ -96,9 +96,6 @@ type Engine struct {
bc *Broadcaster // live activity pub/sub (SSE)
mu sync.RWMutex
planner *agent.Planner
worker *agent.Worker
started sync.Map // taskID -> bool, so Run is idempotent per task
lastAct sync.Map // taskID -> int64 unix, last planner/worker activity (heartbeat)
llmCalls sync.Map // taskID -> *int64, actual planner/worker/main-agent LLM calls
@@ -146,11 +143,14 @@ type Engine struct {
inflight sync.Map // taskID -> *int64, 在跑的 planner.Plan + worker.Execute 计数(用于 drain)
coordStarted sync.Map // taskID -> bool, deadline 协调器是否已启动(Run/reload 去重)
// resolve, if set, returns a task's dedicated planner/worker. In legacy mode
// nil,nil falls back to the global pair; an authoritative resolver uses nil,nil
// to mean this task is deliberately unavailable (for example an exhausted chain).
// resolve returns a task's dedicated planner/worker (wired by the server as the
// authoritative task-router). nil,nil means this task is deliberately unavailable
// (for example an exhausted failover chain) — there is no global-pair fallback.
resolve func(t *Task) (*agent.Planner, *agent.Worker)
resolveAuthoritative bool
// readiness reports whether a global LLM provider is configured — the signal behind
// Ready()/the llm_configured indicator. Wired once at startup; nil → not ready.
readiness func() bool
}
type taskRuntime struct {
@@ -696,18 +696,9 @@ func (e *Engine) appendActivity(t *Task, r db.Activity) (int64, error) {
return 0, err
}
// UseLLM installs (or replaces) the LLM planner + work agent at runtime.
func (e *Engine) UseLLM(p *agent.Planner, w *agent.Worker) {
e.mu.Lock()
e.planner, e.worker = p, w
e.mu.Unlock()
}
func (e *Engine) snapshot() (*agent.Planner, *agent.Worker) {
e.mu.RLock()
defer e.mu.RUnlock()
return e.planner, e.worker
}
// SetReadiness wires the global "an LLM provider is configured" predicate (read by
// Ready() / the llm_configured indicator). Called once at startup.
func (e *Engine) SetReadiness(fn func() bool) { e.readiness = fn }
// SetAgentResolver installs a per-task planner/worker resolver (wired by the server).
// Called once at startup before any task loop runs, so no lock is needed on reads.
@@ -724,9 +715,9 @@ func (e *Engine) SetAuthoritativeAgentResolver(fn func(t *Task) (*agent.Planner,
e.resolveAuthoritative = true
}
// snapshotFor returns the planner/worker a task should run on: the task's dedicated
// pair when it pins a specific LLM profile (via the resolver), else the global active
// pair. The resolver returning nil means "no override" (fall back to global).
// snapshotFor returns the planner/worker a task should run on, from the task-router
// resolver. nil,nil means the task is deliberately unavailable (e.g. an exhausted
// failover chain); there is no global-pair fallback.
func (e *Engine) snapshotFor(t *Task) (*agent.Planner, *agent.Worker) {
if e.resolve != nil {
p, w := e.resolve(t)
@@ -734,13 +725,13 @@ func (e *Engine) snapshotFor(t *Task) (*agent.Planner, *agent.Worker) {
return p, w
}
}
return e.snapshot()
return nil, nil
}
// Ready reports whether an LLM provider is configured.
// Ready reports whether a global LLM provider is configured (via the readiness
// predicate wired at startup).
func (e *Engine) Ready() bool {
p, w := e.snapshot()
return p != nil && w != nil
return e.readiness != nil && e.readiness()
}
// ReadyFor reports whether a specific task can resolve a planner/worker pair.
@@ -751,14 +742,6 @@ func (e *Engine) ReadyFor(t *Task) bool {
return p != nil && w != nil
}
// Mode reports "llm" when ready, else "idle".
func (e *Engine) Mode() string {
if e.Ready() {
return "llm"
}
return "idle"
}
// Run starts the planner loop + N worker loops for a task. The loops always run
// but no-op until an LLM is configured (so a task created while idle picks up
// automatically once LLM is set from the UI).