fix(llm): Agent 绑定优先于任务 LLM 配置链

任务内各角色解析 LLM 的顺序由「任务链 → Agent 绑定 → 全局」改为
「Agent 绑定 → 任务链 → 全局」:显式绑定了模型的角色始终跑在该模型上,
未绑定的角色才落到任务链,任务链为空时再落到全局配置。

- 拆分 fallbackProviderForAgent 为 agentBindingProvider / globalProvider,
  让三个层级可按新顺序自由组合
- current()、CompactionWindow()、taskRuntimeAvailable() 统一按新顺序解析;
  taskRuntimeAvailable 改为逐角色判断,有可用绑定的角色在任务链耗尽时仍可运行
- resolveTaskRoleLLM(UI 展示)与前端 mock 同步对调,避免显示与实际调用不一致

保留两处既有语义:绑定/全局路径仍返回 profileID=0,不推进任务链故障转移
(绑定自身的转移仍由 poolForBinding + llm_pool_bind_fallback 负责);未绑定
角色遇到任务链耗尽仍是硬停止,不静默降级到全局。

Claude-Session: https://claude.ai/code/session_01Q8nhYZfS4H8fsSCBJXxbSy
This commit is contained in:
Autumn-27 committed 2026-08-22 06:14:18 -04:00
1 parent 476a44577c
commit adc8e3521d
5 files changed
+175 -49

No files matched your search

+12 -12
View File
@@ -42,23 +42,12 @@ func (s *Server) resolutionFromProfile(p *db.LLMProfile, source string) taskLLMR
}
// resolveTaskRoleLLM mirrors taskLLMRuntime.current without creating a provider:
// explicit task chain -> role Agent binding -> active/global environment config.
// role Agent binding -> explicit task chain -> active/global environment config.
// Database failures are returned rather than represented as an unavailable model.
func (s *Server) resolveTaskRoleLLM(t *Task, agentKey string) (taskLLMResolution, error) {
if s == nil || s.m == nil || s.m.pg == nil {
return taskLLMResolution{}, fmt.Errorf("database unavailable")
}
state := t.llmStateSnapshot()
if len(state.ProfileIDs) > 0 {
if state.ActiveID == nil {
return taskLLMResolution{Source: "task_chain", Reason: "任务 LLM 配置链额度已耗尽"}, nil
}
p, err := s.m.pg.ProfileByID(*state.ActiveID)
if err != nil {
return taskLLMResolution{}, fmt.Errorf("load task LLM profile: %w", err)
}
return s.resolutionFromProfile(p, "task_chain"), nil
}
a, err := s.m.pg.GetAgentByKey(agentKey)
if err != nil {
return taskLLMResolution{}, fmt.Errorf("load agent %q: %w", agentKey, err)
@@ -75,6 +64,17 @@ func (s *Server) resolveTaskRoleLLM(t *Task, agentKey string) (taskLLMResolution
}
}
}
state := t.llmStateSnapshot()
if len(state.ProfileIDs) > 0 {
if state.ActiveID == nil {
return taskLLMResolution{Source: "task_chain", Reason: "任务 LLM 配置链额度已耗尽"}, nil
}
p, err := s.m.pg.ProfileByID(*state.ActiveID)
if err != nil {
return taskLLMResolution{}, fmt.Errorf("load task LLM profile: %w", err)
}
return s.resolutionFromProfile(p, "task_chain"), nil
}
p, err := s.m.pg.ActiveProfile()
if err != nil {
return taskLLMResolution{}, fmt.Errorf("load global LLM profile: %w", err)