feat(model): 工作模型与 JARVIS 语音模型分开配置 + 语音任务路由到快模型

模型配置加"用途"维度:工作主力(chat,要强)与 JARVIS 语音(voice,要快/低时延)各配各激活,
语音任务走语音模型池,未配置则透明回落工作模型——不影响现有功能。

- contract: ConfigKindVoice="voice" + Meta[model_profile]=voice(与 intent==report 同类路由)
- gateway: ServeConfig/broadcastActive 循环纳入 voice;submitVoiceTask 打 model_profile=voice 标记
- dispatcher: 第二个 llm.Pool(voicePool)吃 voice 配置热更新;board.useVoice 从 Meta 派生(含快照);
  Orchestrator.agentPool(b) 按黑板选池——语音且语音池就绪→语音池,否则工作池;
  agent 生成路径(graph/react/coordinator/compose)全改走 agentPool(b),报告/护栏/记忆固定工作池
- admin: 模型页三 Tab(工作主力/JARVIS语音/向量化),复用 ModelManager;api Kind 加 voice

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-22 11:39:17 +08:00
parent b6927247da
commit aec7ad949c
15 changed files with 102 additions and 50 deletions
@@ -154,7 +154,7 @@ func (o *Orchestrator) buildSpecialists(ctx context.Context, specs []specialistS
usedFuncNames := map[string]bool{}
for i, spec := range specs {
sys := firstNonEmpty(spec.System, defaultAgentSystem) + specialistCondensedSuffix
run := o.specialistRunner(ctx, spec, sys, byName)
run := o.specialistRunner(ctx, b, spec, sys, byName)
if run == nil {
tr.info("coordinator", "system", "专家跳过", "无可用模型:"+spec.Name)
continue
@@ -175,7 +175,7 @@ func (o *Orchestrator) buildSpecialists(ctx context.Context, specs []specialistS
// specialistRunner 构造专家执行闭包:带工具且模型支持函数调用→react.Agent.Generate;否则→ChatModel.Generate。
// 无可用模型返回 nil(该专家被跳过)。
func (o *Orchestrator) specialistRunner(ctx context.Context, spec specialistSpec, sys string, byName map[string]tool.BaseTool) func(context.Context, string) (string, error) {
func (o *Orchestrator) specialistRunner(ctx context.Context, b *board, spec specialistSpec, sys string, byName map[string]tool.BaseTool) func(context.Context, string) (string, error) {
var tools []tool.BaseTool
for _, tn := range spec.Tools {
if t, ok := byName[tn]; ok {
@@ -183,7 +183,7 @@ func (o *Orchestrator) specialistRunner(ctx context.Context, spec specialistSpec
}
}
if len(tools) > 0 {
if tcm := o.pool.ToolCallingModel(); tcm != nil {
if tcm := o.agentPool(b).ToolCallingModel(); tcm != nil {
ag, err := react.NewAgent(ctx, &react.AgentConfig{
ToolCallingModel: tcm,
ToolsConfig: compose.ToolsNodeConfig{Tools: tools},
@@ -202,7 +202,7 @@ func (o *Orchestrator) specialistRunner(ctx context.Context, spec specialistSpec
}
// 工具型专家但模型不支持函数调用 → 退纯对话(下方)
}
cm := o.pool.ChatModel()
cm := o.agentPool(b).ChatModel()
if cm == nil {
return nil
}
@@ -219,7 +219,7 @@ func (o *Orchestrator) specialistRunner(ctx context.Context, spec specialistSpec
// Anthropic 配方(分解→定制简报→并行派发→综合)。无 ToolCallingModel / 0 可用专家 → 降级单 agent。
func (o *Orchestrator) runCoordinator(ctx context.Context, taskID string, b *board, system string, n dsl.Node, tr *execTracer, node string) {
specs := parseSpecialists(n.Config)
tcm := o.pool.ToolCallingModel()
tcm := o.agentPool(b).ToolCallingModel()
if tcm == nil || len(specs) == 0 {
tr.info(node, "system", "协调者降级", "模型不支持函数调用或未配置专家,退回普通对话")
o.runAgent(ctx, taskID, b, system, tr, node, labelOf(n, "多智能体协调"))